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