xref: /haiku/src/apps/cortex/NodeManager/NodeRef.cpp (revision efafab643ce980e3f3c916795ed302599f6b4f66)
1 /*
2  * Copyright (c) 1999-2000, Eric Moon.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions, and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 // NodeRef.cpp
33 
34 #include "NodeRef.h"
35 
36 #include "Connection.h"
37 #include "NodeGroup.h"
38 //#include "NodeGroup_transport_thread.h"
39 #include "NodeManager.h"
40 #include "NodeSyncThread.h"
41 
42 #include "AddOnHost.h"
43 
44 #include <Entry.h>
45 #include <MediaRoster.h>
46 #include <TimeSource.h>
47 
48 #include <algorithm>
49 #include <functional>
50 #include <iterator>
51 #include <stdexcept>
52 
53 #include "functional_tools.h"
54 #include "node_manager_impl.h"
55 #include "SoundUtils.h"
56 
57 // -------------------------------------------------------- //
58 
59 using namespace std;
60 
61 __USE_CORTEX_NAMESPACE
62 
63 #define D_METHOD(x) //PRINT (x)
64 #define D_MESSAGE(x) //PRINT (x)
65 #define D_ROSTER(x) //PRINT (x)
66 #define D_LOCK(x) //PRINT (x)
67 
68 // -------------------------------------------------------- //
69 // addon_hint
70 //
71 // Contains information that can be used to reconstruct this
72 // node later on.
73 // -------------------------------------------------------- //
74 
75 // [e.moon 29sep99] added 'file'
76 
77 struct NodeRef::addon_hint {
78 
79 	addon_hint(
80 		const dormant_node_info*			_dormantInfo,
81 		const entry_ref*							_file=0) :
82 		dormantInfo(*_dormantInfo),
83 		file(_file ? new entry_ref(*_file) : 0) {}
84 
85 	~addon_hint() {
86 		if(file) delete file;
87 	}
88 
89 	void setFile(
90 		const entry_ref*						_file) {
91 		ASSERT(_file);
92 		if(file) delete file;
93 		file = new entry_ref(*_file);
94 	}
95 
96 	dormant_node_info							dormantInfo;
97 	entry_ref*										file;
98 };
99 
100 // -------------------------------------------------------- //
101 // *** ctor/dtor
102 // -------------------------------------------------------- //
103 
104 // free the node (this call will result in the eventual
105 // deletion of the object.)
106 
107 status_t NodeRef::release() {
108 
109 	// release the node, if possible:
110 	status_t err = releaseNode();
111 
112 	// hand off to parent release() implementation
113 	status_t parentErr = _inherited::release();
114 	return (parentErr < B_OK) ? parentErr : err;
115 }
116 
117 NodeRef::~NodeRef() {
118 	D_METHOD(("~NodeRef[%s]\n", name()));
119 	Autolock _l(m_manager);
120 
121 	// remove from NodeManager
122 	m_manager->_removeRef(id());
123 
124 	// [14oct99 e.moon] kill position-report thread if necessary
125 	if(m_positionThread)
126 		_stopPositionThread();
127 
128 	if(m_addonHint) {
129 		delete m_addonHint;
130 		m_addonHint = 0;
131 	}
132 
133 	// close Media Roster connection [e.moon 11oct99]
134 	BMediaRoster* r = BMediaRoster::Roster();
135 	if(m_watching) {
136 		r->StopWatching(
137 			BMessenger(this),
138 			node(),
139 			B_MEDIA_WILDCARD);
140 	}
141 }
142 
143 // -------------------------------------------------------- //
144 // *** const accessors
145 // -------------------------------------------------------- //
146 
147 // [e.moon 13oct99] moved to header
148 
149 //inline const media_node& NodeRef::node() const { return m_info.node; }
150 //inline uint32 NodeRef::kind() const { return m_info.node.kind; }
151 //inline const live_node_info& NodeRef::nodeInfo() const { return m_info; }
152 //inline media_node_id NodeRef::id() const { return m_info.node.node; }
153 //inline const char* NodeRef::name() const { return m_info.name; }
154 
155 // -------------------------------------------------------- //
156 // *** member access
157 // -------------------------------------------------------- //
158 
159 // turn cycle mode (looping) on or off
160 
161 void NodeRef::setCycling(
162 	bool												cycle) {
163 	Autolock _l(this);
164 
165 	D_METHOD((
166 		"NodeRef::setCycling(%s)\n",
167 		cycle ? "true" : "false"));
168 
169 	if(cycle == m_cycle)
170 		return;
171 
172 	m_cycle = cycle;
173 
174 	if(m_group) {
175 		m_group->_refCycleChanged(this);
176 
177 		// +++++ if running, get started...
178 	}
179 
180 	// notify
181 	BMessage m(M_CYCLING_CHANGED);
182 	m.AddBool("cycling", cycle);
183 	notify(&m);
184 }
185 
186 bool NodeRef::isCycling() const {
187 	return m_cycle;
188 }
189 
190 
191 // is the node running?
192 
193 bool NodeRef::isRunning() const {
194 	return m_running;
195 }
196 
197 // was the node created via NodeManager::instantiate()?
198 bool NodeRef::isInternal() const {
199 	return m_implFlags & _INTERNAL;
200 }
201 
202 
203 // fetch the group; may return 0 if the node has no connections
204 NodeGroup* NodeRef::group() const {
205 	Autolock _l(this);
206 	return m_group;
207 }
208 
209 // flag access
210 uint32 NodeRef::flags() const {
211 	Autolock _l(this);
212 	return m_flags;
213 }
214 
215 void NodeRef::setFlags(
216 	uint32											flags) {
217 	Autolock _l(this);
218 	m_flags = flags;
219 }
220 
221 //// has this reference been released?
222 //bool NodeRef::isReleased() const {
223 //	// no lock necessary for bool access -- right?  +++++
224 //	return m_released;
225 //}
226 //
227 
228 // [e.moon 29sep99]
229 // access addon-hint info
230 // - returns B_BAD_VALUE if not an add-on node created by this NodeManager
231 
232 status_t NodeRef::getDormantNodeInfo(
233 	dormant_node_info*						outInfo) {
234 
235 	if(!m_addonHint)
236 		return B_BAD_VALUE;
237 
238 	*outInfo = m_addonHint->dormantInfo;
239 	return B_OK;
240 }
241 
242 // [e.moon 29sep99]
243 // access file being played
244 // - returns B_BAD_VALUE if not an add-on node created by this NodeManager,
245 //   or if the node has no associated file
246 
247 status_t NodeRef::getFile(
248 	entry_ref*										outFile) {
249 
250 	Autolock _l(this);
251 
252 	if(!m_addonHint || !m_addonHint->file)
253 		return B_BAD_VALUE;
254 
255 	*outFile = *(m_addonHint->file);
256 	return B_OK;
257 }
258 
259 // [e.moon 8dec99]
260 // set file to play
261 
262 status_t NodeRef::setFile(
263 	const entry_ref&						file,
264 	bigtime_t*									outDuration) {
265 
266 	Autolock _l(this);
267 
268 	bigtime_t dur;
269 	status_t err = m_manager->roster->SetRefFor(
270 		node(),
271 		file,
272 		false,
273 		&dur);
274 	if(err == B_OK) {
275 		if(outDuration)
276 			*outDuration = dur;
277 		if(m_addonHint)
278 			m_addonHint->setFile(&file);
279 	}
280 
281 	return err;
282 }
283 
284 // [e.moon 23oct99]
285 // returns true if the media_node has been released (call releaseNode() to
286 // make this happen.)
287 
288 bool NodeRef::isNodeReleased() const {
289 	return m_nodeReleased;
290 }
291 
292 // -------------------------------------------------------- //
293 // *** run-mode operations
294 // -------------------------------------------------------- //
295 
296 void NodeRef::setRunMode(
297 	uint32											runMode,
298 	bigtime_t										delay) {
299 	Autolock _l(this);
300 
301 	ASSERT(runMode <= BMediaNode::B_RECORDING);
302 	m_runMode = runMode;
303 	if(runMode == BMediaNode::B_RECORDING)
304 		m_recordingDelay = delay;
305 
306 	// send notification to all observers
307 	BMessage m(M_RUN_MODE_CHANGED);
308 	m.AddInt32("nodeID", id());
309 	m.AddInt32("runMode", runMode);
310 
311 	if(runMode == BMediaNode::B_RECORDING && m_recordingDelay != 0)
312 		m.AddInt64("delay", m_recordingDelay);
313 
314 	notify(&m);
315 
316 	// update real run mode
317 	if(m_group)
318 		_setRunMode(m_group->runMode(), m_recordingDelay);
319 }
320 
321 uint32 NodeRef::runMode() const {
322 	Autolock _l(this);
323 	return m_runMode;
324 }
325 
326 bigtime_t NodeRef::recordingDelay() const {
327 	Autolock _l(this);
328 	return m_recordingDelay;
329 }
330 
331 // calculates the minimum amount of delay needed for
332 // B_RECORDING mode
333 // +++++ 15sep99: returns biggest_output_buffer_duration * 2
334 // +++++ 28sep99: adds downstream latency
335 
336 bigtime_t NodeRef::calculateRecordingModeDelay() {
337 	PRINT((
338 		"NodeRef::calculateRecordingModeDelay()\n"));
339 	status_t err;
340 
341 	bigtime_t maxBufferDur = 0LL;
342 
343 	vector<Connection> outputConnections;
344 	err = getOutputConnections(outputConnections);
345 	for(
346 		vector<Connection>::iterator it = outputConnections.begin();
347 		it != outputConnections.end(); ++it) {
348 
349 		bigtime_t bufferDur = buffer_duration((*it).format().u.raw_audio);
350 		if(bufferDur > maxBufferDur)
351 			maxBufferDur = bufferDur;
352 	}
353 
354 	bigtime_t latency = 0LL;
355 	err = m_manager->roster->GetLatencyFor(
356 		node(), &latency);
357 
358 	PRINT((
359 		"  %" B_PRIdBIGTIME "\n", latency));
360 
361 	return latency; // +++++ stab in the dark 28sep99
362 //	return maxBufferDur + latency;
363 }
364 
365 // -------------------------------------------------------- //
366 // *** connection access
367 // -------------------------------------------------------- //
368 
369 // connection access: vector versions
370 
371 status_t NodeRef::getInputConnections(
372 	vector<Connection>&					ioConnections,
373 	media_type									filterType) const {
374 	Autolock _l(this);
375 
376 	NodeManager::con_map::iterator it, itEnd;
377 	it = m_manager->m_conDestinationMap.lower_bound(m_info.node.node);
378 	itEnd = m_manager->m_conDestinationMap.upper_bound(m_info.node.node);
379 
380 	for(; it != itEnd; ++it) {
381 		if(filterType == B_MEDIA_UNKNOWN_TYPE ||
382 			(*it).second->format().type == filterType) {
383 
384 			ioConnections.push_back(*((*it).second));
385 		}
386 	}
387 
388 	return B_OK;
389 }
390 
391 
392 status_t NodeRef::getOutputConnections(
393 	vector<Connection>&					ioConnections,
394 	media_type									filterType) const {
395 	Autolock _l(this);
396 
397 	NodeManager::con_map::iterator it, itEnd;
398 	it = m_manager->m_conSourceMap.lower_bound(m_info.node.node);
399 	itEnd = m_manager->m_conSourceMap.upper_bound(m_info.node.node);
400 
401 	for(; it != itEnd; ++it) {
402 		if(filterType == B_MEDIA_UNKNOWN_TYPE ||
403 			(*it).second->format().type == filterType) {
404 
405 			ioConnections.push_back(*((*it).second));
406 		}
407 	}
408 
409 	return B_OK;
410 }
411 
412 // connection access: flat array versions
413 
414 status_t NodeRef::getInputConnections(
415 	Connection*									outConnections,
416 	int32												maxConnections,
417 	int32*											outNumConnections,
418 	media_type									filterType) const {
419 	Autolock _l(this);
420 
421 	NodeManager::con_map::iterator it, itEnd;
422 	it = m_manager->m_conDestinationMap.lower_bound(m_info.node.node);
423 	itEnd = m_manager->m_conDestinationMap.upper_bound(m_info.node.node);
424 
425 	int32 count = 0;
426 
427 	for(; it != itEnd && count < maxConnections; ++it) {
428 		if(filterType == B_MEDIA_UNKNOWN_TYPE ||
429 			(*it).second->format().type == filterType) {
430 
431 			outConnections[count++] = *((*it).second);
432 		}
433 	}
434 
435 	*outNumConnections = count;
436 
437 	return B_OK;
438 }
439 
440 status_t NodeRef::getOutputConnections(
441 	Connection*									outConnections,
442 	int32												maxConnections,
443 	int32*											outNumConnections,
444 	media_type									filterType) const {
445 	Autolock _l(this);
446 
447 	NodeManager::con_map::iterator it, itEnd;
448 	it = m_manager->m_conSourceMap.lower_bound(m_info.node.node);
449 	itEnd = m_manager->m_conSourceMap.upper_bound(m_info.node.node);
450 
451 	int32 count = 0;
452 
453 	for(; it != itEnd && count < maxConnections; ++it) {
454 		if(filterType == B_MEDIA_UNKNOWN_TYPE ||
455 			(*it).second->format().type == filterType) {
456 
457 			outConnections[count++] = *((*it).second);
458 		}
459 	}
460 
461 	*outNumConnections = count;
462 
463 	return B_OK;
464 }
465 
466 // -------------------------------------------------------- //
467 // *** position reporting/listening
468 // -------------------------------------------------------- //
469 
470 bool NodeRef::positionReportsEnabled() const {
471 	return m_positionReportsEnabled;
472 }
473 
474 // start thread if necessary
475 void NodeRef::enablePositionReports() {
476 	Autolock _l(this);
477 
478 	if(m_flags & NO_POSITION_REPORTING)
479 		return;
480 
481 	if(m_positionReportsEnabled)
482 		return;
483 
484 	m_positionReportsEnabled = true;
485 	if(m_running) {
486 		// start the thread
487 		_startPositionThread();
488 	}
489 }
490 
491 // stop thread if necessary
492 void NodeRef::disablePositionReports() {
493 	Autolock _l(this);
494 
495 	if(!m_positionReportsEnabled)
496 		return;
497 
498 	m_positionReportsEnabled = false;
499 	if(m_running) {
500 		// shut down the thread
501 		_stopPositionThread();
502 	}
503 }
504 
505 // Fetch the approximate current position:
506 //   Returns the last reported position, and the
507 //   performance time at which that position was reached.  If the
508 //   transport has never been started, the start position and
509 //   a performance time of 0 will be returned.  If position updating
510 //   isn't currently enabled, returns B_NOT_ALLOWED.
511 
512 status_t NodeRef::getLastPosition(
513 	bigtime_t*									outPosition,
514 	bigtime_t*									outPerfTime) const {
515 
516 	Autolock _l(this);
517 
518 	if(!m_positionReportsEnabled)
519 		return B_NOT_ALLOWED;
520 
521 	*outPosition = m_lastPosition;
522 	*outPerfTime = m_tpLastPositionUpdate;
523 	return B_OK;
524 }
525 
526 // Subscribe to regular position reports:
527 //   Position reporting isn't rolled into the regular IObservable
528 //   interface because a large number of messages are generated
529 //   (the frequency can be changed; see below.)
530 
531 status_t NodeRef::addPositionObserver(
532 	BHandler*										handler) {
533 	ASSERT(handler);
534 
535 	// try to create messenger
536 	status_t error;
537 	BMessenger m(handler, NULL, &error);
538 	if(error < B_OK) {
539 		PRINT((
540 			"* NodeRef::addPositionListener(): BMessenger() failed:\n"
541 			"  %s\n"
542 			"  handler %p\n",
543 			strerror(error), handler));
544 		return error;
545 	}
546 
547 	// add to the invoker
548 	Autolock _l(this);
549 	m_positionInvoker.AddTarget(handler);
550 
551 	// enable position updates:
552 	if(!m_positionReportsEnabled)
553 		enablePositionReports();
554 
555 	return B_OK;
556 }
557 
558 status_t NodeRef::removePositionObserver(
559 	BHandler*										handler) {
560 	ASSERT(handler);
561 
562 	Autolock _l(this);
563 
564 	// look for listener
565 	int32 index = m_positionInvoker.IndexOfTarget(handler);
566 	if(index == -1)
567 		return B_ERROR;
568 
569 	// remove it
570 	m_positionInvoker.RemoveTarget(index);
571 
572 	// last observer removed?  kill thread. [e.moon 12oct99]
573 	if(m_positionReportsEnabled && !m_positionInvoker.CountTargets())
574 		disablePositionReports();
575 
576 	return B_OK;
577 }
578 
579 // Set how often position updates will be sent:
580 //   Realistically, period should be > 10000 or so.
581 
582 status_t NodeRef::setPositionUpdatePeriod(
583 	bigtime_t										period) {
584 
585 	Autolock _l(this);
586 	if(period < 1000LL)
587 		return B_BAD_VALUE;
588 	m_positionUpdatePeriod = period;
589 	return B_OK;
590 }
591 
592 bigtime_t NodeRef::positionUpdatePeriod() const{
593 	Autolock _l(this);
594 	return m_positionUpdatePeriod;
595 }
596 
597 // -------------------------------------------------------- //
598 // *** BMediaRoster wrappers & convenience methods
599 // -------------------------------------------------------- //
600 
601 // release the media node
602 // (if allowed, will trigger the release/deletion of this object)
603 status_t NodeRef::releaseNode() {
604 
605 	D_METHOD((
606 		"NodeRef[%s]::releaseNode()\n", name()));
607 	status_t err;
608 
609 	Autolock _l(m_manager);
610 
611 	if(isReleased() || m_nodeReleased)
612 		return B_NOT_ALLOWED;
613 
614 	if(m_group)
615 		m_group->removeNode(this);
616 
617 	// kill off sync thread
618 	if(m_positionThread) {
619 		delete m_positionThread;
620 		m_positionThread = 0;
621 	}
622 
623 	if(m_implFlags & _INTERNAL) {
624 
625 		// tear down all connections if the node was created by
626 		// NodeManager
627 		vector<Connection> c_set;
628 		getInputConnections(c_set);
629 		getOutputConnections(c_set);
630 
631 // [e.moon 13oct99] making PPC compiler happy
632 //		for_each(
633 //			c_set.begin(),
634 //			c_set.end(),
635 //			bound_method(
636 //				*m_manager,
637 //				&NodeManager::disconnect
638 //			)
639 //		);
640 
641 		for(vector<Connection>::iterator it = c_set.begin();
642 			it != c_set.end(); ++it) {
643 			err = m_manager->disconnect(*it);
644 			if(err < B_OK) {
645 				PRINT((
646 					"! NodeRef('%s')::releaseNode():\n"
647 					"  NodeManager::disconnect('%s'->'%s') failed:\n"
648 					"  %s\n",
649 					name(),
650 					(*it).outputName(), (*it).inputName(),
651 					strerror(err)));
652 			}
653 		}
654 
655 		// +++++ ensure that the connections were really broken?
656 	}
657 
658 	err = B_OK;
659 	if(!(m_implFlags & _NO_RELEASE)) {
660 
661 //		PRINT((
662 //			"### releasing node %ld\n",
663 //			id()));
664 //
665 		// free the node
666 		D_ROSTER(("# roster->ReleaseNode(%ld)\n", m_info.node.node));
667 		err = BMediaRoster::Roster()->ReleaseNode(
668 			m_info.node);
669 
670 		if(err < B_OK) {
671 			PRINT((
672 				"!!! ReleaseNode(%" B_PRId32 ") failed:\n"
673 				"    %s\n",
674 				m_info.node.node,
675 				strerror(err)));
676 		}
677 
678 		if(
679 			(m_implFlags & _INTERNAL) &&
680 			m_manager->m_useAddOnHost) {
681 
682 			// ask add-on host to release the node
683 			err = AddOnHost::ReleaseInternalNode(m_info);
684 			if(err < B_OK) {
685 				PRINT((
686 					"!!! AddOnHost::ReleaseInternalNode(%" B_PRId32
687 						") failed:\n"
688 					"    %s\n",
689 					m_info.node.node,
690 					strerror(err)));
691 			}
692 		}
693 	}
694 	else {
695 //		PRINT(("- not releasing node\n"));
696 	}
697 
698 	m_nodeReleased = true;
699 	return err;
700 }
701 
702 
703 // calculate total (internal + downstream) latency for this node
704 
705 status_t NodeRef::totalLatency(
706 	bigtime_t*									outLatency) const {
707 
708 	return BMediaRoster::Roster()->GetLatencyFor(
709 		m_info.node,
710 		outLatency);
711 }
712 
713 // retrieve input/output matching the given destination/source.
714 // returns B_MEDIA_BAD_[SOURCE | DESTINATION] if the destination
715 // or source don't correspond to this node.
716 
717 class match_input_destination { public:
718 	const media_destination& dest;
719 	match_input_destination(const media_destination& _dest) : dest(_dest) {}
720 	bool operator()(const media_input& input) const {
721 		return input.destination == dest;
722 	}
723 };
724 
725 class match_output_source { public:
726 	const media_source& source;
727 	match_output_source(const media_source& _source) : source(_source) {}
728 	bool operator()(const media_output& output) const {
729 		return output.source == source;
730 	}
731 };
732 
733 status_t NodeRef::findInput(
734 	const media_destination&			forDestination,
735 	media_input*									outInput) const {
736 
737 	status_t err;
738 
739 	vector<media_input> inputs;
740 	vector<media_input>::const_iterator it;
741 	inputs.reserve(32);
742 
743 	// check free inputs
744 	err = getFreeInputs(inputs);
745 	if(err < B_OK)
746 		return err;
747 
748 	it = find_if(
749 		inputs.begin(), inputs.end(),
750 		match_input_destination(forDestination));
751 
752 	if(it != inputs.end()) {
753 		*outInput = *it;
754 		return B_OK;
755 	}
756 
757 	// check connected inputs
758 	inputs.clear();
759 	err = getConnectedInputs(inputs);
760 	if(err < B_OK)
761 		return err;
762 
763 	it = find_if(
764 		inputs.begin(), inputs.end(),
765 		match_input_destination(forDestination));
766 
767 	if(it != inputs.end()) {
768 		*outInput = *it;
769 		return B_OK;
770 	}
771 	return B_MEDIA_BAD_DESTINATION;
772 }
773 
774 status_t NodeRef::findOutput(
775 	const media_source&					forSource,
776 	media_output*								outOutput) const {
777 
778 	status_t err;
779 
780 	vector<media_output> outputs;
781 	vector<media_output>::const_iterator it;
782 	outputs.reserve(32);
783 
784 	// check free outputs
785 	err = getFreeOutputs(outputs);
786 	if(err < B_OK)
787 		return err;
788 
789 	it = find_if(
790 		outputs.begin(), outputs.end(),
791 		match_output_source(forSource));
792 
793 	if(it != outputs.end()) {
794 		*outOutput = *it;
795 		return B_OK;
796 	}
797 
798 	// check connected outputs
799 	outputs.clear();
800 	err = getConnectedOutputs(outputs);
801 	if(err < B_OK)
802 		return err;
803 
804 	it = find_if(
805 		outputs.begin(), outputs.end(),
806 		match_output_source(forSource));
807 
808 	if(it != outputs.end()) {
809 		*outOutput = *it;
810 		return B_OK;
811 	}
812 
813 	return B_MEDIA_BAD_SOURCE;
814 }
815 
816 
817 // endpoint matching (given name and/or format as 'hints')
818 
819 template <class T>
820 class match_endpoint_name_format : public unary_function<T, bool> {
821 public:
822 	const char* name;
823 	const media_format* format;
824 
825 	match_endpoint_name_format(const char* _name, const media_format* _format) :
826 		name(_name), format(_format) {}
827 	bool operator()(const T& endpoint) const {
828 		// test name, if given
829 		if(name && strcmp(endpoint.name, name) != 0)
830 			return false;
831 		// test format, if given
832 		media_format* f1 = const_cast<media_format*>(format);
833 		media_format* f2 = const_cast<media_format*>(&endpoint.format);
834 		if(format && !f1->Matches(f2))
835 			return false;
836 		return true;
837 	}
838 };
839 
840 template <class T>
841 class match_endpoint_name_type : public unary_function<T, bool> {
842 public:
843 	const char* name;
844 	media_type type;
845 
846 	match_endpoint_name_type(const char* _name, media_type _type) :
847 		name(_name), type(_type) {}
848 	bool operator()(const T& endpoint) const {
849 		// test name, if given
850 		if(name && strcmp(endpoint.name, name) != 0)
851 			return false;
852 		// test type, if given
853 		if(type != B_MEDIA_UNKNOWN_TYPE &&
854 			type != endpoint.format.type)
855 			return false;
856 
857 		return true;
858 	}
859 };
860 
861 template <class T>
862 class match_endpoint_type : public unary_function<T, bool> {
863 public:
864 	media_type type;
865 
866 	match_endpoint_type(media_type _type) :
867 		type(_type) {}
868 	bool operator()(const T& endpoint) const {
869 		// test type, if given
870 		if(type != B_MEDIA_UNKNOWN_TYPE &&
871 			type != endpoint.format.type)
872 			return false;
873 
874 		return true;
875 	}
876 };
877 
878 status_t NodeRef::findFreeInput(
879 	media_input*								outInput,
880 	const media_format*					format /*=0*/,
881 	const char*									name /*=0*/) const {
882 
883 	status_t err;
884 
885 	vector<media_input> inputs;
886 	vector<media_input>::const_iterator it;
887 	inputs.reserve(32);
888 
889 	err = getFreeInputs(inputs);
890 	if(err < B_OK)
891 		return err;
892 
893 	it = find_if(
894 		inputs.begin(),
895 		inputs.end(),
896 		match_endpoint_name_format<media_input>(name, format));
897 
898 	if(it != inputs.end()) {
899 		*outInput = *it;
900 		return B_OK;
901 	}
902 	return B_ERROR;
903 }
904 
905 status_t NodeRef::findFreeInput(
906 	media_input*								outInput,
907 	media_type									type /*=B_MEDIA_UNKNOWN_TYPE*/,
908 	const char*										name /*=0*/) const {
909 
910 	status_t err;
911 
912 	vector<media_input> inputs;
913 	vector<media_input>::const_iterator it;
914 	inputs.reserve(32);
915 
916 	err = getFreeInputs(inputs);
917 	if(err < B_OK)
918 		return err;
919 
920 	it = find_if(
921 		inputs.begin(),
922 		inputs.end(),
923 		match_endpoint_name_type<media_input>(name, type));
924 	if(it != inputs.end()) {
925 		*outInput = *it;
926 		return B_OK;
927 	}
928 	return B_ERROR;
929 }
930 
931 status_t NodeRef::findFreeOutput(
932 	media_output*								outOutput,
933 	const media_format*					format /*=0*/,
934 	const char*										name /*=0*/) const {
935 
936 	status_t err;
937 
938 	vector<media_output> outputs;
939 	vector<media_output>::const_iterator it;
940 	outputs.reserve(32);
941 
942 	err = getFreeOutputs(outputs);
943 	if(err < B_OK)
944 		return err;
945 
946 	it = find_if(
947 		outputs.begin(),
948 		outputs.end(),
949 		match_endpoint_name_format<media_output>(name, format));
950 	if(it != outputs.end()) {
951 		*outOutput = *it;
952 		return B_OK;
953 	}
954 	return B_ERROR;
955 }
956 
957 status_t NodeRef::findFreeOutput(
958 	media_output*								outOutput,
959 	media_type									type /*=B_MEDIA_UNKNOWN_TYPE*/,
960 	const char*										name /*=0*/) const {
961 
962 	status_t err;
963 
964 	vector<media_output> outputs;
965 	vector<media_output>::const_iterator it;
966 	outputs.reserve(32);
967 
968 	err = getFreeOutputs(outputs);
969 	if(err < B_OK)
970 		return err;
971 
972 	it = find_if(
973 		outputs.begin(),
974 		outputs.end(),
975 		match_endpoint_name_type<media_output>(name, type));
976 	if(it != outputs.end()) {
977 		*outOutput = *it;
978 		return B_OK;
979 	}
980 	return B_ERROR;
981 }
982 
983 
984 // node endpoint access: vector versions (wrappers for BMediaRoster
985 // calls.)
986 
987 status_t NodeRef::getFreeInputs(
988 	vector<media_input>&		ioInputs,
989 	media_type							filterType) const {
990 
991 	BMediaRoster* r = BMediaRoster::Roster();
992 	status_t err;
993 
994 	int32 count;
995 	int32 bufferInc = 16;
996 	int32 inputBufferSize = 16;
997 	media_input* inputBuffer = new media_input[inputBufferSize];
998 
999 	while (true) {
1000 		err = r->GetFreeInputsFor(
1001 			m_info.node, inputBuffer, inputBufferSize, &count, filterType);
1002 		if (err < B_OK) {
1003 			delete [] inputBuffer;
1004 			return err;
1005 		}
1006 
1007 		if (count == inputBufferSize) {
1008 			// buffer too small; increase & try again
1009 			inputBufferSize += bufferInc;
1010 			delete [] inputBuffer;
1011 			inputBuffer = new media_input[inputBufferSize];
1012 			continue;
1013 		}
1014 
1015 		if (count)
1016 			// copy found inputs into vector
1017 			copy(inputBuffer, inputBuffer + count,
1018 				back_inserter(ioInputs));
1019 
1020 		break;
1021 	}
1022 
1023 	// fix missing node info
1024 	_fixInputs(ioInputs);
1025 
1026 	delete [] inputBuffer;
1027 	return B_OK;
1028 }
1029 
1030 // +++++ broken?
1031 status_t NodeRef::getConnectedInputs(
1032 	vector<media_input>&		ioInputs,
1033 	media_type							filterType) const {
1034 
1035 	BMediaRoster* r = BMediaRoster::Roster();
1036 	status_t err;
1037 
1038 	int32 count;
1039 	int32 bufferInc = 16;
1040 	int32 inputBufferSize = 16;
1041 	media_input* inputBuffer = new media_input[inputBufferSize];
1042 
1043 	while (true) {
1044 		err = r->GetConnectedInputsFor(
1045 			m_info.node, inputBuffer, inputBufferSize, &count);
1046 		if (err < B_OK) {
1047 			delete [] inputBuffer;
1048 			return err;
1049 		}
1050 
1051 		if (count == inputBufferSize) {
1052 			// buffer too small; increase & try again
1053 			inputBufferSize += bufferInc;
1054 			delete [] inputBuffer;
1055 			inputBuffer = new media_input[inputBufferSize];
1056 			continue;
1057 		}
1058 
1059 		if (count)
1060 			// copy found inputs matching the given type into vector
1061 			remove_copy_if(inputBuffer, inputBuffer + count,
1062 				back_inserter(ioInputs),
1063 				not1(match_endpoint_type<media_input>(filterType)));
1064 
1065 		break;
1066 	}
1067 
1068 	// fix missing node info
1069 	_fixInputs(ioInputs);
1070 
1071 	delete [] inputBuffer;
1072 	return B_OK;
1073 }
1074 
1075 status_t NodeRef::getFreeOutputs(
1076 	vector<media_output>&		ioOutputs,
1077 	media_type							filterType) const {
1078 
1079 	BMediaRoster* r = BMediaRoster::Roster();
1080 	status_t err;
1081 
1082 	int32 count;
1083 	int32 bufferInc = 16;
1084 	int32 outputBufferSize = 16;
1085 	media_output* outputBuffer = new media_output[outputBufferSize];
1086 
1087 	while (true) {
1088 		err = r->GetFreeOutputsFor(
1089 			m_info.node, outputBuffer, outputBufferSize, &count, filterType);
1090 		if (err < B_OK) {
1091 			delete [] outputBuffer;
1092 			return err;
1093 		}
1094 
1095 		if (count == outputBufferSize) {
1096 			// buffer too small; increase & try again
1097 			outputBufferSize += bufferInc;
1098 			delete [] outputBuffer;
1099 			outputBuffer = new media_output[outputBufferSize];
1100 			continue;
1101 		}
1102 
1103 		if (count)
1104 			// copy found outputs into vector
1105 			copy(outputBuffer, outputBuffer + count,
1106 				back_inserter(ioOutputs));
1107 
1108 		break;
1109 	}
1110 
1111 	// fix missing node info
1112 	_fixOutputs(ioOutputs);
1113 
1114 	delete [] outputBuffer;
1115 	return B_OK;
1116 }
1117 
1118 status_t NodeRef::getConnectedOutputs(
1119 	vector<media_output>&		ioOutputs,
1120 	media_type							filterType) const {
1121 
1122 	BMediaRoster* r = BMediaRoster::Roster();
1123 	status_t err;
1124 
1125 	int32 count;
1126 	int32 bufferInc = 16;
1127 	int32 outputBufferSize = 16;
1128 	media_output* outputBuffer = new media_output[outputBufferSize];
1129 
1130 	while (true) {
1131 		err = r->GetConnectedOutputsFor(
1132 			m_info.node, outputBuffer, outputBufferSize, &count);
1133 		if (err < B_OK) {
1134 			delete [] outputBuffer;
1135 			return err;
1136 		}
1137 
1138 		if (count == outputBufferSize) {
1139 			// buffer too small; increase & try again
1140 			outputBufferSize += bufferInc;
1141 			delete [] outputBuffer;
1142 			outputBuffer = new media_output[outputBufferSize];
1143 			continue;
1144 		}
1145 
1146 		if (count)
1147 			// copy found outputs matching the given type into vector
1148 			remove_copy_if(outputBuffer, outputBuffer + count,
1149 				back_inserter(ioOutputs),
1150 				not1(match_endpoint_type<media_output>(filterType)));
1151 
1152 		break;
1153 	}
1154 
1155 	// fix missing node info
1156 	_fixOutputs(ioOutputs);
1157 
1158 	delete [] outputBuffer;
1159 	return B_OK;
1160 }
1161 
1162 
1163 // node endpoint access: array versions (wrappers for BMediaRoster
1164 // calls.)
1165 
1166 status_t NodeRef::getFreeInputs(
1167 	media_input*								outInputs,
1168 	int32												maxInputs,
1169 	int32*											outNumInputs,
1170 	media_type									filterType) const {
1171 
1172 	status_t err = BMediaRoster::Roster()->GetFreeInputsFor(
1173 		m_info.node, outInputs, maxInputs, outNumInputs, filterType);
1174 
1175 	if(err < B_OK)
1176 		return err;
1177 
1178 	// fix missing node info
1179 	_fixInputs(outInputs, *outNumInputs);
1180 	return err;
1181 }
1182 
1183 
1184 status_t NodeRef::getConnectedInputs(
1185 	media_input*								outInputs,
1186 	int32												maxInputs,
1187 	int32*											outNumInputs) const {
1188 
1189 	status_t err = BMediaRoster::Roster()->GetConnectedInputsFor(
1190 		m_info.node, outInputs, maxInputs, outNumInputs);
1191 
1192 	if(err < B_OK)
1193 		return err;
1194 
1195 	// fix missing node info
1196 	_fixInputs(outInputs, *outNumInputs);
1197 	return err;
1198 }
1199 
1200 status_t NodeRef::getFreeOutputs(
1201 	media_output*								outOutputs,
1202 	int32												maxOutputs,
1203 	int32*											outNumOutputs,
1204 	media_type									filterType) const {
1205 
1206 	status_t err = BMediaRoster::Roster()->GetFreeOutputsFor(
1207 		m_info.node, outOutputs, maxOutputs, outNumOutputs, filterType);
1208 
1209 	if(err < B_OK)
1210 		return err;
1211 
1212 	// fix missing node info
1213 	_fixOutputs(outOutputs, *outNumOutputs);
1214 	return err;
1215 }
1216 
1217 status_t NodeRef::getConnectedOutputs(
1218 	media_output*								outOutputs,
1219 	int32												maxOutputs,
1220 	int32*											outNumOutputs) const {
1221 
1222 	status_t err = BMediaRoster::Roster()->GetConnectedOutputsFor(
1223 		m_info.node, outOutputs, maxOutputs, outNumOutputs);
1224 
1225 	if(err < B_OK)
1226 		return err;
1227 
1228 	// fix missing node info
1229 	_fixOutputs(outOutputs, *outNumOutputs);
1230 	return err;
1231 }
1232 
1233 
1234 // -------------------------------------------------------- //
1235 // *** IPersistent
1236 // -------------------------------------------------------- //
1237 
1238 // !
1239 #if CORTEX_XML
1240 // !
1241 
1242 // +++++
1243 
1244 // !
1245 #endif /*CORTEX_XML*/
1246 // !
1247 
1248 // -------------------------------------------------------- //
1249 // *** BHandler:
1250 // -------------------------------------------------------- //
1251 
1252 void NodeRef::MessageReceived(
1253 	BMessage*								message) {
1254 
1255 	D_MESSAGE((
1256 		"NodeRef['%s']::MessageReceived(): %c%c%c%c\n",
1257 		name(),
1258 		 message->what >> 24,
1259 		(message->what >> 16)	& 0xff,
1260 		(message->what >> 8)	& 0xff,
1261 		(message->what) 			& 0xff));
1262 	status_t err;
1263 
1264 	switch(message->what) {
1265 		case M_SET_RUN_MODE:
1266 			{
1267 				// set run mode & delay (if given)
1268 				int32 runMode;
1269 				bigtime_t delay = 0LL;
1270 				err = message->FindInt32("runMode", &runMode);
1271 				if(err < B_OK) {
1272 					PRINT((
1273 						"! NodeRef::MessageReceived(M_SET_RUN_MODE): no value found.\n"));
1274 					break;
1275 				}
1276 				if(runMode == BMediaNode::B_RECORDING)
1277 					message->FindInt64("delay", &delay); // optional
1278 
1279 				setRunMode(runMode, delay);
1280 			}
1281 			break;
1282 
1283 
1284 		case M_PREROLL:
1285 			// +++++
1286 			break;
1287 
1288 		case M_SET_CYCLING:
1289 			{
1290 				bool cycling;
1291 				err = message->FindBool("cycling", &cycling);
1292 				if(err < B_OK) {
1293 					int32 val;
1294 					err = message->FindInt32("be:value", &val);
1295 					if(err < B_OK) {
1296 						PRINT((
1297 							"! NodeRef::MessageReceived(M_SET_CYCLING): no value found.\n"));
1298 						break;
1299 					}
1300 					cycling = val;
1301 				}
1302 
1303 				setCycling(cycling);
1304 			}
1305 			break;
1306 
1307 		case B_MEDIA_NODE_STOPPED:
1308 //			PRINT(("### B_MEDIA_NODE_STOPPED\n"));
1309 //
1310 			// if still marked running, let the group know [e.moon 11oct99]
1311 			if(m_running) {
1312 				m_running = false;
1313 				m_stopQueued = false;
1314 
1315 				if(m_group) {
1316 					Autolock _l(m_group);
1317 					m_group->_refStopped(this);
1318 				}
1319 			}
1320 
1321 			break;
1322 
1323 		case NodeSyncThread::M_SYNC_COMPLETE: {
1324 			// [e.moon 14oct99] position-report messages are now sent
1325 			// by the NodeSyncThread.
1326 
1327 			Autolock _l(this);
1328 
1329 			// unpack message
1330 			bigtime_t when, position;
1331 			err = message->FindInt64("perfTime", &when);
1332 			ASSERT(err == B_OK);
1333 			err = message->FindInt64("position", &position);
1334 			ASSERT(err == B_OK);
1335 
1336 			_handlePositionUpdate(when, position);
1337 			break;
1338 		}
1339 
1340 		default:
1341 			_inherited::MessageReceived(message);
1342 	}
1343 }
1344 
1345 // -------------------------------------------------------- //
1346 // *** IObservable:		[20aug99]
1347 // -------------------------------------------------------- //
1348 
1349 void NodeRef::observerAdded(
1350 	const BMessenger&				observer) {
1351 
1352 	BMessage m(M_OBSERVER_ADDED);
1353 	m.AddInt32("nodeID", id());
1354 	m.AddMessenger("target", BMessenger(this));
1355 	observer.SendMessage(&m);
1356 }
1357 
1358 void NodeRef::observerRemoved(
1359 	const BMessenger&				observer) {
1360 
1361 	BMessage m(M_OBSERVER_REMOVED);
1362 	m.AddInt32("nodeID", id());
1363 	m.AddMessenger("target", BMessenger(this));
1364 	observer.SendMessage(&m);
1365 }
1366 
1367 void NodeRef::notifyRelease() {
1368 
1369 	BMessage m(M_RELEASED);
1370 	m.AddInt32("nodeID", id());
1371 	m.AddMessenger("target", BMessenger(this));
1372 	notify(&m);
1373 }
1374 
1375 void NodeRef::releaseComplete() {
1376 	// +++++
1377 }
1378 
1379 // -------------------------------------------------------- //
1380 // *** ILockable: pass lock requests to parent group,
1381 //                or manager if no group found
1382 // -------------------------------------------------------- //
1383 
1384 // this is hideous. [24aug99]
1385 // it must die soon.
1386 
1387 // The two-stage lock appears safe UNLESS it's multiply acquired
1388 // (and the NodeManager is locked somewhere in between.)  Then
1389 // it's deadlocks all around...
1390 
1391 // safe two-stage lock (only WRITE locking is supported)
1392 // Notes:
1393 // a) a NodeRef either belongs to a group (m_group != 0) or
1394 //    is free.  If the ref is free, the NodeManager is the
1395 //    target for locking; otherwise the group is the 'lockee'.
1396 // b) operations which affect a NodeRef's group affiliation
1397 //    (ie. adding or removing a node to/from a group) must
1398 //    lock first the NodeManager, then the NodeGroup.  The
1399 //    locks should be released in the opposite order.
1400 
1401 bool NodeRef::lock(
1402 	lock_t type,
1403 	bigtime_t timeout) {
1404 
1405 	D_LOCK(("*** NodeRef::lock(): %ld\n", find_thread(0)));
1406 
1407 	ASSERT(type == WRITE);
1408 	ASSERT(m_manager);
1409 
1410 	// lock manager
1411 	if(!m_manager->lock(type, timeout))
1412 		return false;
1413 
1414 	// transfer lock to group, if any
1415 	NodeGroup* group = m_group;
1416 	if(!group)
1417 		return true;
1418 
1419 	bool ret = m_group->lock(type, timeout);
1420 
1421 	m_manager->unlock();
1422 
1423 	D_LOCK(("*** NodeRef::lock() ACQUIRED: %ld\n", find_thread(0)));
1424 
1425 	return ret;
1426 }
1427 
1428 bool NodeRef::unlock(
1429 		lock_t type) {
1430 
1431 	D_LOCK(("*** NodeRef::unlock(): %ld\n", find_thread(0)));
1432 
1433 	ASSERT(type == WRITE);
1434 	ASSERT(m_manager);
1435 
1436 	NodeGroup* group = m_group;
1437 	if(group) {
1438 		bool ret = m_group->unlock(type);
1439 		D_LOCK(("*** NodeRef::unlock() RELEASED: %ld\n", find_thread(0)));
1440 		return ret;
1441 	}
1442 
1443 	bool ret = m_manager->unlock(type);
1444 
1445 	D_LOCK(("*** NodeRef::unlock() RELEASED: %ld\n", find_thread(0)));
1446 	return ret;
1447 }
1448 
1449 bool NodeRef::isLocked(
1450 	lock_t type) const {
1451 
1452 	ASSERT(type == WRITE);
1453 	ASSERT(m_manager);
1454 
1455 	NodeGroup* group = m_group;
1456 	if(group)
1457 		return m_group->isLocked(type);
1458 
1459 	return m_manager->isLocked(type);
1460 }
1461 
1462 // -------------------------------------------------------- //
1463 // *** ctor
1464 // -------------------------------------------------------- //
1465 
1466 NodeRef::NodeRef(
1467 	const media_node&		node,
1468 	NodeManager*				manager,
1469 	uint32							userFlags,
1470 	uint32							implFlags) :
1471 
1472 	m_manager(manager),
1473 	m_group(0),
1474 	m_flags(userFlags),
1475 	m_implFlags(implFlags),
1476 	m_runMode(0),
1477 	m_recordingDelay(0LL),
1478 	m_watching(false),
1479 	m_addonHint(0),
1480 	m_positionReportsEnabled(false),
1481 	m_positionReportsStarted(false),
1482 	m_positionUpdatePeriod(s_defaultPositionUpdatePeriod),
1483 	m_tpLastPositionUpdate(0LL),
1484 	m_lastPosition(0LL),
1485 	m_positionThread(0),
1486 	m_running(false),
1487 	m_nodeReleased(false),
1488 	m_cycle(false),
1489 	m_prerolled(false),
1490 //	m_cycleSyncThread(0),
1491 	m_stopQueued(false),
1492 	m_latency(0LL) {
1493 
1494 	ASSERT(manager);
1495 
1496 	if(!m_manager->Lock()) {
1497 		ASSERT(!"m_manager->Lock() failed");
1498 	}
1499 	m_manager->AddHandler(this);
1500 	m_manager->Unlock();
1501 
1502 	// fetch node details
1503 	BMediaRoster* r = BMediaRoster::Roster();
1504 	status_t err = r->GetLiveNodeInfo(
1505 		node,
1506 		&m_info);
1507 
1508 	if(err < B_OK) {
1509 		PRINT((
1510 			"!!! NodeRef(): BMediaRoster::GetLiveNodeInfo(%" B_PRId32
1511 				") failed:\n"
1512 			"    %s\n",
1513 			node.node,
1514 			strerror(err)));
1515 		// at least store node info
1516 		m_info.node = node;
1517 	}
1518 
1519 	// name self after node
1520 	SetName(m_info.name);
1521 
1522 	// init Media Roster connection [e.moon 11oct99]
1523 	if(!(m_flags & NO_ROSTER_WATCH)) {
1524 		r->StartWatching(
1525 			BMessenger(this),
1526 			m_info.node,
1527 			B_MEDIA_NODE_STOPPED);
1528 		m_watching = true;
1529 	}
1530 }
1531 // -------------------------------------------------------- //
1532 // *** endpoint-fixing operations (no lock required)
1533 // -------------------------------------------------------- //
1534 
1535 template <class T>
1536 class fixEndpointFn : public unary_function<T&, void> {
1537 	const media_node&		node;
1538 public:
1539 	fixEndpointFn(const media_node& _n) : node(_n) {}
1540 	void operator()(T& endpoint) {
1541 //		PRINT((
1542 //			"fixEndpointFn(): endpoint '%s', node ID %ld\n",
1543 //				endpoint.name, endpoint.node.node));
1544 		if(endpoint.node != node) {
1545 			PRINT((
1546 				"  fixing '%s'\n", endpoint.name));
1547 			endpoint.node = node;
1548 		}
1549 	}
1550 };
1551 
1552 // 'fix' (fill in node if needed) sets of inputs/outputs
1553 void NodeRef::_fixInputs(
1554 	media_input*									inputs,
1555 	int32													count) const {
1556 
1557 	D_METHOD((
1558 		"NodeRef[%s]::fixInputs()\n", m_info.name));
1559 
1560 	for_each(
1561 		inputs,
1562 		inputs+count,
1563 		fixEndpointFn<media_input>(node()));
1564 }
1565 
1566 void NodeRef::_fixInputs(
1567 	vector<media_input>&			inputs) const {
1568 
1569 	D_METHOD((
1570 		"NodeRef[%s]::fixInputs()\n", m_info.name));
1571 
1572 	for_each(
1573 		inputs.begin(),
1574 		inputs.end(),
1575 		fixEndpointFn<media_input>(node()));
1576 }
1577 
1578 void NodeRef::_fixOutputs(
1579 	media_output*									outputs,
1580 	int32													count) const {
1581 
1582 	D_METHOD((
1583 		"NodeRef[%s]::fixOutputs()\n", m_info.name));
1584 
1585 	for_each(
1586 		outputs,
1587 		outputs+count,
1588 		fixEndpointFn<media_output>(node()));
1589 }
1590 
1591 void NodeRef::_fixOutputs(
1592 	vector<media_output>&		outputs) const {
1593 
1594 	D_METHOD((
1595 		"NodeRef[%s]::fixOutputs()\n", m_info.name));
1596 
1597 	for_each(
1598 		outputs.begin(),
1599 		outputs.end(),
1600 		fixEndpointFn<media_output>(node()));
1601 }
1602 
1603 // -------------------------------------------------------- //
1604 // *** internal/NodeManager operations (LOCK REQUIRED)
1605 // -------------------------------------------------------- //
1606 
1607 // call after instantiation to register the dormant_node_info
1608 // used to select this add-on node
1609 
1610 void NodeRef::_setAddonHint(
1611 	const dormant_node_info*				info,
1612 	const entry_ref*								file) {
1613 
1614 	assert_locked(this);
1615 
1616 	if(m_addonHint)
1617 		delete m_addonHint;
1618 
1619 	m_addonHint = new addon_hint(info, file);
1620 }
1621 
1622 // call to set a new group; if 0, the node must have no
1623 // connections
1624 void NodeRef::_setGroup(
1625 	NodeGroup*										group) {
1626 	assert_locked(this);
1627 
1628 	m_group = group;
1629 
1630 	if(!LockLooper()) {
1631 		ASSERT(!"LockLooper() failed.");
1632 	}
1633 	BMessage m(M_GROUP_CHANGED);
1634 	m.AddInt32("nodeID", (int32)m_info.node.node);
1635 	m.AddInt32("groupID", m_group ? (int32)m_group->id() : 0);
1636 	notify(&m);
1637 	UnlockLooper();
1638 }
1639 
1640 // *** NodeGroup API ***
1641 //     9aug99: moved from NodeGroup
1642 
1643 // initialize the given node's transport-state members
1644 // (this may be called from the transport thread or from
1645 //  an API-implementation method.)
1646 
1647 status_t NodeRef::_initTransportState() {
1648 	assert_locked(this);
1649 
1650 	D_METHOD((
1651 		"NodeRef('%s')::_initTransportState()\n",
1652 		name()));
1653 
1654 	// init transport state	for this node
1655 	m_prerolled = false;
1656 	m_tpStart = 0LL;
1657 	m_tpLastSeek = 0LL;
1658 	m_lastSeekPos = 0LL;
1659 
1660 	// +++++ init position reporting stuff here?
1661 
1662 	return B_OK;
1663 }
1664 
1665 status_t NodeRef::_setTimeSource(
1666 	media_node_id					timeSourceID) {
1667 	assert_locked(this);
1668 
1669 	D_METHOD((
1670 		"NodeRef('%s')::_setTimeSource(%ld)\n",
1671 		name(), timeSourceID));
1672 	status_t err;
1673 
1674 	// set time source
1675 	ASSERT(timeSourceID != media_node::null.node);
1676 	D_ROSTER(("# roster->SetTimeSourceFor()\n"));
1677 	err = m_manager->roster->SetTimeSourceFor(
1678 		id(), timeSourceID);
1679 
1680 	if(err < B_OK) {
1681 		PRINT((
1682 			"* NodeRef('%s')::_setTimeSource(%" B_PRId32 "):\n"
1683 			"  SetTimeSourceFor() failed: %s\n",
1684 			name(), timeSourceID, strerror(err)));
1685 	}
1686 
1687 	return err;
1688 }
1689 
1690 status_t NodeRef::_setRunMode(
1691 	const uint32					runMode,
1692 	bigtime_t							delay) {
1693 	assert_locked(this);
1694 
1695 	D_METHOD((
1696 		"NodeRef('%s')::_setRunMode(%ld : %Ld)\n",
1697 		name(), runMode, delay));
1698 	status_t err;
1699 
1700 
1701 	BMediaNode::run_mode m =
1702 		// if group is in offline mode, so are all its nodes
1703 		(runMode == BMediaNode::B_OFFLINE) ?
1704 			(BMediaNode::run_mode)runMode :
1705 			// if non-0, the node's setting is used
1706 			(m_runMode > 0) ?
1707 				(BMediaNode::run_mode)m_runMode :
1708 				(BMediaNode::run_mode)runMode;
1709 	ASSERT(m > 0);
1710 
1711 	// +++++ additional producer run-mode delay support here?
1712 
1713 	if(
1714 		kind() & B_BUFFER_PRODUCER &&
1715 		runMode == BMediaNode::B_RECORDING) {
1716 
1717 		D_ROSTER(("# roster->SetProducerRunModeDelay()\n"));
1718 		err = m_manager->roster->SetProducerRunModeDelay(
1719 			node(), delay, m);
1720 		if(err < B_OK) {
1721 			PRINT((
1722 				"NodeRef('%s')::_setRunMode(): SetProducerRunModeDelay(%"
1723 					B_PRIdBIGTIME ") failed: %s\n",
1724 				name(), delay, strerror(err)));
1725 		}
1726 	} else {
1727 
1728 		D_ROSTER(("# roster->SetRunModeNode()\n"));
1729 		err = m_manager->roster->SetRunModeNode(
1730 			node(), m);
1731 		if(err < B_OK) {
1732 			PRINT((
1733 				"NodeRef('%s')::_setRunMode(): SetRunModeNode(%d) failed: %s\n",
1734 				name(), m, strerror(err)));
1735 		}
1736 	}
1737 
1738 	return err;
1739 }
1740 
1741 status_t NodeRef::_setRunModeAuto(
1742 	const uint32					runMode) {
1743 
1744 	if(
1745 		kind() & B_BUFFER_PRODUCER &&
1746 		runMode == BMediaNode::B_RECORDING) {
1747 
1748 		return _setRunMode(
1749 			runMode,
1750 			calculateRecordingModeDelay());
1751 
1752 	} else
1753 		return _setRunMode(runMode);
1754 }
1755 
1756 // seek and preroll the given node.
1757 // *** this method should not be called from the transport thread
1758 // (since preroll operations can block for a relatively long time.)
1759 //
1760 // returns B_NOT_ALLOWED if the node is running, or if its NO_PREROLL
1761 // flag is set; otherwise, returns B_OK on success or a Media Roster
1762 // error.
1763 
1764 status_t NodeRef::_preroll(
1765 	bigtime_t							position) {
1766 	assert_locked(this);
1767 
1768 	D_METHOD((
1769 		"NodeRef('%s')::_preroll(%Ld)\n",
1770 		name(), position));
1771 	status_t err;
1772 
1773 	// make sure the node can be and wants to be prerolled
1774 	if(m_running ||
1775 		m_flags & NO_PREROLL)
1776 		return B_NOT_ALLOWED;
1777 
1778 	if(!(m_flags & NO_SEEK)) {
1779 		// seek the node first
1780 		err = BMediaRoster::Roster()->SeekNode(
1781 			node(),
1782 			position,
1783 			0LL);
1784 
1785 		if(err < B_OK) {
1786 			PRINT((
1787 				"*** NodeRef('%s')::_preroll(%" B_PRIdBIGTIME
1788 					"): BMediaRoster::SeekNode():\n"
1789 				"    %s\n",
1790 				name(), position, strerror(err)));
1791 			return err;
1792 		}
1793 	}
1794 
1795 	// preroll the node (*** this blocks until the node's Preroll()
1796 	//                       implementation returns ***)
1797 
1798 	err = BMediaRoster::Roster()->PrerollNode(
1799 		node());
1800 
1801 	if(err < B_OK) {
1802 		PRINT((
1803 			"*** NodeRef('%s')::_preroll(%" B_PRIdBIGTIME
1804 				"): BMediaRoster::PrerollNode():\n"
1805 			"    %s\n",
1806 			name(), position, strerror(err)));
1807 		return err;
1808 	}
1809 
1810 	m_prerolled = true;
1811 	m_tpLastSeek = 0LL;
1812 	m_lastSeekPos = position;
1813 
1814 	return B_OK;
1815 }
1816 
1817 // seek the given node if possible
1818 // (this may be called from the transport thread or from
1819 //  an API-implementation method.)
1820 
1821 status_t NodeRef::_seek(
1822 	bigtime_t							position,
1823 	bigtime_t							when) {
1824 	assert_locked(this);
1825 
1826 	D_METHOD((
1827 		"NodeRef('%s')::_seek(to %" B_PRIdBIGTIME ", at %" B_PRIdBIGTIME ")\n",
1828 		name(), position, when));
1829 
1830 	if(m_flags & NO_SEEK)
1831 		// the node should not be seek'd
1832 		return B_OK;
1833 
1834 	if(m_prerolled && m_lastSeekPos == position)
1835 		// the node has already been advanced to the proper position
1836 		return B_OK;
1837 
1838 	// do it
1839 	status_t err = BMediaRoster::Roster()->SeekNode(
1840 		node(), position, when);
1841 
1842 	if(err < B_OK) {
1843 		PRINT((
1844 			"*** NodeRef('%s')::_seek(to %" B_PRIdBIGTIME ", at %"
1845 				B_PRIdBIGTIME "): BMediaRoster::SeekNode():\n"
1846 			"    %s\n",
1847 			name(), position, when, strerror(err)));
1848 		return err;
1849 	}
1850 
1851 	// update node state
1852 	m_tpLastSeek = when;
1853 	m_lastSeekPos = position;
1854 
1855 	// node can't be considered prerolled after a seek
1856 	m_prerolled = false;
1857 
1858 	return B_OK;
1859 }
1860 
1861 // seek the given (stopped) node
1862 // (this may be called from the transport thread or from
1863 //  an API-implementation method.)
1864 
1865 status_t NodeRef::_seekStopped(
1866 	bigtime_t							position) {
1867 	assert_locked(this);
1868 
1869 	D_METHOD((
1870 		"NodeRef('%s')::_seekStopped(to %Ld)\n",
1871 		name(), position));
1872 
1873 	if(m_running)
1874 		return B_NOT_ALLOWED;
1875 
1876 	return _seek(position, 0LL);
1877 }
1878 
1879 
1880 // start the given node, if possible & necessary, at
1881 // the given time
1882 // (this may be called from the transport thread or from
1883 //  an API-implementation method.)
1884 
1885 status_t NodeRef::_start(
1886 	bigtime_t							when) {
1887 	assert_locked(this);
1888 
1889 	D_METHOD((
1890 		"NodeRef('%s')::_start(at %Ld)\n",
1891 		name(), when));
1892 
1893 	if(isRunning()) {
1894 		D_METHOD((
1895 			"  * node already running; aborting\n"));
1896 		return B_OK; // +++++ is this technically an error?
1897 	}
1898 
1899 	// +++++ is this proper?
1900 	ASSERT(m_group);
1901 	ASSERT(
1902 		m_group->m_transportState == NodeGroup::TRANSPORT_RUNNING ||
1903 		m_group->m_transportState == NodeGroup::TRANSPORT_STARTING);
1904 
1905 	if(m_flags & NO_START_STOP) {
1906 		D_METHOD((
1907 			"  * NO_START_STOP; aborting\n"));
1908 		return B_OK;
1909 	}
1910 
1911 	D_ROSTER(("# roster->StartNode(%ld)\n", id()));
1912 	status_t err = BMediaRoster::Roster()->StartNode(
1913 		node(),	when);
1914 
1915 	if(err < B_OK) {
1916 		PRINT((
1917 			"  * StartNode(%" B_PRId32 ") failed: '%s'\n",
1918 			id(), strerror(err)));
1919 		return err;
1920 	}
1921 
1922 	// update state
1923 	m_running = true;
1924 	m_tpStart = when;
1925 
1926 	// fetch new node latency
1927 	_updateLatency();
1928 
1929 	// start position tracking thread if needed
1930 	m_positionReportsStarted = false;
1931 	if(m_positionReportsEnabled)
1932 		_startPositionThread();
1933 
1934 	return B_OK;
1935 }
1936 
1937 // stop the given node (which may or may not still be
1938 // a member of this group.)
1939 // (this may be called from the transport thread or from
1940 //  an API-implementation method.)
1941 
1942 status_t NodeRef::_stop() {
1943 	assert_locked(this);
1944 
1945 	D_METHOD((
1946 		"NodeRef('%s')::_stop()\n",
1947 		name()));
1948 
1949 	if(!isRunning())
1950 		return B_OK; // +++++ error?
1951 
1952 	if(m_flags & NO_START_STOP || m_flags & NO_STOP)
1953 		return B_OK;
1954 
1955 	D_ROSTER(("# roster->StopNode(%ld)\n", id()));
1956 	status_t err = BMediaRoster::Roster()->StopNode(
1957 		node(), 0, true);
1958 
1959 	if(err < B_OK)
1960 		return err;
1961 
1962 	// 9aug99: refuse further position notification
1963 	_stopPositionThread();
1964 
1965 	// clear node's state
1966 	m_running = false;
1967 	m_stopQueued = false; // asked for immediate stop [e.moon 11oct99]
1968 	return _initTransportState();
1969 }
1970 
1971 // roll the given node, if possible
1972 // (this may be called from the transport thread or from
1973 //  an API-implementation method.)
1974 status_t NodeRef::_roll(
1975 	bigtime_t							start,
1976 	bigtime_t							stop,
1977 	bigtime_t							position) {
1978 	assert_locked(this);
1979 
1980 	D_METHOD((
1981 		"NodeRef('%s')::_roll(%Ld to %Ld, from %Ld)\n",
1982 		name(), start, stop, position));
1983 	status_t err;
1984 
1985 	// roll only if the node can be started & stopped,
1986 	// AND if this NodeRef is watching the Media Roster.
1987 	if(
1988 		m_flags & NO_START_STOP ||
1989 		m_flags & NO_STOP ||
1990 		m_flags & NO_ROSTER_WATCH)
1991 		return B_NOT_ALLOWED;
1992 
1993 	if(isRunning())
1994 		return B_NOT_ALLOWED;
1995 
1996 	ASSERT(m_group);
1997 	ASSERT(
1998 		m_group->m_transportState == NodeGroup::TRANSPORT_RUNNING ||
1999 		m_group->m_transportState == NodeGroup::TRANSPORT_STARTING);
2000 
2001 	D_ROSTER(("# roster->RollNode(%" B_PRId32 ")\n", id()));
2002 	if(m_flags & NO_SEEK)
2003 		err = BMediaRoster::Roster()->RollNode(
2004 			node(),	start, stop);
2005 	else
2006 		err = BMediaRoster::Roster()->RollNode(
2007 			node(),	start, stop, position);
2008 
2009 	if(err < B_OK) {
2010 		PRINT((
2011 			"NodeRef('%s')::_roll(%" B_PRIdBIGTIME " to %" B_PRIdBIGTIME
2012 				", from %" B_PRIdBIGTIME ")\n"
2013 			"!!! BMediaRoster::RollNode(%" B_PRId32 ") failed: '%s'\n",
2014 			name(), start, stop, position, id(), strerror(err)));
2015 		return err;
2016 	}
2017 
2018 	// update state
2019 	m_running = true;
2020 	m_stopQueued = true; // remember that node will stop on its own [e.moon 11oct99]
2021 	m_tpStart = start;
2022 
2023 	// fetch new node latency
2024 	_updateLatency();
2025 
2026 	// start position tracking thread if needed
2027 	m_positionReportsStarted = false;
2028 	if(m_positionReportsEnabled)
2029 		_startPositionThread();
2030 
2031 	return B_OK;
2032 }
2033 
2034 // [28sep99 e.moon]
2035 // refresh the node's current latency; if I reference
2036 // a B_RECORDING node, update its 'producer delay'.
2037 
2038 status_t NodeRef::_updateLatency() {
2039 	assert_locked(this);
2040 
2041 	// [11nov99 e.moon] don't bother if it's not a producer:
2042 	if(!(kind() & B_BUFFER_PRODUCER)) {
2043 		m_latency = 0LL;
2044 		return B_OK;
2045 	}
2046 
2047 	bigtime_t latency;
2048 	status_t err = BMediaRoster::Roster()->GetLatencyFor(
2049 		node(),
2050 		&latency);
2051 	if(err < B_OK) {
2052 		PRINT((
2053 			"* NodeRef('%s')::_updateLatency(): GetLatencyFor() failed:\n"
2054 			"  %s\n",
2055 			name(), strerror(err)));
2056 
2057 		return err;
2058 	}
2059 
2060 	// success
2061 	m_latency = latency;
2062 
2063 	// update run-mode & delay if necessary
2064 	if(
2065 		m_runMode == BMediaNode::B_RECORDING ||
2066 		(m_runMode == 0 && m_group && m_group->runMode() == BMediaNode::B_RECORDING))
2067 		_setRunModeAuto(BMediaNode::B_RECORDING);
2068 
2069 	return B_OK;
2070 }
2071 
2072 // Figure the earliest time at which the given node can be started.
2073 // Also calculates the position at which it should start from to
2074 // play in sync with other nodes in the group, if the transport is
2075 // running; if stopped, *outPosition will be set to the current
2076 // start position.
2077 // Pass the estimated amount of time needed to prepare the
2078 // node for playback (ie. preroll & a little fudge factor) in
2079 // startDelay.
2080 //
2081 // (this may be called from the transport thread or from
2082 //  an API-implementation method.)
2083 
2084 status_t NodeRef::_calcStartTime(
2085 	bigtime_t							startDelay,
2086 	bigtime_t*						outTime,
2087 	bigtime_t*						outPosition) {
2088 	assert_locked(this);
2089 
2090 	// +++++
2091 
2092 	return B_ERROR;
2093 }
2094 
2095 
2096 // -------------------------------------------------------- //
2097 // *** Position and cycle thread management *** (LOCK REQUIRED)
2098 // -------------------------------------------------------- //
2099 
2100 status_t NodeRef::_startPositionThread() {
2101 	assert_locked(this);
2102 	ASSERT(m_group);
2103 	status_t err;
2104 
2105 	if(!m_positionReportsEnabled)
2106 		return B_NOT_ALLOWED;
2107 
2108 	if(m_positionThread)
2109 		_stopPositionThread();
2110 
2111 	m_positionThread = new NodeSyncThread(
2112 		m_info.node,
2113 		new BMessenger(this));
2114 
2115 	// send an initial position report if necessary
2116 	if(!m_positionReportsStarted) {
2117 		m_positionReportsStarted = true;
2118 
2119 		err = _handlePositionUpdate(
2120 			m_tpStart,
2121 			m_lastSeekPos);
2122 
2123 		if(err < B_OK) {
2124 			PRINT((
2125 				"* NodeRef::_startPositionThread(): _handlePositionUpdate() failed:\n"
2126 				"  %s\\n",
2127 				strerror(err)));
2128 			return err;
2129 		}
2130 	}
2131 	else {
2132 
2133 		// figure when the last jump in position occurred
2134 		bigtime_t tpFrom = (m_tpLastSeek > m_tpStart) ? m_tpLastSeek : m_tpStart;
2135 
2136 		// figure the corresponding position
2137 		bigtime_t lastPosition = m_lastSeekPos;
2138 
2139 		// figure the next time for a position report
2140 		BTimeSource* ts = m_group->m_timeSourceObj;
2141 
2142 		bigtime_t tpTarget = ts->Now() + m_positionUpdatePeriod;
2143 		bigtime_t targetPosition = lastPosition + (tpTarget-tpFrom);
2144 
2145 		err = _schedulePositionUpdate(
2146 			tpTarget,
2147 			targetPosition);
2148 
2149 		if(err < B_OK) {
2150 			PRINT((
2151 				"* NodeRef::_createPositionThread(): _schedulePositionUpdate() failed:\n"
2152 				"  %s\\n",
2153 				strerror(err)));
2154 			return err;
2155 		}
2156 	}
2157 
2158 	return B_OK;
2159 }
2160 
2161 status_t NodeRef::_handlePositionUpdate(
2162 	bigtime_t							perfTime,
2163 	bigtime_t							position) {
2164 	assert_locked(this);
2165 	status_t err;
2166 
2167 	if(!m_running) {
2168 		PRINT((
2169 			"* NodeRef::_handlePositionUpdate(): not running.\n"));
2170 		return B_NOT_ALLOWED;
2171 	}
2172 
2173 	if(!m_positionReportsEnabled) {
2174 		PRINT((
2175 			"* NodeRef::_handlePositionUpdate(): position reports disabled.\n"));
2176 		return B_NOT_ALLOWED;
2177 	}
2178 
2179 	// store info
2180 	m_tpLastPositionUpdate = perfTime;
2181 	m_lastPosition = position;
2182 
2183 	// relay notification to all 'position listeners'
2184 	_notifyPosition(perfTime, position);
2185 
2186 	// schedule next update
2187 	err = _schedulePositionUpdate(
2188 		perfTime + m_positionUpdatePeriod,
2189 		position + m_positionUpdatePeriod);
2190 
2191 	if(err < B_OK) {
2192 		PRINT((
2193 			"* NodeRef::_handlePositionUpdate(): _schedulePositionUpdate() failed:\n"
2194 			"  %s\n",
2195 			strerror(err)));
2196 	}
2197 	return err;
2198 }
2199 
2200 status_t NodeRef::_schedulePositionUpdate(
2201 	bigtime_t							when,
2202 	bigtime_t							position) {
2203 	assert_locked(this);
2204 	status_t err;
2205 
2206 	if(!m_positionReportsEnabled)
2207 		return B_NOT_ALLOWED;
2208 	ASSERT(m_positionThread);
2209 
2210 	if(m_cycle && m_group->_cycleValid()) {
2211 		if(position >= m_group->endPosition()) {
2212 			// snap to start of next cycle
2213 			when = m_group->_cycleBoundary();
2214 			position = m_group->startPosition();
2215 		}
2216 	}
2217 
2218 ////	position_sync_msg m = {
2219 ////		id(),
2220 ////		m_group->id(),
2221 ////		when,
2222 ////		position
2223 ////	};
2224 //
2225 ////	PRINT((
2226 ////		"NodeRef::_schedulePositionUpdate():\n"
2227 ////		"  when     = %Ld\n"
2228 ////		"  position = %Ld\n",
2229 ////		when, position));
2230 //
2231 //	m_positionSyncThread->setPosition(position);
2232 //
2233 //	if(first)
2234 //		err = m_positionSyncThread->go(when);
2235 //	else
2236 //		err = m_positionSyncThread->reschedule(when);
2237 
2238 	err = m_positionThread->sync(when, position, B_INFINITE_TIMEOUT);
2239 
2240 	if(err < B_OK) {
2241 		PRINT((
2242 			"! NodeRef::_schedulePositionUpdate(): m_positionThread->sync() failed:\n"
2243 			"  %s\n", strerror(err)));
2244 	}
2245 	return err;
2246 }
2247 
2248 status_t NodeRef::_stopPositionThread() {
2249 	assert_locked(this);
2250 
2251 	if(!m_positionThread)
2252 		return B_NOT_ALLOWED;
2253 
2254 	delete m_positionThread;
2255 	m_positionThread = 0;
2256 
2257 	return B_OK;
2258 }
2259 
2260 
2261 // Send a message to all position listeners
2262 status_t NodeRef::_notifyPosition(
2263 	bigtime_t							when,
2264 	bigtime_t							position) {
2265 	assert_locked(this);
2266 	status_t err = B_OK;
2267 
2268 	if(!m_positionReportsEnabled)
2269 		return B_NOT_ALLOWED;
2270 
2271 	BMessage message(M_POSITION);
2272 	message.AddInt32("nodeID", id());
2273 	message.AddInt64("when", when);
2274 	message.AddInt64("position", position);
2275 
2276 	m_positionInvoker.Invoke(&message);
2277 
2278 	return err;
2279 }
2280 
2281 // END -- NodeRef.cpp --
2282