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