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