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