xref: /haiku/src/apps/cortex/NodeManager/NodeRef.cpp (revision 7a74a5df454197933bc6e80a542102362ee98703)
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 		"  %Ld\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(%ld) 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(%ld) failed:\n"
687 					"    %s\n",
688 					m_info.node.node,
689 					strerror(err)));
690 			}
691 		}
692 	}
693 	else {
694 //		PRINT(("- not releasing node\n"));
695 	}
696 
697 	m_nodeReleased = true;
698 	return err;
699 }
700 
701 
702 // calculate total (internal + downstream) latency for this node
703 
704 status_t NodeRef::totalLatency(
705 	bigtime_t*									outLatency) const {
706 
707 	return BMediaRoster::Roster()->GetLatencyFor(
708 		m_info.node,
709 		outLatency);
710 }
711 
712 // retrieve input/output matching the given destination/source.
713 // returns B_MEDIA_BAD_[SOURCE | DESTINATION] if the destination
714 // or source don't correspond to this node.
715 
716 class match_input_destination { public:
717 	const media_destination& dest;
718 	match_input_destination(const media_destination& _dest) : dest(_dest) {}
719 	bool operator()(const media_input& input) const {
720 		return input.destination == dest;
721 	}
722 };
723 
724 class match_output_source { public:
725 	const media_source& source;
726 	match_output_source(const media_source& _source) : source(_source) {}
727 	bool operator()(const media_output& output) const {
728 		return output.source == source;
729 	}
730 };
731 
732 status_t NodeRef::findInput(
733 	const media_destination&			forDestination,
734 	media_input*									outInput) const {
735 
736 	status_t err;
737 
738 	vector<media_input> inputs;
739 	vector<media_input>::const_iterator it;
740 	inputs.reserve(32);
741 
742 	// check free inputs
743 	err = getFreeInputs(inputs);
744 	if(err < B_OK)
745 		return err;
746 
747 	it = find_if(
748 		inputs.begin(), inputs.end(),
749 		match_input_destination(forDestination));
750 
751 	if(it != inputs.end()) {
752 		*outInput = *it;
753 		return B_OK;
754 	}
755 
756 	// check connected inputs
757 	inputs.clear();
758 	err = getConnectedInputs(inputs);
759 	if(err < B_OK)
760 		return err;
761 
762 	it = find_if(
763 		inputs.begin(), inputs.end(),
764 		match_input_destination(forDestination));
765 
766 	if(it != inputs.end()) {
767 		*outInput = *it;
768 		return B_OK;
769 	}
770 	return B_MEDIA_BAD_DESTINATION;
771 }
772 
773 status_t NodeRef::findOutput(
774 	const media_source&					forSource,
775 	media_output*								outOutput) const {
776 
777 	status_t err;
778 
779 	vector<media_output> outputs;
780 	vector<media_output>::const_iterator it;
781 	outputs.reserve(32);
782 
783 	// check free outputs
784 	err = getFreeOutputs(outputs);
785 	if(err < B_OK)
786 		return err;
787 
788 	it = find_if(
789 		outputs.begin(), outputs.end(),
790 		match_output_source(forSource));
791 
792 	if(it != outputs.end()) {
793 		*outOutput = *it;
794 		return B_OK;
795 	}
796 
797 	// check connected outputs
798 	outputs.clear();
799 	err = getConnectedOutputs(outputs);
800 	if(err < B_OK)
801 		return err;
802 
803 	it = find_if(
804 		outputs.begin(), outputs.end(),
805 		match_output_source(forSource));
806 
807 	if(it != outputs.end()) {
808 		*outOutput = *it;
809 		return B_OK;
810 	}
811 
812 	return B_MEDIA_BAD_SOURCE;
813 }
814 
815 
816 // endpoint matching (given name and/or format as 'hints')
817 
818 template <class T>
819 class match_endpoint_name_format : public unary_function<T, bool> {
820 public:
821 	const char* name;
822 	const media_format* format;
823 
824 	match_endpoint_name_format(const char* _name, const media_format* _format) :
825 		name(_name), format(_format) {}
826 	bool operator()(const T& endpoint) const {
827 		// test name, if given
828 		if(name && strcmp(endpoint.name, name) != 0)
829 			return false;
830 		// test format, if given
831 		media_format* f1 = const_cast<media_format*>(format);
832 		media_format* f2 = const_cast<media_format*>(&endpoint.format);
833 		if(format && !f1->Matches(f2))
834 			return false;
835 		return true;
836 	}
837 };
838 
839 template <class T>
840 class match_endpoint_name_type : public unary_function<T, bool> {
841 public:
842 	const char* name;
843 	media_type type;
844 
845 	match_endpoint_name_type(const char* _name, media_type _type) :
846 		name(_name), type(_type) {}
847 	bool operator()(const T& endpoint) const {
848 		// test name, if given
849 		if(name && strcmp(endpoint.name, name) != 0)
850 			return false;
851 		// test type, if given
852 		if(type != B_MEDIA_UNKNOWN_TYPE &&
853 			type != endpoint.format.type)
854 			return false;
855 
856 		return true;
857 	}
858 };
859 
860 template <class T>
861 class match_endpoint_type : public unary_function<T, bool> {
862 public:
863 	media_type type;
864 
865 	match_endpoint_type(media_type _type) :
866 		type(_type) {}
867 	bool operator()(const T& endpoint) const {
868 		// test type, if given
869 		if(type != B_MEDIA_UNKNOWN_TYPE &&
870 			type != endpoint.format.type)
871 			return false;
872 
873 		return true;
874 	}
875 };
876 
877 status_t NodeRef::findFreeInput(
878 	media_input*								outInput,
879 	const media_format*					format /*=0*/,
880 	const char*									name /*=0*/) const {
881 
882 	status_t err;
883 
884 	vector<media_input> inputs;
885 	vector<media_input>::const_iterator it;
886 	inputs.reserve(32);
887 
888 	err = getFreeInputs(inputs);
889 	if(err < B_OK)
890 		return err;
891 
892 	it = find_if(
893 		inputs.begin(),
894 		inputs.end(),
895 		match_endpoint_name_format<media_input>(name, format));
896 
897 	if(it != inputs.end()) {
898 		*outInput = *it;
899 		return B_OK;
900 	}
901 	return B_ERROR;
902 }
903 
904 status_t NodeRef::findFreeInput(
905 	media_input*								outInput,
906 	media_type									type /*=B_MEDIA_UNKNOWN_TYPE*/,
907 	const char*										name /*=0*/) const {
908 
909 	status_t err;
910 
911 	vector<media_input> inputs;
912 	vector<media_input>::const_iterator it;
913 	inputs.reserve(32);
914 
915 	err = getFreeInputs(inputs);
916 	if(err < B_OK)
917 		return err;
918 
919 	it = find_if(
920 		inputs.begin(),
921 		inputs.end(),
922 		match_endpoint_name_type<media_input>(name, type));
923 	if(it != inputs.end()) {
924 		*outInput = *it;
925 		return B_OK;
926 	}
927 	return B_ERROR;
928 }
929 
930 status_t NodeRef::findFreeOutput(
931 	media_output*								outOutput,
932 	const media_format*					format /*=0*/,
933 	const char*										name /*=0*/) const {
934 
935 	status_t err;
936 
937 	vector<media_output> outputs;
938 	vector<media_output>::const_iterator it;
939 	outputs.reserve(32);
940 
941 	err = getFreeOutputs(outputs);
942 	if(err < B_OK)
943 		return err;
944 
945 	it = find_if(
946 		outputs.begin(),
947 		outputs.end(),
948 		match_endpoint_name_format<media_output>(name, format));
949 	if(it != outputs.end()) {
950 		*outOutput = *it;
951 		return B_OK;
952 	}
953 	return B_ERROR;
954 }
955 
956 status_t NodeRef::findFreeOutput(
957 	media_output*								outOutput,
958 	media_type									type /*=B_MEDIA_UNKNOWN_TYPE*/,
959 	const char*										name /*=0*/) const {
960 
961 	status_t err;
962 
963 	vector<media_output> outputs;
964 	vector<media_output>::const_iterator it;
965 	outputs.reserve(32);
966 
967 	err = getFreeOutputs(outputs);
968 	if(err < B_OK)
969 		return err;
970 
971 	it = find_if(
972 		outputs.begin(),
973 		outputs.end(),
974 		match_endpoint_name_type<media_output>(name, type));
975 	if(it != outputs.end()) {
976 		*outOutput = *it;
977 		return B_OK;
978 	}
979 	return B_ERROR;
980 }
981 
982 
983 // node endpoint access: vector versions (wrappers for BMediaRoster
984 // calls.)
985 
986 status_t NodeRef::getFreeInputs(
987 	vector<media_input>&		ioInputs,
988 	media_type							filterType) const {
989 
990 	BMediaRoster* r = BMediaRoster::Roster();
991 	status_t err;
992 
993 	int32 count;
994 	int32 bufferInc = 16;
995 	int32 inputBufferSize = 16;
996 	media_input* inputBuffer = new media_input[inputBufferSize];
997 
998 	while(true) {
999 		err = r->GetFreeInputsFor(
1000 			m_info.node, inputBuffer, inputBufferSize, &count, filterType);
1001 		if(err < B_OK)
1002 			return err;
1003 
1004 		if(count == inputBufferSize) {
1005 			// buffer too small; increase & try again
1006 			inputBufferSize += bufferInc;
1007 			delete [] inputBuffer;
1008 			inputBuffer = new media_input[inputBufferSize];
1009 			continue;
1010 		}
1011 
1012 		if(count)
1013 			// copy found inputs into vector
1014 			copy(inputBuffer, inputBuffer + count,
1015 				back_inserter(ioInputs));
1016 
1017 		break;
1018 	}
1019 
1020 	// fix missing node info
1021 	_fixInputs(ioInputs);
1022 
1023 	delete [] inputBuffer;
1024 	return B_OK;
1025 }
1026 
1027 // +++++ broken?
1028 status_t NodeRef::getConnectedInputs(
1029 	vector<media_input>&		ioInputs,
1030 	media_type							filterType) const {
1031 
1032 	BMediaRoster* r = BMediaRoster::Roster();
1033 	status_t err;
1034 
1035 	int32 count;
1036 	int32 bufferInc = 16;
1037 	int32 inputBufferSize = 16;
1038 	media_input* inputBuffer = new media_input[inputBufferSize];
1039 
1040 	while(true) {
1041 		err = r->GetConnectedInputsFor(
1042 			m_info.node, inputBuffer, inputBufferSize, &count);
1043 		if(err < B_OK)
1044 			return err;
1045 
1046 		if(count == inputBufferSize) {
1047 			// buffer too small; increase & try again
1048 			inputBufferSize += bufferInc;
1049 			delete [] inputBuffer;
1050 			inputBuffer = new media_input[inputBufferSize];
1051 			continue;
1052 		}
1053 
1054 		if(count)
1055 			// copy found inputs matching the given type into vector
1056 			remove_copy_if(inputBuffer, inputBuffer + count,
1057 				back_inserter(ioInputs),
1058 				not1(match_endpoint_type<media_input>(filterType)));
1059 
1060 		break;
1061 	}
1062 
1063 	// fix missing node info
1064 	_fixInputs(ioInputs);
1065 
1066 	delete [] inputBuffer;
1067 	return B_OK;
1068 }
1069 
1070 status_t NodeRef::getFreeOutputs(
1071 	vector<media_output>&		ioOutputs,
1072 	media_type							filterType) const {
1073 
1074 	BMediaRoster* r = BMediaRoster::Roster();
1075 	status_t err;
1076 
1077 	int32 count;
1078 	int32 bufferInc = 16;
1079 	int32 outputBufferSize = 16;
1080 	media_output* outputBuffer = new media_output[outputBufferSize];
1081 
1082 	while(true) {
1083 		err = r->GetFreeOutputsFor(
1084 			m_info.node, outputBuffer, outputBufferSize, &count, filterType);
1085 		if(err < B_OK)
1086 			return err;
1087 
1088 		if(count == outputBufferSize) {
1089 			// buffer too small; increase & try again
1090 			outputBufferSize += bufferInc;
1091 			delete [] outputBuffer;
1092 			outputBuffer = new media_output[outputBufferSize];
1093 			continue;
1094 		}
1095 
1096 		if(count)
1097 			// copy found outputs into vector
1098 			copy(outputBuffer, outputBuffer + count,
1099 				back_inserter(ioOutputs));
1100 
1101 		break;
1102 	}
1103 
1104 	// fix missing node info
1105 	_fixOutputs(ioOutputs);
1106 
1107 	delete [] outputBuffer;
1108 	return B_OK;
1109 }
1110 
1111 status_t NodeRef::getConnectedOutputs(
1112 	vector<media_output>&		ioOutputs,
1113 	media_type							filterType) const {
1114 
1115 	BMediaRoster* r = BMediaRoster::Roster();
1116 	status_t err;
1117 
1118 	int32 count;
1119 	int32 bufferInc = 16;
1120 	int32 outputBufferSize = 16;
1121 	media_output* outputBuffer = new media_output[outputBufferSize];
1122 
1123 	while(true) {
1124 		err = r->GetConnectedOutputsFor(
1125 			m_info.node, outputBuffer, outputBufferSize, &count);
1126 		if(err < B_OK)
1127 			return err;
1128 
1129 		if(count == outputBufferSize) {
1130 			// buffer too small; increase & try again
1131 			outputBufferSize += bufferInc;
1132 			delete [] outputBuffer;
1133 			outputBuffer = new media_output[outputBufferSize];
1134 			continue;
1135 		}
1136 
1137 		if(count)
1138 			// copy found outputs matching the given type into vector
1139 			remove_copy_if(outputBuffer, outputBuffer + count,
1140 				back_inserter(ioOutputs),
1141 				not1(match_endpoint_type<media_output>(filterType)));
1142 
1143 		break;
1144 	}
1145 
1146 	// fix missing node info
1147 	_fixOutputs(ioOutputs);
1148 
1149 	delete [] outputBuffer;
1150 	return B_OK;
1151 }
1152 
1153 
1154 // node endpoint access: array versions (wrappers for BMediaRoster
1155 // calls.)
1156 
1157 status_t NodeRef::getFreeInputs(
1158 	media_input*								outInputs,
1159 	int32												maxInputs,
1160 	int32*											outNumInputs,
1161 	media_type									filterType) const {
1162 
1163 	status_t err = BMediaRoster::Roster()->GetFreeInputsFor(
1164 		m_info.node, outInputs, maxInputs, outNumInputs, filterType);
1165 
1166 	if(err < B_OK)
1167 		return err;
1168 
1169 	// fix missing node info
1170 	_fixInputs(outInputs, *outNumInputs);
1171 	return err;
1172 }
1173 
1174 
1175 status_t NodeRef::getConnectedInputs(
1176 	media_input*								outInputs,
1177 	int32												maxInputs,
1178 	int32*											outNumInputs) const {
1179 
1180 	status_t err = BMediaRoster::Roster()->GetConnectedInputsFor(
1181 		m_info.node, outInputs, maxInputs, outNumInputs);
1182 
1183 	if(err < B_OK)
1184 		return err;
1185 
1186 	// fix missing node info
1187 	_fixInputs(outInputs, *outNumInputs);
1188 	return err;
1189 }
1190 
1191 status_t NodeRef::getFreeOutputs(
1192 	media_output*								outOutputs,
1193 	int32												maxOutputs,
1194 	int32*											outNumOutputs,
1195 	media_type									filterType) const {
1196 
1197 	status_t err = BMediaRoster::Roster()->GetFreeOutputsFor(
1198 		m_info.node, outOutputs, maxOutputs, outNumOutputs, filterType);
1199 
1200 	if(err < B_OK)
1201 		return err;
1202 
1203 	// fix missing node info
1204 	_fixOutputs(outOutputs, *outNumOutputs);
1205 	return err;
1206 }
1207 
1208 status_t NodeRef::getConnectedOutputs(
1209 	media_output*								outOutputs,
1210 	int32												maxOutputs,
1211 	int32*											outNumOutputs) const {
1212 
1213 	status_t err = BMediaRoster::Roster()->GetConnectedOutputsFor(
1214 		m_info.node, outOutputs, maxOutputs, outNumOutputs);
1215 
1216 	if(err < B_OK)
1217 		return err;
1218 
1219 	// fix missing node info
1220 	_fixOutputs(outOutputs, *outNumOutputs);
1221 	return err;
1222 }
1223 
1224 
1225 // -------------------------------------------------------- //
1226 // *** IPersistent
1227 // -------------------------------------------------------- //
1228 
1229 // !
1230 #if CORTEX_XML
1231 // !
1232 
1233 // +++++
1234 
1235 // !
1236 #endif /*CORTEX_XML*/
1237 // !
1238 
1239 // -------------------------------------------------------- //
1240 // *** BHandler:
1241 // -------------------------------------------------------- //
1242 
1243 void NodeRef::MessageReceived(
1244 	BMessage*								message) {
1245 
1246 	D_MESSAGE((
1247 		"NodeRef['%s']::MessageReceived(): %c%c%c%c\n",
1248 		name(),
1249 		 message->what >> 24,
1250 		(message->what >> 16)	& 0xff,
1251 		(message->what >> 8)	& 0xff,
1252 		(message->what) 			& 0xff));
1253 	status_t err;
1254 
1255 	switch(message->what) {
1256 		case M_SET_RUN_MODE:
1257 			{
1258 				// set run mode & delay (if given)
1259 				int32 runMode;
1260 				bigtime_t delay = 0LL;
1261 				err = message->FindInt32("runMode", &runMode);
1262 				if(err < B_OK) {
1263 					PRINT((
1264 						"! NodeRef::MessageReceived(M_SET_RUN_MODE): no value found.\n"));
1265 					break;
1266 				}
1267 				if(runMode == BMediaNode::B_RECORDING)
1268 					message->FindInt64("delay", &delay); // optional
1269 
1270 				setRunMode(runMode, delay);
1271 			}
1272 			break;
1273 
1274 
1275 		case M_PREROLL:
1276 			// +++++
1277 			break;
1278 
1279 		case M_SET_CYCLING:
1280 			{
1281 				bool cycling;
1282 				err = message->FindBool("cycling", &cycling);
1283 				if(err < B_OK) {
1284 					int32 val;
1285 					err = message->FindInt32("be:value", &val);
1286 					if(err < B_OK) {
1287 						PRINT((
1288 							"! NodeRef::MessageReceived(M_SET_CYCLING): no value found.\n"));
1289 						break;
1290 					}
1291 					cycling = val;
1292 				}
1293 
1294 				setCycling(cycling);
1295 			}
1296 			break;
1297 
1298 		case B_MEDIA_NODE_STOPPED:
1299 //			PRINT(("### B_MEDIA_NODE_STOPPED\n"));
1300 //
1301 			// if still marked running, let the group know [e.moon 11oct99]
1302 			if(m_running) {
1303 				m_running = false;
1304 				m_stopQueued = false;
1305 
1306 				if(m_group) {
1307 					Autolock _l(m_group);
1308 					m_group->_refStopped(this);
1309 				}
1310 			}
1311 
1312 			break;
1313 
1314 		case NodeSyncThread::M_SYNC_COMPLETE: {
1315 			// [e.moon 14oct99] position-report messages are now sent
1316 			// by the NodeSyncThread.
1317 
1318 			Autolock _l(this);
1319 
1320 			// unpack message
1321 			bigtime_t when, position;
1322 			err = message->FindInt64("perfTime", &when);
1323 			ASSERT(err == B_OK);
1324 			err = message->FindInt64("position", &position);
1325 			ASSERT(err == B_OK);
1326 
1327 			_handlePositionUpdate(when, position);
1328 			break;
1329 		}
1330 
1331 		default:
1332 			_inherited::MessageReceived(message);
1333 	}
1334 }
1335 
1336 // -------------------------------------------------------- //
1337 // *** IObservable:		[20aug99]
1338 // -------------------------------------------------------- //
1339 
1340 void NodeRef::observerAdded(
1341 	const BMessenger&				observer) {
1342 
1343 	BMessage m(M_OBSERVER_ADDED);
1344 	m.AddInt32("nodeID", id());
1345 	m.AddMessenger("target", BMessenger(this));
1346 	observer.SendMessage(&m);
1347 }
1348 
1349 void NodeRef::observerRemoved(
1350 	const BMessenger&				observer) {
1351 
1352 	BMessage m(M_OBSERVER_REMOVED);
1353 	m.AddInt32("nodeID", id());
1354 	m.AddMessenger("target", BMessenger(this));
1355 	observer.SendMessage(&m);
1356 }
1357 
1358 void NodeRef::notifyRelease() {
1359 
1360 	BMessage m(M_RELEASED);
1361 	m.AddInt32("nodeID", id());
1362 	m.AddMessenger("target", BMessenger(this));
1363 	notify(&m);
1364 }
1365 
1366 void NodeRef::releaseComplete() {
1367 	// +++++
1368 }
1369 
1370 // -------------------------------------------------------- //
1371 // *** ILockable: pass lock requests to parent group,
1372 //                or manager if no group found
1373 // -------------------------------------------------------- //
1374 
1375 // this is hideous. [24aug99]
1376 // it must die soon.
1377 
1378 // The two-stage lock appears safe UNLESS it's multiply acquired
1379 // (and the NodeManager is locked somewhere in between.)  Then
1380 // it's deadlocks all around...
1381 
1382 // safe two-stage lock (only WRITE locking is supported)
1383 // Notes:
1384 // a) a NodeRef either belongs to a group (m_group != 0) or
1385 //    is free.  If the ref is free, the NodeManager is the
1386 //    target for locking; otherwise the group is the 'lockee'.
1387 // b) operations which affect a NodeRef's group affiliation
1388 //    (ie. adding or removing a node to/from a group) must
1389 //    lock first the NodeManager, then the NodeGroup.  The
1390 //    locks should be released in the opposite order.
1391 
1392 bool NodeRef::lock(
1393 	lock_t type,
1394 	bigtime_t timeout) {
1395 
1396 	D_LOCK(("*** NodeRef::lock(): %ld\n", find_thread(0)));
1397 
1398 	ASSERT(type == WRITE);
1399 	ASSERT(m_manager);
1400 
1401 	// lock manager
1402 	if(!m_manager->lock(type, timeout))
1403 		return false;
1404 
1405 	// transfer lock to group, if any
1406 	NodeGroup* group = m_group;
1407 	if(!group)
1408 		return true;
1409 
1410 	bool ret = m_group->lock(type, timeout);
1411 
1412 	m_manager->unlock();
1413 
1414 	D_LOCK(("*** NodeRef::lock() ACQUIRED: %ld\n", find_thread(0)));
1415 
1416 	return ret;
1417 }
1418 
1419 bool NodeRef::unlock(
1420 		lock_t type) {
1421 
1422 	D_LOCK(("*** NodeRef::unlock(): %ld\n", find_thread(0)));
1423 
1424 	ASSERT(type == WRITE);
1425 	ASSERT(m_manager);
1426 
1427 	NodeGroup* group = m_group;
1428 	if(group) {
1429 		bool ret = m_group->unlock(type);
1430 		D_LOCK(("*** NodeRef::unlock() RELEASED: %ld\n", find_thread(0)));
1431 		return ret;
1432 	}
1433 
1434 	bool ret = m_manager->unlock(type);
1435 
1436 	D_LOCK(("*** NodeRef::unlock() RELEASED: %ld\n", find_thread(0)));
1437 	return ret;
1438 }
1439 
1440 bool NodeRef::isLocked(
1441 	lock_t type) const {
1442 
1443 	ASSERT(type == WRITE);
1444 	ASSERT(m_manager);
1445 
1446 	NodeGroup* group = m_group;
1447 	if(group)
1448 		return m_group->isLocked(type);
1449 
1450 	return m_manager->isLocked(type);
1451 }
1452 
1453 // -------------------------------------------------------- //
1454 // *** ctor
1455 // -------------------------------------------------------- //
1456 
1457 NodeRef::NodeRef(
1458 	const media_node&		node,
1459 	NodeManager*				manager,
1460 	uint32							userFlags,
1461 	uint32							implFlags) :
1462 
1463 	m_manager(manager),
1464 	m_group(0),
1465 	m_flags(userFlags),
1466 	m_implFlags(implFlags),
1467 	m_runMode(0),
1468 	m_recordingDelay(0LL),
1469 	m_watching(false),
1470 	m_addonHint(0),
1471 	m_positionReportsEnabled(false),
1472 	m_positionReportsStarted(false),
1473 	m_positionUpdatePeriod(s_defaultPositionUpdatePeriod),
1474 	m_tpLastPositionUpdate(0LL),
1475 	m_lastPosition(0LL),
1476 	m_positionThread(0),
1477 	m_running(false),
1478 	m_nodeReleased(false),
1479 	m_cycle(false),
1480 	m_prerolled(false),
1481 //	m_cycleSyncThread(0),
1482 	m_stopQueued(false),
1483 	m_latency(0LL) {
1484 
1485 	ASSERT(manager);
1486 
1487 	if(!m_manager->Lock()) {
1488 		ASSERT(!"m_manager->Lock() failed");
1489 	}
1490 	m_manager->AddHandler(this);
1491 	m_manager->Unlock();
1492 
1493 	// fetch node details
1494 	BMediaRoster* r = BMediaRoster::Roster();
1495 	status_t err = r->GetLiveNodeInfo(
1496 		node,
1497 		&m_info);
1498 
1499 	if(err < B_OK) {
1500 		PRINT((
1501 			"!!! NodeRef(): BMediaRoster::GetLiveNodeInfo(%ld) failed:\n"
1502 			"    %s\n",
1503 			node.node,
1504 			strerror(err)));
1505 		// at least store node info
1506 		m_info.node = node;
1507 	}
1508 
1509 	// name self after node
1510 	SetName(m_info.name);
1511 
1512 	// init Media Roster connection [e.moon 11oct99]
1513 	if(!(m_flags & NO_ROSTER_WATCH)) {
1514 		r->StartWatching(
1515 			BMessenger(this),
1516 			m_info.node,
1517 			B_MEDIA_NODE_STOPPED);
1518 		m_watching = true;
1519 	}
1520 }
1521 // -------------------------------------------------------- //
1522 // *** endpoint-fixing operations (no lock required)
1523 // -------------------------------------------------------- //
1524 
1525 template <class T>
1526 class fixEndpointFn : public unary_function<T&, void> {
1527 	const media_node&		node;
1528 public:
1529 	fixEndpointFn(const media_node& _n) : node(_n) {}
1530 	void operator()(T& endpoint) {
1531 //		PRINT((
1532 //			"fixEndpointFn(): endpoint '%s', node ID %ld\n",
1533 //				endpoint.name, endpoint.node.node));
1534 		if(endpoint.node != node) {
1535 			PRINT((
1536 				"  fixing '%s'\n", endpoint.name));
1537 			endpoint.node = node;
1538 		}
1539 	}
1540 };
1541 
1542 // 'fix' (fill in node if needed) sets of inputs/outputs
1543 void NodeRef::_fixInputs(
1544 	media_input*									inputs,
1545 	int32													count) const {
1546 
1547 	D_METHOD((
1548 		"NodeRef[%s]::fixInputs()\n", m_info.name));
1549 
1550 	for_each(
1551 		inputs,
1552 		inputs+count,
1553 		fixEndpointFn<media_input>(node()));
1554 }
1555 
1556 void NodeRef::_fixInputs(
1557 	vector<media_input>&			inputs) const {
1558 
1559 	D_METHOD((
1560 		"NodeRef[%s]::fixInputs()\n", m_info.name));
1561 
1562 	for_each(
1563 		inputs.begin(),
1564 		inputs.end(),
1565 		fixEndpointFn<media_input>(node()));
1566 }
1567 
1568 void NodeRef::_fixOutputs(
1569 	media_output*									outputs,
1570 	int32													count) const {
1571 
1572 	D_METHOD((
1573 		"NodeRef[%s]::fixOutputs()\n", m_info.name));
1574 
1575 	for_each(
1576 		outputs,
1577 		outputs+count,
1578 		fixEndpointFn<media_output>(node()));
1579 }
1580 
1581 void NodeRef::_fixOutputs(
1582 	vector<media_output>&		outputs) const {
1583 
1584 	D_METHOD((
1585 		"NodeRef[%s]::fixOutputs()\n", m_info.name));
1586 
1587 	for_each(
1588 		outputs.begin(),
1589 		outputs.end(),
1590 		fixEndpointFn<media_output>(node()));
1591 }
1592 
1593 // -------------------------------------------------------- //
1594 // *** internal/NodeManager operations (LOCK REQUIRED)
1595 // -------------------------------------------------------- //
1596 
1597 // call after instantiation to register the dormant_node_info
1598 // used to select this add-on node
1599 
1600 void NodeRef::_setAddonHint(
1601 	const dormant_node_info*				info,
1602 	const entry_ref*								file) {
1603 
1604 	assert_locked(this);
1605 
1606 	if(m_addonHint)
1607 		delete m_addonHint;
1608 
1609 	m_addonHint = new addon_hint(info, file);
1610 }
1611 
1612 // call to set a new group; if 0, the node must have no
1613 // connections
1614 void NodeRef::_setGroup(
1615 	NodeGroup*										group) {
1616 	assert_locked(this);
1617 
1618 	m_group = group;
1619 
1620 	if(!LockLooper()) {
1621 		ASSERT(!"LockLooper() failed.");
1622 	}
1623 	BMessage m(M_GROUP_CHANGED);
1624 	m.AddInt32("nodeID", (int32)m_info.node.node);
1625 	m.AddInt32("groupID", m_group ? (int32)m_group->id() : 0);
1626 	notify(&m);
1627 	UnlockLooper();
1628 }
1629 
1630 // *** NodeGroup API ***
1631 //     9aug99: moved from NodeGroup
1632 
1633 // initialize the given node's transport-state members
1634 // (this may be called from the transport thread or from
1635 //  an API-implementation method.)
1636 
1637 status_t NodeRef::_initTransportState() {
1638 	assert_locked(this);
1639 
1640 	D_METHOD((
1641 		"NodeRef('%s')::_initTransportState()\n",
1642 		name()));
1643 
1644 	// init transport state	for this node
1645 	m_prerolled = false;
1646 	m_tpStart = 0LL;
1647 	m_tpLastSeek = 0LL;
1648 	m_lastSeekPos = 0LL;
1649 
1650 	// +++++ init position reporting stuff here?
1651 
1652 	return B_OK;
1653 }
1654 
1655 status_t NodeRef::_setTimeSource(
1656 	media_node_id					timeSourceID) {
1657 	assert_locked(this);
1658 
1659 	D_METHOD((
1660 		"NodeRef('%s')::_setTimeSource(%ld)\n",
1661 		name(), timeSourceID));
1662 	status_t err;
1663 
1664 	// set time source
1665 	ASSERT(timeSourceID != media_node::null.node);
1666 	D_ROSTER(("# roster->SetTimeSourceFor()\n"));
1667 	err = m_manager->roster->SetTimeSourceFor(
1668 		id(), timeSourceID);
1669 
1670 	if(err < B_OK) {
1671 		PRINT((
1672 			"* NodeRef('%s')::_setTimeSource(%ld):\n"
1673 			"  SetTimeSourceFor() failed: %s\n",
1674 			name(), timeSourceID, strerror(err)));
1675 	}
1676 
1677 	return err;
1678 }
1679 
1680 status_t NodeRef::_setRunMode(
1681 	const uint32					runMode,
1682 	bigtime_t							delay) {
1683 	assert_locked(this);
1684 
1685 	D_METHOD((
1686 		"NodeRef('%s')::_setRunMode(%ld : %Ld)\n",
1687 		name(), runMode, delay));
1688 	status_t err;
1689 
1690 
1691 	BMediaNode::run_mode m =
1692 		// if group is in offline mode, so are all its nodes
1693 		(runMode == BMediaNode::B_OFFLINE) ?
1694 			(BMediaNode::run_mode)runMode :
1695 			// if non-0, the node's setting is used
1696 			(m_runMode > 0) ?
1697 				(BMediaNode::run_mode)m_runMode :
1698 				(BMediaNode::run_mode)runMode;
1699 	ASSERT(m > 0);
1700 
1701 	// +++++ additional producer run-mode delay support here?
1702 
1703 	if(
1704 		kind() & B_BUFFER_PRODUCER &&
1705 		runMode == BMediaNode::B_RECORDING) {
1706 
1707 		D_ROSTER(("# roster->SetProducerRunModeDelay()\n"));
1708 		err = m_manager->roster->SetProducerRunModeDelay(
1709 			node(), delay, m);
1710 		if(err < B_OK) {
1711 			PRINT((
1712 				"NodeRef('%s')::_setRunMode(): SetProducerRunModeDelay(%Ld) failed: %s\n",
1713 				name(), delay, strerror(err)));
1714 		}
1715 	} else {
1716 
1717 		D_ROSTER(("# roster->SetRunModeNode()\n"));
1718 		err = m_manager->roster->SetRunModeNode(
1719 			node(), m);
1720 		if(err < B_OK) {
1721 			PRINT((
1722 				"NodeRef('%s')::_setRunMode(): SetRunModeNode(%d) failed: %s\n",
1723 				name(), m, strerror(err)));
1724 		}
1725 	}
1726 
1727 	return err;
1728 }
1729 
1730 status_t NodeRef::_setRunModeAuto(
1731 	const uint32					runMode) {
1732 
1733 	if(
1734 		kind() && B_BUFFER_PRODUCER &&
1735 		runMode == BMediaNode::B_RECORDING) {
1736 
1737 		return _setRunMode(
1738 			runMode,
1739 			calculateRecordingModeDelay());
1740 
1741 	} else
1742 		return _setRunMode(runMode);
1743 }
1744 
1745 // seek and preroll the given node.
1746 // *** this method should not be called from the transport thread
1747 // (since preroll operations can block for a relatively long time.)
1748 //
1749 // returns B_NOT_ALLOWED if the node is running, or if its NO_PREROLL
1750 // flag is set; otherwise, returns B_OK on success or a Media Roster
1751 // error.
1752 
1753 status_t NodeRef::_preroll(
1754 	bigtime_t							position) {
1755 	assert_locked(this);
1756 
1757 	D_METHOD((
1758 		"NodeRef('%s')::_preroll(%Ld)\n",
1759 		name(), position));
1760 	status_t err;
1761 
1762 	// make sure the node can be and wants to be prerolled
1763 	if(m_running ||
1764 		m_flags & NO_PREROLL)
1765 		return B_NOT_ALLOWED;
1766 
1767 	if(!(m_flags & NO_SEEK)) {
1768 		// seek the node first
1769 		err = BMediaRoster::Roster()->SeekNode(
1770 			node(),
1771 			position,
1772 			0LL);
1773 
1774 		if(err < B_OK) {
1775 			PRINT((
1776 				"*** NodeRef('%s')::_preroll(%Ld): BMediaRoster::SeekNode():\n"
1777 				"    %s\n",
1778 				name(), position, strerror(err)));
1779 			return err;
1780 		}
1781 	}
1782 
1783 	// preroll the node (*** this blocks until the node's Preroll()
1784 	//                       implementation returns ***)
1785 
1786 	err = BMediaRoster::Roster()->PrerollNode(
1787 		node());
1788 
1789 	if(err < B_OK) {
1790 		PRINT((
1791 			"*** NodeRef('%s')::_preroll(%Ld): BMediaRoster::PrerollNode():\n"
1792 			"    %s\n",
1793 			name(), position, strerror(err)));
1794 		return err;
1795 	}
1796 
1797 	m_prerolled = true;
1798 	m_tpLastSeek = 0LL;
1799 	m_lastSeekPos = position;
1800 
1801 	return B_OK;
1802 }
1803 
1804 // seek the given node if possible
1805 // (this may be called from the transport thread or from
1806 //  an API-implementation method.)
1807 
1808 status_t NodeRef::_seek(
1809 	bigtime_t							position,
1810 	bigtime_t							when) {
1811 	assert_locked(this);
1812 
1813 	D_METHOD((
1814 		"NodeRef('%s')::_seek(to %Ld, at %Ld)\n",
1815 		name(), position, when));
1816 
1817 	if(m_flags & NO_SEEK)
1818 		// the node should not be seek'd
1819 		return B_OK;
1820 
1821 	if(m_prerolled && m_lastSeekPos == position)
1822 		// the node has already been advanced to the proper position
1823 		return B_OK;
1824 
1825 	// do it
1826 	status_t err = BMediaRoster::Roster()->SeekNode(
1827 		node(), position, when);
1828 
1829 	if(err < B_OK) {
1830 		PRINT((
1831 			"*** NodeRef('%s')::_seek(to %Ld, at %Ld): BMediaRoster::SeekNode():\n"
1832 			"    %s\n",
1833 			name(), position, when, strerror(err)));
1834 		return err;
1835 	}
1836 
1837 	// update node state
1838 	m_tpLastSeek = when;
1839 	m_lastSeekPos = position;
1840 
1841 	// node can't be considered prerolled after a seek
1842 	m_prerolled = false;
1843 
1844 	return B_OK;
1845 }
1846 
1847 // seek the given (stopped) node
1848 // (this may be called from the transport thread or from
1849 //  an API-implementation method.)
1850 
1851 status_t NodeRef::_seekStopped(
1852 	bigtime_t							position) {
1853 	assert_locked(this);
1854 
1855 	D_METHOD((
1856 		"NodeRef('%s')::_seekStopped(to %Ld)\n",
1857 		name(), position));
1858 
1859 	if(m_running)
1860 		return B_NOT_ALLOWED;
1861 
1862 	return _seek(position, 0LL);
1863 }
1864 
1865 
1866 // start the given node, if possible & necessary, at
1867 // the given time
1868 // (this may be called from the transport thread or from
1869 //  an API-implementation method.)
1870 
1871 status_t NodeRef::_start(
1872 	bigtime_t							when) {
1873 	assert_locked(this);
1874 
1875 	D_METHOD((
1876 		"NodeRef('%s')::_start(at %Ld)\n",
1877 		name(), when));
1878 
1879 	if(isRunning()) {
1880 		D_METHOD((
1881 			"  * node already running; aborting\n"));
1882 		return B_OK; // +++++ is this technically an error?
1883 	}
1884 
1885 	// +++++ is this proper?
1886 	ASSERT(m_group);
1887 	ASSERT(
1888 		m_group->m_transportState == NodeGroup::TRANSPORT_RUNNING ||
1889 		m_group->m_transportState == NodeGroup::TRANSPORT_STARTING);
1890 
1891 	if(m_flags & NO_START_STOP) {
1892 		D_METHOD((
1893 			"  * NO_START_STOP; aborting\n"));
1894 		return B_OK;
1895 	}
1896 
1897 	D_ROSTER(("# roster->StartNode(%ld)\n", id()));
1898 	status_t err = BMediaRoster::Roster()->StartNode(
1899 		node(),	when);
1900 
1901 	if(err < B_OK) {
1902 		PRINT((
1903 			"  * StartNode(%ld) failed: '%s'\n",
1904 			id(), strerror(err)));
1905 		return err;
1906 	}
1907 
1908 	// update state
1909 	m_running = true;
1910 	m_tpStart = when;
1911 
1912 	// fetch new node latency
1913 	_updateLatency();
1914 
1915 	// start position tracking thread if needed
1916 	m_positionReportsStarted = false;
1917 	if(m_positionReportsEnabled)
1918 		_startPositionThread();
1919 
1920 	return B_OK;
1921 }
1922 
1923 // stop the given node (which may or may not still be
1924 // a member of this group.)
1925 // (this may be called from the transport thread or from
1926 //  an API-implementation method.)
1927 
1928 status_t NodeRef::_stop() {
1929 	assert_locked(this);
1930 
1931 	D_METHOD((
1932 		"NodeRef('%s')::_stop()\n",
1933 		name()));
1934 
1935 	if(!isRunning())
1936 		return B_OK; // +++++ error?
1937 
1938 	if(m_flags & NO_START_STOP || m_flags & NO_STOP)
1939 		return B_OK;
1940 
1941 	D_ROSTER(("# roster->StopNode(%ld)\n", id()));
1942 	status_t err = BMediaRoster::Roster()->StopNode(
1943 		node(), 0, true);
1944 
1945 	if(err < B_OK)
1946 		return err;
1947 
1948 	// 9aug99: refuse further position notification
1949 	_stopPositionThread();
1950 
1951 	// clear node's state
1952 	m_running = false;
1953 	m_stopQueued = false; // asked for immediate stop [e.moon 11oct99]
1954 	return _initTransportState();
1955 }
1956 
1957 // roll the given node, if possible
1958 // (this may be called from the transport thread or from
1959 //  an API-implementation method.)
1960 status_t NodeRef::_roll(
1961 	bigtime_t							start,
1962 	bigtime_t							stop,
1963 	bigtime_t							position) {
1964 	assert_locked(this);
1965 
1966 	D_METHOD((
1967 		"NodeRef('%s')::_roll(%Ld to %Ld, from %Ld)\n",
1968 		name(), start, stop, position));
1969 	status_t err;
1970 
1971 	// roll only if the node can be started & stopped,
1972 	// AND if this NodeRef is watching the Media Roster.
1973 	if(
1974 		m_flags & NO_START_STOP ||
1975 		m_flags & NO_STOP ||
1976 		m_flags & NO_ROSTER_WATCH)
1977 		return B_NOT_ALLOWED;
1978 
1979 	if(isRunning())
1980 		return B_NOT_ALLOWED;
1981 
1982 	ASSERT(m_group);
1983 	ASSERT(
1984 		m_group->m_transportState == NodeGroup::TRANSPORT_RUNNING ||
1985 		m_group->m_transportState == NodeGroup::TRANSPORT_STARTING);
1986 
1987 	D_ROSTER(("# roster->RollNode(%ld)\n", id()));
1988 	if(m_flags & NO_SEEK)
1989 		err = BMediaRoster::Roster()->RollNode(
1990 			node(),	start, stop);
1991 	else
1992 		err = BMediaRoster::Roster()->RollNode(
1993 			node(),	start, stop, position);
1994 
1995 	if(err < B_OK) {
1996 		PRINT((
1997 			"NodeRef('%s')::_roll(%Ld to %Ld, from %Ld)\n"
1998 			"!!! BMediaRoster::RollNode(%ld) failed: '%s'\n",
1999 			name(), start, stop, position, id(), strerror(err)));
2000 		return err;
2001 	}
2002 
2003 	// update state
2004 	m_running = true;
2005 	m_stopQueued = true; // remember that node will stop on its own [e.moon 11oct99]
2006 	m_tpStart = start;
2007 
2008 	// fetch new node latency
2009 	_updateLatency();
2010 
2011 	// start position tracking thread if needed
2012 	m_positionReportsStarted = false;
2013 	if(m_positionReportsEnabled)
2014 		_startPositionThread();
2015 
2016 	return B_OK;
2017 }
2018 
2019 // [28sep99 e.moon]
2020 // refresh the node's current latency; if I reference
2021 // a B_RECORDING node, update its 'producer delay'.
2022 
2023 status_t NodeRef::_updateLatency() {
2024 	assert_locked(this);
2025 
2026 	// [11nov99 e.moon] don't bother if it's not a producer:
2027 	if(!(kind() & B_BUFFER_PRODUCER)) {
2028 		m_latency = 0LL;
2029 		return B_OK;
2030 	}
2031 
2032 	bigtime_t latency;
2033 	status_t err = BMediaRoster::Roster()->GetLatencyFor(
2034 		node(),
2035 		&latency);
2036 	if(err < B_OK) {
2037 		PRINT((
2038 			"* NodeRef('%s')::_updateLatency(): GetLatencyFor() failed:\n"
2039 			"  %s\n",
2040 			name(), strerror(err)));
2041 
2042 		return err;
2043 	}
2044 
2045 	// success
2046 	m_latency = latency;
2047 
2048 	// update run-mode & delay if necessary
2049 	if(
2050 		m_runMode == BMediaNode::B_RECORDING ||
2051 		(m_runMode == 0 && m_group && m_group->runMode() == BMediaNode::B_RECORDING))
2052 		_setRunModeAuto(BMediaNode::B_RECORDING);
2053 
2054 	return B_OK;
2055 }
2056 
2057 // Figure the earliest time at which the given node can be started.
2058 // Also calculates the position at which it should start from to
2059 // play in sync with other nodes in the group, if the transport is
2060 // running; if stopped, *outPosition will be set to the current
2061 // start position.
2062 // Pass the estimated amount of time needed to prepare the
2063 // node for playback (ie. preroll & a little fudge factor) in
2064 // startDelay.
2065 //
2066 // (this may be called from the transport thread or from
2067 //  an API-implementation method.)
2068 
2069 status_t NodeRef::_calcStartTime(
2070 	bigtime_t							startDelay,
2071 	bigtime_t*						outTime,
2072 	bigtime_t*						outPosition) {
2073 	assert_locked(this);
2074 
2075 	// +++++
2076 
2077 	return B_ERROR;
2078 }
2079 
2080 
2081 // -------------------------------------------------------- //
2082 // *** Position and cycle thread management *** (LOCK REQUIRED)
2083 // -------------------------------------------------------- //
2084 
2085 status_t NodeRef::_startPositionThread() {
2086 	assert_locked(this);
2087 	ASSERT(m_group);
2088 	status_t err;
2089 
2090 	if(!m_positionReportsEnabled)
2091 		return B_NOT_ALLOWED;
2092 
2093 	if(m_positionThread)
2094 		_stopPositionThread();
2095 
2096 	m_positionThread = new NodeSyncThread(
2097 		m_info.node,
2098 		new BMessenger(this));
2099 
2100 	// send an initial position report if necessary
2101 	if(!m_positionReportsStarted) {
2102 		m_positionReportsStarted = true;
2103 
2104 		err = _handlePositionUpdate(
2105 			m_tpStart,
2106 			m_lastSeekPos);
2107 
2108 		if(err < B_OK) {
2109 			PRINT((
2110 				"* NodeRef::_startPositionThread(): _handlePositionUpdate() failed:\n"
2111 				"  %s\\n",
2112 				strerror(err)));
2113 			return err;
2114 		}
2115 	}
2116 	else {
2117 
2118 		// figure when the last jump in position occurred
2119 		bigtime_t tpFrom = (m_tpLastSeek > m_tpStart) ? m_tpLastSeek : m_tpStart;
2120 
2121 		// figure the corresponding position
2122 		bigtime_t lastPosition = m_lastSeekPos;
2123 
2124 		// figure the next time for a position report
2125 		BTimeSource* ts = m_group->m_timeSourceObj;
2126 
2127 		bigtime_t tpTarget = ts->Now() + m_positionUpdatePeriod;
2128 		bigtime_t targetPosition = lastPosition + (tpTarget-tpFrom);
2129 
2130 		err = _schedulePositionUpdate(
2131 			tpTarget,
2132 			targetPosition);
2133 
2134 		if(err < B_OK) {
2135 			PRINT((
2136 				"* NodeRef::_createPositionThread(): _schedulePositionUpdate() failed:\n"
2137 				"  %s\\n",
2138 				strerror(err)));
2139 			return err;
2140 		}
2141 	}
2142 
2143 	return B_OK;
2144 }
2145 
2146 status_t NodeRef::_handlePositionUpdate(
2147 	bigtime_t							perfTime,
2148 	bigtime_t							position) {
2149 	assert_locked(this);
2150 	status_t err;
2151 
2152 	if(!m_running) {
2153 		PRINT((
2154 			"* NodeRef::_handlePositionUpdate(): not running.\n"));
2155 		return B_NOT_ALLOWED;
2156 	}
2157 
2158 	if(!m_positionReportsEnabled) {
2159 		PRINT((
2160 			"* NodeRef::_handlePositionUpdate(): position reports disabled.\n"));
2161 		return B_NOT_ALLOWED;
2162 	}
2163 
2164 	// store info
2165 	m_tpLastPositionUpdate = perfTime;
2166 	m_lastPosition = position;
2167 
2168 	// relay notification to all 'position listeners'
2169 	_notifyPosition(perfTime, position);
2170 
2171 	// schedule next update
2172 	err = _schedulePositionUpdate(
2173 		perfTime + m_positionUpdatePeriod,
2174 		position + m_positionUpdatePeriod);
2175 
2176 	if(err < B_OK) {
2177 		PRINT((
2178 			"* NodeRef::_handlePositionUpdate(): _schedulePositionUpdate() failed:\n"
2179 			"  %s\n",
2180 			strerror(err)));
2181 	}
2182 	return err;
2183 }
2184 
2185 status_t NodeRef::_schedulePositionUpdate(
2186 	bigtime_t							when,
2187 	bigtime_t							position) {
2188 	assert_locked(this);
2189 	status_t err;
2190 
2191 	if(!m_positionReportsEnabled)
2192 		return B_NOT_ALLOWED;
2193 	ASSERT(m_positionThread);
2194 
2195 	if(m_cycle && m_group->_cycleValid()) {
2196 		if(position >= m_group->endPosition()) {
2197 			// snap to start of next cycle
2198 			when = m_group->_cycleBoundary();
2199 			position = m_group->startPosition();
2200 		}
2201 	}
2202 
2203 ////	position_sync_msg m = {
2204 ////		id(),
2205 ////		m_group->id(),
2206 ////		when,
2207 ////		position
2208 ////	};
2209 //
2210 ////	PRINT((
2211 ////		"NodeRef::_schedulePositionUpdate():\n"
2212 ////		"  when     = %Ld\n"
2213 ////		"  position = %Ld\n",
2214 ////		when, position));
2215 //
2216 //	m_positionSyncThread->setPosition(position);
2217 //
2218 //	if(first)
2219 //		err = m_positionSyncThread->go(when);
2220 //	else
2221 //		err = m_positionSyncThread->reschedule(when);
2222 
2223 	err = m_positionThread->sync(when, position, B_INFINITE_TIMEOUT);
2224 
2225 	if(err < B_OK) {
2226 		PRINT((
2227 			"! NodeRef::_schedulePositionUpdate(): m_positionThread->sync() failed:\n"
2228 			"  %s\n", strerror(err)));
2229 	}
2230 	return err;
2231 }
2232 
2233 status_t NodeRef::_stopPositionThread() {
2234 	assert_locked(this);
2235 
2236 	if(!m_positionThread)
2237 		return B_NOT_ALLOWED;
2238 
2239 	delete m_positionThread;
2240 	m_positionThread = 0;
2241 
2242 	return B_OK;
2243 }
2244 
2245 
2246 // Send a message to all position listeners
2247 status_t NodeRef::_notifyPosition(
2248 	bigtime_t							when,
2249 	bigtime_t							position) {
2250 	assert_locked(this);
2251 	status_t err = B_OK;
2252 
2253 	if(!m_positionReportsEnabled)
2254 		return B_NOT_ALLOWED;
2255 
2256 	BMessage message(M_POSITION);
2257 	message.AddInt32("nodeID", id());
2258 	message.AddInt64("when", when);
2259 	message.AddInt64("position", position);
2260 
2261 	m_positionInvoker.Invoke(&message);
2262 
2263 	return err;
2264 }
2265 
2266 // END -- NodeRef.cpp --
2267