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