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// NodeManager.cpp
33
34#include "NodeManager.h"
35
36#include "AddOnHost.h"
37#include "Connection.h"
38#include "NodeGroup.h"
39#include "NodeRef.h"
40
41#include <Debug.h>
42#include <MediaRoster.h>
43
44#include <algorithm>
45#include <cstring>
46#include <functional>
47#include <list>
48#include <set>
49
50#include "set_tools.h"
51#include "functional_tools.h"
52
53#include "node_manager_impl.h"
54
55using namespace std;
56
57__USE_CORTEX_NAMESPACE
58
59#define D_METHOD(x) //PRINT (x)
60#define D_MESSAGE(x) //PRINT (x)
61#define D_ROSTER(x) //PRINT (x)
62#define D_LOCK(x) //PRINT (x)
63
64// -------------------------------------------------------- //
65// messaging constants
66// -------------------------------------------------------- //
67
68// B_MEDIA_CONNECTION_BROKEN
69const char* const _connectionField = "__connection_id";
70const char* const _sourceNodeField = "__source_node_id";
71const char* const _destNodeField = "__destination_node_id";
72
73
74
75
76// -------------------------------------------------------- //
77// *** hooks
78// -------------------------------------------------------- //
79
80// [e.moon 7nov99] these hooks are called during processing of
81// BMediaRoster messages, before any notification is sent to
82// observers.  For example, if a B_MEDIA_NODES_CREATED message
83// were received describing 3 new nodes, nodeCreated() would be
84// called 3 times before the notification was sent.
85
86void NodeManager::nodeCreated(
87	NodeRef*											ref) {}
88
89void NodeManager::nodeDeleted(
90	const NodeRef*								ref) {}
91
92void NodeManager::connectionMade(
93	Connection*										connection) {}
94
95void NodeManager::connectionBroken(
96	const Connection*							connection) {}
97
98void NodeManager::connectionFailed(
99	const media_output &							output,
100	const media_input &								input,
101	const media_format &							format,
102	status_t error) {}
103
104// -------------------------------------------------------- //
105// helpers
106// -------------------------------------------------------- //
107
108class _for_each_state {
109public:
110	// marks nodes visited
111	set<media_node_id>		visited;
112};
113
114// [e.moon 28sep99] graph crawling
115// - does NOT apply operation to origin node.
116// - if inGroup is non-0, visits only nodes in that group.
117//
118// [e.moon 13oct99]: no longer supports locking (use _lockAllGroups() to
119// be sure all nodes are locked, if necessary.)
120
121template<class Op>
122void _do_for_each_connected(
123	NodeManager*											manager,
124	NodeRef*													origin,
125	NodeGroup*												inGroup,
126	bool															recurse,
127	Op																operation,
128	_for_each_state*									state) {
129
130//	PRINT(("### _do_for_each_connected()\n"));
131
132	ASSERT(manager->IsLocked());
133
134	ASSERT(origin);
135	ASSERT(state);
136	status_t err;
137
138	if(state->visited.find(origin->id()) != state->visited.end()) {
139//		PRINT(("### already visited\n"));
140		// already visited
141		return;
142	}
143
144	// used to walk connections
145	vector<Connection>		connections;
146
147	// mark visited
148	state->visited.insert(origin->id());
149
150	// walk input connections
151	origin->getInputConnections(connections);
152	for(uint32 n = 0; n < connections.size(); ++n) {
153
154		if(!connections[n].isValid())
155			continue;
156
157//		PRINT(("# source: %ld\n", connections[n].sourceNode()));
158
159		NodeRef* targetRef;
160		err = manager->getNodeRef(
161			connections[n].sourceNode(),
162			&targetRef);
163		ASSERT(err == B_OK);
164		ASSERT(targetRef);
165
166		if(inGroup && targetRef->group() != inGroup) {
167//			PRINT(("# .group mismatch\n"));
168			// don't need to visit
169			return;
170		}
171
172		// invoke operation
173//		if(lockRef)
174//			targetRef->lock();
175		operation(targetRef);
176//		if(lockRef)
177//			targetRef->unlock();
178
179		// recurse?
180		if(recurse)
181			_do_for_each_connected(
182				manager,
183				targetRef,
184				inGroup,
185				true,
186				operation,
187				state);
188	}
189
190	// walk output connections
191	connections.clear();
192	origin->getOutputConnections(connections);
193	for(uint32 n = 0; n < connections.size(); ++n) {
194//		PRINT(("# dest: %ld\n", connections[n].destinationNode()));
195
196		if(!connections[n].isValid())
197			continue;
198
199		NodeRef* targetRef;
200		err = manager->getNodeRef(
201			connections[n].destinationNode(),
202			&targetRef);
203		ASSERT(err == B_OK);
204		ASSERT(targetRef);
205
206		if(inGroup && targetRef->group() != inGroup) {
207//			PRINT(("# .group mismatch\n"));
208			// don't need to visit
209			return;
210		}
211
212		// invoke operation
213//		if(lockRef)
214//			targetRef->lock();
215		operation(targetRef);
216//		if(lockRef)
217//			targetRef->unlock();
218
219		// recurse?
220		if(recurse)
221			_do_for_each_connected(
222				manager,
223				targetRef,
224				inGroup,
225				true,
226				operation,
227				state);
228	}
229}
230
231// dtor helpers
232inline void NodeManager::_clearGroup(
233	NodeGroup*										group) {
234	Autolock _l(group);
235	D_METHOD((
236		"NodeManager::_clearGroup()\n"));
237
238	// stop group before clearing [10aug99]
239	group->_stop();
240
241	int32 n;
242	while((n = group->countNodes()) > 0) {
243		group->removeNode(n-1);
244	}
245
246//	// [e.moon 7nov99] release the group
247//	status_t err = remove_observer(this, group);
248//	if(err < B_OK) {
249//		// spew diagnostics
250//		PRINT((
251//			"!!! NodeManager::_clearGroup(): remove_observer(group %ld):\n"
252//			"    %s\n",
253//			group->id(),
254//			strerror(err)));
255//	}
256}
257
258inline void NodeManager::_freeConnection(
259	Connection*										connection) {
260	ASSERT(connection);
261
262	D_METHOD((
263		"NodeManager::_freeConnection(%ld)\n", connection->id()));
264	status_t err;
265
266	// break if internal & still valid
267	if(
268		connection->isValid() &&
269		connection->flags() & Connection::INTERNAL &&
270		!(connection->flags() & Connection::LOCKED)) {
271
272		D_METHOD((
273			"! breaking connection:\n"
274			"  source node:  %ld\n"
275			"  source id:    %ld\n"
276			"  source port:  %ld\n"
277			"  dest node:    %ld\n"
278			"  dest id:      %ld\n"
279			"  dest port:    %ld\n",
280			connection->sourceNode(),
281			connection->source().id, connection->source().port,
282			connection->destinationNode(),
283			connection->destination().id, connection->destination().port));
284
285		// do it
286		D_ROSTER(("# roster->Disconnect()\n"));
287		err = roster->Disconnect(
288			connection->sourceNode(),
289			connection->source(),
290			connection->destinationNode(),
291			connection->destination());
292
293		if(err < B_OK) {
294			D_METHOD((
295				"!!! BMediaRoster::Disconnect('%s' -> '%s') failed:\n"
296				"    %s\n",
297				connection->outputName(), connection->inputName(),
298				strerror(err)));
299		}
300	}
301
302	// delete
303	delete connection;
304}
305
306// -------------------------------------------------------- //
307// *** ctor/dtor
308// -------------------------------------------------------- //
309
310
311NodeManager::~NodeManager() {
312	D_METHOD((
313		"~NodeManager()\n"));
314	ASSERT(IsLocked());
315
316	// make a list of nodes to be released
317	list<NodeRef*> deadNodes;
318
319	for(node_ref_map::const_iterator it = m_nodeRefMap.begin();
320		it != m_nodeRefMap.end(); ++it) {
321		deadNodes.push_back((*it).second);
322	}
323
324	// ungroup all nodes
325
326// [e.moon 13oct99] making PPC compiler happy
327//	for_each(
328//		m_nodeGroupSet.begin(),
329//		m_nodeGroupSet.end(),
330//		bound_method(
331//			*this,
332//			&NodeManager::_clearGroup
333//		)
334//	);
335	for(node_group_set::iterator it = m_nodeGroupSet.begin();
336		it != m_nodeGroupSet.end(); ++it) {
337		_clearGroup(*it);
338	}
339
340	// delete groups
341	ptr_set_delete(
342		m_nodeGroupSet.begin(),
343		m_nodeGroupSet.end());
344	m_nodeGroupSet.clear();
345
346
347	// deallocate all connections; disconnect internal nodes
348// [e.moon 13oct99] making PPC compiler happy
349//	for_each(
350//		m_conSourceMap.begin(),
351//		m_conSourceMap.end(),
352//		unary_map_function(
353//			m_conSourceMap,
354//			bound_method(
355//				*this,
356//				&NodeManager::_freeConnection
357//			)
358//		)
359//	);
360	for(con_map::iterator it = m_conSourceMap.begin();
361		it != m_conSourceMap.end(); ++it) {
362		_freeConnection((*it).second);
363	}
364	m_conSourceMap.clear();
365	m_conDestinationMap.clear();
366
367	// release all nodes
368	for(list<NodeRef*>::const_iterator it = deadNodes.begin();
369		it != deadNodes.end(); ++it) {
370		(*it)->release();
371	}
372
373	if(m_nodeRefMap.size()) {
374		// +++++ nodes will only remain if they have observers; cope!
375		PRINT(("*** %ld nodes remaining!\n", m_nodeRefMap.size()));
376
377		deadNodes.clear();
378		for(node_ref_map::const_iterator it = m_nodeRefMap.begin();
379			it != m_nodeRefMap.end(); ++it)
380				deadNodes.push_back((*it).second);
381
382		ptr_set_delete(
383			deadNodes.begin(),
384			deadNodes.end());
385	}
386
387//	for_each(
388//		m_nodeRefMap.begin(),
389//		m_nodeRefMap.end(),
390//		unary_map_function(
391//			m_nodeRefMap,
392//			mem_fun(
393//				&NodeRef::release
394//			)
395//		)
396//	);
397//
398//	// delete all nodes
399//	ptr_map_delete(
400//		m_nodeRefMap.begin(),
401//		m_nodeRefMap.end());
402//
403//
404//	PRINT((
405//		"~NodeManager() done\n"));
406//
407}
408
409const char* const			NodeManager::s_defaultGroupPrefix = "No Name";
410const char* const			NodeManager::s_timeSourceGroup = "Time Sources";
411const char* const			NodeManager::s_audioInputGroup = "System Audio Input";
412const char* const			NodeManager::s_videoInputGroup = "System Video Input";
413const char* const			NodeManager::s_audioMixerGroup = "System Audio Mixer";
414const char* const			NodeManager::s_videoOutputGroup = "System Video Output";
415
416NodeManager::NodeManager(
417	bool													useAddOnHost) :
418
419	ObservableLooper("NodeManager"),
420	roster(BMediaRoster::Roster()),
421	m_audioInputNode(0),
422	m_videoInputNode(0),
423	m_audioMixerNode(0),
424	m_audioOutputNode(0),
425	m_videoOutputNode(0),
426	m_nextConID(1),
427	m_existingNodesInit(false),
428	m_useAddOnHost(useAddOnHost) {
429
430	D_METHOD((
431		"NodeManager()\n"));
432
433	ASSERT(roster);
434
435	// create refs for common nodes
436	_initCommonNodes();
437
438	// start the looper
439	Run();
440
441	// initialize connection to the media roster
442	D_ROSTER(("# roster->StartWatching(%p)\n", this));
443	roster->StartWatching(BMessenger(this));
444}
445
446// -------------------------------------------------------- //
447// *** operations
448// -------------------------------------------------------- //
449
450// * ACCESS
451
452// fetches NodeRef corresponding to a given ID; returns
453// B_BAD_VALUE if no matching entry was found (and writes
454// a 0 into the provided pointer.)
455
456status_t NodeManager::getNodeRef(
457	media_node_id									id,
458	NodeRef**											outRef) const {
459	Autolock _l(this);
460
461	D_METHOD((
462		"NodeManager::getNodeRef(%ld)\n", id));
463
464	node_ref_map::const_iterator it = m_nodeRefMap.find(id);
465	if(it == m_nodeRefMap.end()) {
466		*outRef = 0;
467		return B_BAD_VALUE;
468	}
469
470	*outRef = (*it).second;
471	return B_OK;
472}
473
474// [13aug99]
475// fetches Connection corresponding to a given source/destination
476// on a given node.  Returns an invalid connection and B_BAD_VALUE
477// if no matching connection was found.
478
479status_t NodeManager::findConnection(
480	media_node_id									node,
481	const media_source&						source,
482	Connection*										outConnection) const {
483	Autolock _l(this);
484
485	D_METHOD((
486		"NodeManager::findConnection()\n"));
487	ASSERT(source != media_source::null);
488
489	con_map::const_iterator it = m_conSourceMap.lower_bound(node);
490	con_map::const_iterator itEnd = m_conSourceMap.upper_bound(node);
491	for(; it != itEnd; ++it)
492		if((*it).second->source() == source) {
493			// copy connection
494			*outConnection = *((*it).second);
495			return B_OK;
496		}
497
498	*outConnection = Connection();
499	return B_BAD_VALUE;
500}
501
502status_t NodeManager::findConnection(
503	media_node_id									node,
504	const media_destination&			destination,
505	Connection*										outConnection) const {
506	Autolock _l(this);
507
508	D_METHOD((
509		"NodeManager::findConnection()\n"));
510	ASSERT(destination != media_destination::null);
511
512	con_map::const_iterator it = m_conDestinationMap.lower_bound(node);
513	con_map::const_iterator itEnd = m_conDestinationMap.upper_bound(node);
514	for(; it != itEnd; ++it)
515		if((*it).second->destination() == destination) {
516			// copy connection
517			*outConnection = *((*it).second);
518			return B_OK;
519		}
520
521	*outConnection = Connection();
522	return B_BAD_VALUE;
523}
524
525// [e.moon 28sep99]
526// fetches a Connection matching the given source and destination
527// nodes.  Returns an invalid connection and B_BAD_VALUE if
528// no matching connection was found
529
530status_t NodeManager::findConnection(
531	media_node_id									sourceNode,
532	media_node_id									destinationNode,
533	Connection*										outConnection) const {
534	Autolock _l(this);
535
536	D_METHOD((
537		"NodeManager::findConnection(source %ld, dest %ld)\n", sourceNode, destinationNode));
538
539	con_map::const_iterator it = m_conSourceMap.lower_bound(sourceNode);
540	con_map::const_iterator itEnd = m_conSourceMap.upper_bound(sourceNode);
541	for(; it != itEnd; ++it) {
542		if((*it).second->destinationNode() == destinationNode) {
543			*outConnection = *((*it).second);
544			return B_OK;
545		}
546	}
547
548	*outConnection = Connection();
549	return B_BAD_VALUE;
550}
551
552// [e.moon 28sep99]
553// tries to find a route from 'nodeA' to 'nodeB'; returns
554// true if one exists, false if not.
555
556class NodeManager::_find_route_state {
557public:
558	set<media_node_id>						visited;
559};
560
561bool NodeManager::findRoute(
562	media_node_id									nodeA,
563	media_node_id									nodeB) {
564	Autolock _l(this);
565
566	D_METHOD((
567		"NodeManager::findRoute(%ld, %ld)\n", nodeA, nodeB));
568	status_t err;
569
570	NodeRef* ref;
571	err = getNodeRef(nodeA, &ref);
572	if(err < B_OK) {
573		PRINT((
574			"!!! NodeManager::findRoute(%" B_PRId32 ", %" B_PRId32
575			"): no ref for node %" B_PRId32 "\n", nodeA, nodeB, nodeA));
576		return false;
577	}
578
579	_find_route_state st;
580	return _find_route_recurse(ref, nodeB, &st);
581}
582
583// implementation of above
584
585bool NodeManager::_find_route_recurse(
586	NodeRef*													origin,
587	media_node_id											target,
588	_find_route_state*								state) {
589
590	ASSERT(IsLocked());
591	ASSERT(origin);
592	ASSERT(state);
593	status_t err;
594
595	// node already visited?
596	if(state->visited.find(origin->id()) != state->visited.end()) {
597		return false;
598	}
599
600	// mark node visited
601	state->visited.insert(origin->id());
602
603	vector<Connection> connections;
604
605	// walk input connections
606	origin->getInputConnections(connections);
607	for(uint32 n = 0; n < connections.size(); ++n) {
608
609		if(!connections[n].isValid())
610			continue;
611
612		// test against target
613		if(connections[n].sourceNode() == target)
614			return true; // SUCCESS
615
616		// recurse
617		NodeRef* ref;
618		err = getNodeRef(
619			connections[n].sourceNode(),
620			&ref);
621		ASSERT(err == B_OK);
622		ASSERT(ref);
623
624		if(_find_route_recurse(
625			ref,
626			target,
627			state))
628			return true; // SUCCESS
629	}
630
631	// walk output connections
632	connections.clear();
633	origin->getOutputConnections(connections);
634	for(uint32 n = 0; n < connections.size(); ++n) {
635
636		if(!connections[n].isValid())
637			continue;
638
639		// test against target
640		if(connections[n].destinationNode() == target)
641			return true; // SUCCESS
642
643		// recurse
644		NodeRef* ref;
645		err = getNodeRef(
646			connections[n].destinationNode(),
647			&ref);
648		ASSERT(err == B_OK);
649		ASSERT(ref);
650
651		if(_find_route_recurse(
652			ref,
653			target,
654			state))
655			return true; // SUCCESS
656	}
657
658	return false; // FAILED
659}
660
661
662
663// fetches Connection corresponding to a given source or
664// destination; Returns an invalid connection and B_BAD_VALUE if
665// none found.
666// * Note: this is the slowest possible way to look up a
667//   connection.  Since the low-level source/destination
668//   structures don't include a node ID, and a destination
669//   port can differ from its node's control port, a linear
670//   search of all known connections is performed.  Only
671//   use these methods if you have no clue what node the
672//   connection corresponds to.
673
674status_t NodeManager::findConnection(
675	const media_source&						source,
676	Connection*										outConnection) const {
677	Autolock _l(this);
678
679	D_METHOD((
680		"NodeManager::findConnection()\n"));
681	ASSERT(source != media_source::null);
682
683	for(con_map::const_iterator it = m_conSourceMap.begin();
684		it != m_conSourceMap.end(); ++it) {
685		if((*it).second->source() == source) {
686			// copy connection
687			*outConnection = *((*it).second);
688			return B_OK;
689		}
690	}
691
692	*outConnection = Connection();
693	return B_BAD_VALUE;
694}
695
696status_t NodeManager::findConnection(
697	const media_destination&			destination,
698	Connection*										outConnection) const {
699	Autolock _l(this);
700
701	D_METHOD((
702		"NodeManager::findConnection()\n"));
703	ASSERT(destination != media_destination::null);
704
705	for(con_map::const_iterator it = m_conDestinationMap.begin();
706		it != m_conDestinationMap.end(); ++it) {
707		if((*it).second->destination() == destination) {
708			// copy connection
709			*outConnection = *((*it).second);
710			return B_OK;
711		}
712	}
713
714	*outConnection = Connection();
715	return B_BAD_VALUE;
716}
717
718
719// fetch NodeRefs for system nodes (if a particular node doesn't
720// exist, these methods return 0)
721
722NodeRef* NodeManager::audioInputNode() const {
723	Autolock _l(this);
724	return m_audioInputNode;
725}
726NodeRef* NodeManager::videoInputNode() const {
727	Autolock _l(this);
728	return m_videoInputNode;
729}
730NodeRef* NodeManager::audioMixerNode() const {
731	Autolock _l(this);
732	return m_audioMixerNode;
733}
734NodeRef* NodeManager::audioOutputNode() const {
735	Autolock _l(this);
736	return m_audioOutputNode;
737}
738NodeRef* NodeManager::videoOutputNode() const {
739	Autolock _l(this);
740	return m_videoOutputNode;
741}
742
743// fetch groups by index
744// - you can write-lock the manager during sets of calls to these methods;
745//   this ensures that the group set won't change.  The methods do lock
746//   the group internally, so locking isn't explicitly required.
747
748uint32 NodeManager::countGroups() const {
749	Autolock _l(this);
750	D_METHOD((
751		"NodeManager::countGroups()\n"));
752
753	return m_nodeGroupSet.size();
754}
755
756NodeGroup* NodeManager::groupAt(
757	uint32												index) const {
758	Autolock _l(this);
759	D_METHOD((
760		"NodeManager::groupAt()\n"));
761
762	return (index < m_nodeGroupSet.size()) ?
763		m_nodeGroupSet[index] :
764		0;
765}
766
767// look up a group by unique ID; returns B_BAD_VALUE if no
768// matching group was found
769
770class match_group_by_id :
771	public binary_function<const NodeGroup*, uint32, bool> {
772public:
773	bool operator()(const NodeGroup* group, uint32 id) const {
774		return group->id() == id;
775	}
776};
777
778status_t NodeManager::findGroup(
779	uint32												id,
780	NodeGroup**										outGroup) const {
781	Autolock _l(this);
782	D_METHOD((
783		"NodeManager::findGroup(id)\n"));
784
785	node_group_set::const_iterator it =
786		find_if(
787			m_nodeGroupSet.begin(),
788			m_nodeGroupSet.end(),
789			bind2nd(match_group_by_id(), id)
790		);
791
792	if(it == m_nodeGroupSet.end()) {
793		*outGroup = 0;
794		return B_BAD_VALUE;
795	}
796
797	*outGroup = *it;
798	return B_OK;
799}
800
801// look up a group by name; returns B_NAME_NOT_FOUND if
802// no group matching the name was found.
803
804class match_group_by_name :
805	public binary_function<const NodeGroup*, const char*, bool> {
806public:
807	bool operator()(const NodeGroup* group, const char* name) const {
808		return !strcmp(group->name(), name);
809	}
810};
811
812status_t NodeManager::findGroup(
813	const char*										name,
814	NodeGroup**										outGroup) const {
815	Autolock _l(this);
816	D_METHOD((
817		"NodeManager::findGroup(name)\n"));
818
819	node_group_set::const_iterator it =
820		find_if(
821			m_nodeGroupSet.begin(),
822			m_nodeGroupSet.end(),
823			bind2nd(match_group_by_name(), name)
824		);
825
826	if(it == m_nodeGroupSet.end()) {
827		*outGroup = 0;
828		return B_BAD_VALUE;
829	}
830
831	*outGroup = *it;
832	return B_OK;
833}
834
835// merge the given source group to the given destination;
836// empties and releases the source group
837
838status_t NodeManager::mergeGroups(
839	NodeGroup*										sourceGroup,
840	NodeGroup*										destinationGroup) {
841	Autolock _l(this);
842	D_METHOD((
843		"NodeManager::mergeGroups(name)\n"));
844
845	status_t err;
846
847	// [5feb00 c.lenz] already merged
848	if(sourceGroup->id() == destinationGroup->id())
849		return B_OK;
850
851	if(sourceGroup->isReleased() || destinationGroup->isReleased())
852		return B_NOT_ALLOWED;
853
854	for(uint32 n = sourceGroup->countNodes(); n; --n) {
855		NodeRef* node = sourceGroup->nodeAt(n-1);
856		ASSERT(node);
857		err = sourceGroup->removeNode(n-1);
858		ASSERT(err == B_OK);
859		err = destinationGroup->addNode(node);
860		ASSERT(err == B_OK);
861	}
862
863	// [7nov99 e.moon] delete the source group
864	_removeGroup(sourceGroup);
865	sourceGroup->release();
866
867	return B_OK;
868}
869
870// [e.moon 28sep99]
871// split group: given two nodes currently in the same group
872// that are not connected (directly OR indirectly),
873// this method removes outsideNode, and all nodes connected
874// to outsideNode, from the common group.  These nodes are
875// then added to a new group, returned in 'outGroup'.  The
876// new group has " split" appended to the end of the original
877// group's name.
878//
879// Returns B_NOT_ALLOWED if any of the above conditions aren't
880// met (ie. the nodes are in different groups or an indirect
881// route exists from one to the other), or B_OK if the group
882// was split successfully.
883
884
885class _changeNodeGroupFn :
886	public	unary_function<NodeRef*, void> {
887public:
888	NodeGroup*										newGroup;
889
890	_changeNodeGroupFn(
891		NodeGroup*									_newGroup) : newGroup(_newGroup) {
892		ASSERT(newGroup);
893	}
894
895	void operator()(
896		NodeRef*										node) {
897
898		PRINT((
899			"_changeNodeGroupFn(): '%s'\n", node->name()));
900
901		status_t err;
902		NodeGroup* oldGroup = node->group();
903		if(oldGroup) {
904			err = oldGroup->removeNode(node);
905			ASSERT(err == B_OK);
906		}
907
908		err = newGroup->addNode(node);
909		ASSERT(err == B_OK);
910	}
911};
912
913status_t NodeManager::splitGroup(
914	NodeRef*											insideNode,
915	NodeRef*											outsideNode,
916	NodeGroup**										outGroup) {
917
918	ASSERT(insideNode);
919	ASSERT(outsideNode);
920
921	Autolock _l(this);
922
923	// ensure that no route exists from insideNode to outsideNode
924	if(findRoute(insideNode->id(), outsideNode->id())) {
925		PRINT((
926			"!!! NodeManager::splitGroup(): route exists from %" B_PRId32
927			" to %" B_PRId32 "\n", insideNode->id(), outsideNode->id()));
928		return B_NOT_ALLOWED;
929	}
930
931	// make sure the nodes share a common group
932	NodeGroup* oldGroup = insideNode->group();
933	if(!oldGroup) {
934		PRINT(("!!! NodeManager::splitGroup(): invalid group\n"));
935		return B_NOT_ALLOWED;
936	}
937	if(oldGroup != outsideNode->group()) {
938		PRINT((
939			"!!! NodeManager::splitGroup(): mismatched groups for %" B_PRId32
940			" and %" B_PRId32 "\n", insideNode->id(), outsideNode->id()));
941		return B_NOT_ALLOWED;
942	}
943
944	Autolock _l_old_group(oldGroup);
945
946	// create the new group
947	BString nameBuffer = oldGroup->name();
948	nameBuffer << " split";
949
950	NodeGroup* newGroup = createGroup(
951		nameBuffer.String(),
952		oldGroup->runMode());
953	*outGroup = newGroup;
954
955	// move nodes connected to outsideNode from old to new group
956	_changeNodeGroupFn fn(newGroup);
957	fn(outsideNode);
958
959	_for_each_state st;
960	_do_for_each_connected(
961		this,
962		outsideNode,
963		oldGroup,
964		true,
965		fn,
966		&st);
967
968	// [e.moon 1dec99] a single-node group takes that node's name
969	if(newGroup->countNodes() == 1)
970		newGroup->setName(newGroup->nodeAt(0)->name());
971
972	if(oldGroup->countNodes() == 1)
973		oldGroup->setName(oldGroup->nodeAt(0)->name());
974
975	return B_OK;
976}
977
978
979// * INSTANTIATION & CONNECTION
980//   Use these calls rather than the associated BMediaRoster()
981//   methods to assure that the nodes and connections you set up
982//   can be properly serialized & reconstituted.
983
984// basic BMediaRoster::InstantiateDormantNode() wrapper
985
986status_t NodeManager::instantiate(
987	const dormant_node_info&			info,
988	NodeRef**											outRef,
989	bigtime_t											timeout,
990	uint32												nodeFlags) {
991
992	Autolock _l(this);
993	status_t err;
994	D_METHOD((
995		"NodeManager::instantiate()\n"));
996
997	// * instantiate
998
999	media_node node;
1000
1001	if(m_useAddOnHost) {
1002		err = AddOnHost::InstantiateDormantNode(
1003			info, &node, timeout);
1004
1005		if(err < B_OK) {
1006			node = media_node::null;
1007
1008			// attempt to relaunch
1009			BMessenger mess;
1010			err = AddOnHost::Launch(&mess);
1011			if(err < B_OK) {
1012				PRINT((
1013					"!!! NodeManager::instantiate(): giving up on AddOnHost\n"));
1014
1015				m_useAddOnHost = false;
1016			}
1017			else {
1018				err = AddOnHost::InstantiateDormantNode(
1019					info, &node, timeout);
1020			}
1021		}
1022	}
1023
1024	if(!m_useAddOnHost || node == media_node::null) {
1025		D_ROSTER((
1026			"# roster->InstantiateDormantNode()\n"));
1027		err = roster->InstantiateDormantNode(info, &node);
1028	}
1029
1030	if(err < B_OK) {
1031		*outRef = 0;
1032		return err;
1033	}
1034
1035	if(node == media_node::null) {
1036		// [e.moon 23oct99] +++++
1037		// instantiating a soundcard input/output (emu10k or sonic_vibes)
1038		// produces a node ID of -1 (R4.5.2 x86)
1039		//
1040		PRINT((
1041			"! InstantiateDormantNode(): invalid media node\n"));
1042
1043		// bail out
1044		*outRef = 0;
1045		return B_BAD_INDEX;
1046	}
1047
1048	// * create NodeRef
1049	NodeRef* ref = new NodeRef(
1050		node,
1051		this,
1052		nodeFlags, // | NodeRef::NO_ROSTER_WATCH, // +++++ e.moon 11oct99
1053		NodeRef::_INTERNAL);
1054
1055	ref->_setAddonHint(&info);
1056	_addRef(ref);
1057
1058	// * return it
1059	*outRef = ref;
1060	return B_OK;
1061}
1062
1063// SniffRef/Instantiate.../SetRefFor: a one-call interface
1064// to create a node capable of playing a given media file.
1065
1066status_t NodeManager::instantiate(
1067	const entry_ref&							file,
1068	uint64												requireNodeKinds,
1069	NodeRef**											outRef,
1070	bigtime_t											timeout,
1071	uint32												nodeFlags,
1072	bigtime_t*										outDuration) {
1073
1074	D_METHOD((
1075		"NodeManager::instantiate(ref)\n"));
1076
1077	// [no lock needed; calls the full form of instantiate()]
1078
1079	status_t err;
1080
1081	// * Find matching add-on
1082	dormant_node_info info;
1083
1084	D_ROSTER((
1085		"# roster->SniffRef()\n"));
1086	err = roster->SniffRef(
1087		file,
1088		requireNodeKinds,
1089		&info);
1090	if(err < B_OK) {
1091		*outRef = 0;
1092		return err;
1093	}
1094
1095	// * Instantiate
1096	err = instantiate(info, outRef, timeout, nodeFlags);
1097
1098	if(err < B_OK)
1099		return err;
1100
1101	ASSERT(*outRef);
1102
1103	// * Set file to play
1104	bigtime_t dur;
1105	D_ROSTER(("# roster->SetRefFor()\n"));
1106	err = roster->SetRefFor(
1107		(*outRef)->node(),
1108		file,
1109		false,
1110		&dur);
1111
1112	if(err < B_OK) {
1113		PRINT((
1114			"* SetRefFor() failed: %s\n", strerror(err)));
1115	}
1116	else if(outDuration)
1117		*outDuration = dur;
1118
1119	// * update info [e.moon 29sep99]
1120	Autolock _l(*outRef);
1121	(*outRef)->_setAddonHint(&info, &file);
1122
1123	return err;
1124}
1125
1126// use this method to reference nodes internal to your
1127// application.
1128
1129status_t NodeManager::reference(
1130	BMediaNode*										node,
1131	NodeRef**											outRef,
1132	uint32												nodeFlags) {
1133
1134	Autolock _l(this);
1135	D_METHOD((
1136		"NodeManager::reference()\n"));
1137
1138	// should this node be marked _NO_RELEASE?
1139	NodeRef* ref = new NodeRef(node->Node(), this, nodeFlags, 0);
1140	_addRef(ref);
1141
1142	*outRef = ref;
1143	return B_OK;
1144}
1145
1146// the most flexible form of connect(): set the template
1147// format as you would for BMediaRoster::Connect().
1148
1149status_t NodeManager::connect(
1150	const media_output&						output,
1151	const media_input&						input,
1152	const media_format&						templateFormat,
1153	Connection*										outConnection /*=0*/) {
1154
1155	Autolock _l(this);
1156	status_t err;
1157	D_METHOD((
1158		"NodeManager::connect()\n"));
1159
1160	// * Find (& create if necessary) NodeRefs
1161
1162	NodeRef* outputRef;
1163	if(getNodeRef(output.node.node, &outputRef) < B_OK)
1164		outputRef = _addRefFor(output.node, 0);
1165
1166	NodeRef* inputRef;
1167	if(getNodeRef(input.node.node, &inputRef) < B_OK)
1168		inputRef = _addRefFor(input.node, 0);
1169
1170	// * Connect the nodes
1171
1172	media_output finalOutput;
1173	media_input finalInput;
1174	media_format finalFormat = templateFormat;
1175
1176	D_ROSTER(("# roster->Connect()\n"));
1177	err = roster->Connect(
1178		output.source,
1179		input.destination,
1180		&finalFormat,
1181		&finalOutput,
1182		&finalInput);
1183
1184	if(err < B_OK) {
1185		if(outConnection)
1186			*outConnection = Connection();
1187		connectionFailed(output, input, templateFormat, err);
1188		return err;
1189	}
1190
1191	// * Create Connection instance; mark it INTERNAL
1192	//   to automatically remove it upon shutdown.
1193
1194	D_METHOD((
1195		"! creating connection:\n"
1196		"  source id:    %" B_PRId32 "\n"
1197		"  source port:  %" B_PRId32 "\n"
1198		"  dest id:      %" B_PRId32 "\n"
1199		"  dest port:    %" B_PRId32 "\n",
1200		finalOutput.source.id, finalOutput.source.port,
1201		finalInput.destination.id, finalInput.destination.port));
1202
1203	uint32 cflags = Connection::INTERNAL;
1204	if(outputRef->m_info.node.kind & B_FILE_INTERFACE) {
1205		// 3aug99:
1206		// workaround for bug 19990802-12798:
1207		//   connections involving a file-interface node aren't removed
1208		cflags |= Connection::LOCKED;
1209	}
1210
1211	Connection* con = new Connection(
1212		m_nextConID++,
1213		output.node,
1214		finalOutput.source,
1215		finalOutput.name,
1216		input.node,
1217		finalInput.destination,
1218		finalInput.name,
1219		finalFormat,
1220		cflags);
1221
1222	con->setOutputHint(
1223		output.name,
1224		output.format);
1225
1226	con->setInputHint(
1227		input.name,
1228		input.format);
1229
1230	con->setRequestedFormat(
1231		templateFormat);
1232
1233	_addConnection(con);
1234
1235	// [e.moon 10aug99]
1236	// fetch updated latencies;
1237	// [e.moon 28sep99]
1238	// scan all nodes connected directly OR indirectly to the
1239	// newly-connected nodes and update their latencies -- this includes
1240	// recalculating the node's 'producer delay' if in B_RECORDING mode.
1241
1242	_updateLatenciesFrom(inputRef, true);
1243
1244	// copy connection
1245	if(outConnection) {
1246		*outConnection = *con;
1247	}
1248	return B_OK;
1249}
1250
1251// format-guessing form of connect(): tries to find
1252// a common format between output & input before connection;
1253// returns B_MEDIA_BAD_FORMAT if no common format appears
1254// possible.
1255//
1256// NOTE: the specifics of the input and output formats are ignored;
1257//       this method only looks at the format type, and properly
1258//       handles wildcards at that level (B_MEDIA_NO_TYPE).
1259
1260status_t NodeManager::connect(
1261	const media_output&						output,
1262	const media_input&						input,
1263	Connection*										outConnection /*=0*/) {
1264
1265	D_METHOD((
1266		"NodeManager::connect(guess)\n"));
1267
1268	// [no lock needed; calls the full form of connect()]
1269
1270	// defer to the pickier endpoint
1271	media_format f;
1272
1273	if(output.format.type > B_MEDIA_UNKNOWN_TYPE) {
1274		f = output.format;
1275		if ((input.format.type > B_MEDIA_UNKNOWN_TYPE) &&
1276			(f.type != input.format.type)) {
1277			connectionFailed(output, input, f, B_MEDIA_BAD_FORMAT);
1278			return B_MEDIA_BAD_FORMAT;
1279		}
1280	}
1281	else if(input.format.type > B_MEDIA_UNKNOWN_TYPE) {
1282		f = input.format;
1283		// output node doesn't care
1284	}
1285	else {
1286		// about as non-picky as two nodes can possibly be
1287		f.type = B_MEDIA_UNKNOWN_TYPE;
1288	}
1289
1290	// +++++ ? revert to wildcard ?
1291
1292	// let the nodes try to work out a common format from here
1293	return connect(
1294		output,
1295		input,
1296		f,
1297		outConnection);
1298}
1299
1300// disconnects the connection represented by the provided
1301// Connection object.  if successful, returns B_OK.
1302
1303status_t NodeManager::disconnect(
1304	const Connection&								connection) {
1305
1306	Autolock _l(this);
1307	status_t err;
1308	D_METHOD((
1309		"NodeManager::disconnect()\n"));
1310
1311	// don't bother trying to disconnect an invalid connection
1312	if(!connection.isValid())
1313		return B_NOT_ALLOWED;
1314
1315	// make sure connection can be released
1316	if(connection.flags() & Connection::LOCKED) {
1317		PRINT((
1318			"NodeManager::disconnect(): connection locked:\n"
1319			"  %" B_PRId32 ":%s -> %" B_PRId32 ":%s\n",
1320			connection.sourceNode(),
1321			connection.outputName(),
1322			connection.destinationNode(),
1323			connection.inputName()));
1324		return B_NOT_ALLOWED;
1325	}
1326
1327	D_METHOD((
1328		"! breaking connection:\n"
1329		"  source node:  %" B_PRId32 "\n"
1330		"  source id:    %" B_PRId32 "\n"
1331		"  source port:  %" B_PRId32 "\n"
1332		"  dest node:    %" B_PRId32 "\n"
1333		"  dest id:      %" B_PRId32 "\n"
1334		"  dest port:    %" B_PRId32 "\n",
1335		connection.sourceNode(),
1336		connection.source().id, connection.source().port,
1337		connection.destinationNode(),
1338		connection.destination().id, connection.destination().port));
1339
1340	// do it
1341	D_ROSTER(("# roster->Disconnect()\n"));
1342	err = roster->Disconnect(
1343		connection.sourceNode(),
1344		connection.source(),
1345		connection.destinationNode(),
1346		connection.destination());
1347
1348	// mark disconnected
1349	if(err == B_OK) {
1350		con_map::iterator it = m_conSourceMap.lower_bound(connection.sourceNode());
1351		con_map::iterator itEnd = m_conSourceMap.upper_bound(connection.sourceNode());
1352		for(; it != itEnd; ++it)
1353			if((*it).second->id() == connection.id()) {
1354				(*it).second->m_disconnected = true;
1355				break;
1356			}
1357		ASSERT(it != itEnd);
1358
1359		// [e.moon 28sep99]
1360		// fetch updated latencies;
1361		// scan all nodes connected directly OR indirectly to the
1362		// newly-connected nodes and update their latencies -- this includes
1363		// recalculating the node's 'producer delay' if in B_RECORDING mode.
1364
1365		NodeRef* ref;
1366		if(getNodeRef(connection.sourceNode(), &ref) == B_OK)
1367			_updateLatenciesFrom(ref, true);
1368		if(getNodeRef(connection.destinationNode(), &ref) == B_OK)
1369			_updateLatenciesFrom(ref, true);
1370
1371	} else {
1372		// +++++ if this failed, somebody somewhere is mighty confused
1373		PRINT((
1374			"NodeManager::disconnect(): Disconnect() failed:\n  %s\n",
1375			strerror(err)));
1376	}
1377
1378	return err;
1379}
1380
1381// * GROUP CREATION
1382
1383NodeGroup* NodeManager::createGroup(
1384	const char*										name,
1385	BMediaNode::run_mode					runMode) {
1386
1387	Autolock _l(this);
1388	D_METHOD((
1389		"NodeManager::createGroup()\n"));
1390
1391	NodeGroup* g = new NodeGroup(name, this, runMode);
1392	_addGroup(g);
1393
1394	return g;
1395}
1396
1397// -------------------------------------------------------- //
1398// *** node/connection iteration
1399// *** MUST BE LOCKED for any of these calls
1400// -------------------------------------------------------- //
1401
1402// usage:
1403//   For the first call, pass 'cookie' a pointer to a void* set to 0.
1404//   Returns B_BAD_INDEX when the set of nodes has been exhausted (and
1405//   re-zeroes the cookie, cleaning up any unused memory.)
1406
1407status_t NodeManager::getNextRef(
1408	NodeRef**											ref,
1409	void**												cookie) {
1410	ASSERT(IsLocked());
1411	ASSERT(cookie);
1412
1413	if(!*cookie)
1414		*cookie = new node_ref_map::iterator(m_nodeRefMap.begin());
1415
1416	node_ref_map::iterator* pit = (node_ref_map::iterator*)*cookie;
1417
1418	// at end of set?
1419	if(*pit == m_nodeRefMap.end()) {
1420		delete pit;
1421		*cookie = 0;
1422		return B_BAD_INDEX;
1423	}
1424
1425	// return next entry
1426	*ref = (*(*pit)).second;
1427	++(*pit);
1428	return B_OK;
1429}
1430
1431// +++++ reworked 13sep99: dtors wouldn't have been called with 'delete *cookie'! +++++
1432void NodeManager::disposeRefCookie(
1433	void**												cookie) {
1434
1435	if(!cookie)
1436		return;
1437
1438	node_ref_map::iterator* it =
1439		reinterpret_cast<node_ref_map::iterator*>(*cookie);
1440	ASSERT(it);
1441	if(it)
1442		delete it;
1443}
1444
1445status_t NodeManager::getNextConnection(
1446	Connection*										connection,
1447	void**												cookie) {
1448	ASSERT(IsLocked());
1449	ASSERT(cookie);
1450
1451	if(!*cookie)
1452		*cookie = new con_map::iterator(m_conSourceMap.begin());
1453
1454	con_map::iterator* pit = (con_map::iterator*)*cookie;
1455
1456	// at end of set?
1457	if(*pit == m_conSourceMap.end()) {
1458		delete pit;
1459		*cookie = 0;
1460		return B_BAD_INDEX;
1461	}
1462
1463	// return next entry (ewww)
1464	*connection = *((*(*pit)).second);
1465	++(*pit);
1466	return B_OK;
1467}
1468
1469// +++++ reworked 13sep99: dtors wouldn't have been called with 'delete *cookie'! +++++
1470void NodeManager::disposeConnectionCookie(
1471	void**												cookie) {
1472
1473	if(!cookie)
1474		return;
1475
1476	con_map::iterator* it =
1477		reinterpret_cast<con_map::iterator*>(*cookie);
1478	ASSERT(it);
1479	if(it)
1480		delete it;
1481}
1482
1483
1484// -------------------------------------------------------- //
1485// *** BHandler impl
1486// -------------------------------------------------------- //
1487
1488// +++++ support all Media Roster messages! +++++
1489
1490// [e.moon 7nov99] implemented observer pattern for NodeGroup
1491void NodeManager::MessageReceived(BMessage* message) {
1492
1493	D_MESSAGE((
1494		"NodeManager::MessageReceived(): %c%c%c%c\n",
1495		 message->what >> 24,
1496		(message->what >> 16)	& 0xff,
1497		(message->what >> 8)	& 0xff,
1498		(message->what) 			& 0xff));
1499
1500	switch(message->what) {
1501
1502		// *** Media Roster messages ***
1503
1504		case B_MEDIA_NODE_CREATED:
1505			if(_handleNodesCreated(message) == B_OK)
1506				notify(message);
1507			break;
1508
1509		case B_MEDIA_NODE_DELETED:
1510			_handleNodesDeleted(message);
1511			notify(message);
1512			break;
1513
1514		case B_MEDIA_CONNECTION_MADE:
1515			_handleConnectionMade(message);
1516			notify(message);
1517			break;
1518
1519		case B_MEDIA_CONNECTION_BROKEN:
1520			_handleConnectionBroken(message); // augments message!
1521			notify(message);
1522			break;
1523
1524		case B_MEDIA_FORMAT_CHANGED:
1525			_handleFormatChanged(message);
1526			notify(message);
1527			break;
1528
1529		default:
1530			_inherited::MessageReceived(message);
1531			break;
1532	}
1533}
1534
1535// -------------------------------------------------------- //
1536// *** IObservable hooks
1537// -------------------------------------------------------- //
1538
1539void NodeManager::observerAdded(
1540	const BMessenger&				observer) {
1541
1542	BMessage m(M_OBSERVER_ADDED);
1543	m.AddMessenger("target", BMessenger(this));
1544	observer.SendMessage(&m);
1545}
1546
1547
1548void NodeManager::observerRemoved(
1549	const BMessenger&				observer) {
1550
1551	BMessage m(M_OBSERVER_REMOVED);
1552	m.AddMessenger("target", BMessenger(this));
1553	observer.SendMessage(&m);
1554}
1555
1556void NodeManager::notifyRelease() {
1557
1558	BMessage m(M_RELEASED);
1559	m.AddMessenger("target", BMessenger(this));
1560	notify(&m);
1561}
1562
1563void NodeManager::releaseComplete() {
1564	// tear down media roster connection
1565	D_ROSTER(("# roster->StopWatching()\n"));
1566	status_t err = roster->StopWatching(
1567		BMessenger(this));
1568	if(err < B_OK) {
1569		PRINT((
1570			"* roster->StopWatching() failed: %s\n", strerror(err)));
1571	}
1572}
1573
1574
1575// -------------------------------------------------------- //
1576// *** ILockable impl.
1577// -------------------------------------------------------- //
1578
1579bool NodeManager::lock(
1580	lock_t												type,
1581	bigtime_t											timeout) {
1582
1583	D_LOCK(("*** NodeManager::lock(): %ld\n", find_thread(0)));
1584
1585	status_t err = LockWithTimeout(timeout);
1586
1587
1588	D_LOCK(("*** NodeManager::lock() ACQUIRED: %ld\n", find_thread(0)));
1589
1590	return err == B_OK;
1591}
1592
1593bool NodeManager::unlock(
1594	lock_t												type) {
1595
1596	D_LOCK(("*** NodeManager::unlock(): %ld\n", find_thread(0)));
1597
1598	Unlock();
1599
1600	D_LOCK(("*** NodeManager::unlock() RELEASED: %ld\n", find_thread(0)));
1601
1602	return true;
1603}
1604
1605bool NodeManager::isLocked(
1606	lock_t												type) const {
1607
1608	return IsLocked();
1609}
1610
1611// -------------------------------------------------------- //
1612// *** internal operations (LOCK REQUIRED)
1613// -------------------------------------------------------- //
1614
1615void NodeManager::_initCommonNodes() {
1616
1617	ASSERT(IsLocked());
1618	status_t err;
1619	media_node node;
1620
1621	D_METHOD((
1622		"NodeManager::_initCommonNodes()\n"));
1623
1624	uint32 disableTransport =
1625		(NodeRef::NO_START_STOP | NodeRef::NO_SEEK | NodeRef::NO_PREROLL);
1626
1627	// video input
1628	D_ROSTER(("# roster->GetVideoInput()\n"));
1629	err = roster->GetVideoInput(&node);
1630	if(err == B_OK)
1631		m_videoInputNode = _addRefFor(
1632			node,
1633			_userFlagsForKind(node.kind),
1634			_implFlagsForKind(node.kind));
1635
1636	// video output
1637	D_ROSTER(("# roster->GetVideoOutput()\n"));
1638	err = roster->GetVideoOutput(&node);
1639	if(err == B_OK) {
1640		if(m_videoInputNode && node.node == m_videoInputNode->id()) {
1641			// input and output nodes identical
1642			// [e.moon 20dec99]
1643			m_videoOutputNode = m_videoInputNode;
1644		}
1645		else {
1646			m_videoOutputNode = _addRefFor(
1647				node,
1648				_userFlagsForKind(node.kind) & ~NodeRef::NO_START_STOP,
1649				_implFlagsForKind(node.kind));
1650		}
1651	}
1652
1653	// audio mixer
1654	D_ROSTER(("# roster->GetAudioMixer()\n"));
1655	err = roster->GetAudioMixer(&node);
1656	if(err == B_OK)
1657		m_audioMixerNode = _addRefFor(
1658			node,
1659			_userFlagsForKind(node.kind) | disableTransport,
1660			_implFlagsForKind(node.kind));
1661
1662	// audio input
1663	D_ROSTER(("# roster->GetAudioInput()\n"));
1664	err = roster->GetAudioInput(&node);
1665	if(err == B_OK)
1666		m_audioInputNode = _addRefFor(
1667			node,
1668			_userFlagsForKind(node.kind),
1669			_implFlagsForKind(node.kind));
1670
1671	// audio output
1672	D_ROSTER(("# roster->GetAudioOutput()\n"));
1673	err = roster->GetAudioOutput(&node);
1674	if(err == B_OK) {
1675		if(m_audioInputNode && node.node == m_audioInputNode->id()) {
1676			// input and output nodes identical
1677			// [e.moon 20dec99]
1678			m_audioOutputNode = m_audioInputNode;
1679
1680			// disable transport controls (the default audio output must always
1681			// be running, and can't be seeked.)
1682
1683			m_audioOutputNode->setFlags(
1684				m_audioOutputNode->flags() | disableTransport);
1685		}
1686		else {
1687			m_audioOutputNode = _addRefFor(
1688				node,
1689				_userFlagsForKind(node.kind) | disableTransport,
1690				_implFlagsForKind(node.kind));
1691		}
1692	}
1693}
1694
1695void NodeManager::_addRef(
1696	NodeRef*											ref) {
1697
1698	ASSERT(ref);
1699	ASSERT(IsLocked());
1700
1701	D_METHOD((
1702		"NodeManager::_addRef()\n"));
1703
1704	// precondition: no NodeRef yet exists for this node
1705	// +++++
1706	// [e.moon 21oct99]
1707	// <markjan@xs4all.nl> sez this fails on startup w/ MediaKit 10.5
1708	ASSERT(
1709		m_nodeRefMap.find(ref->id()) == m_nodeRefMap.end());
1710
1711	// add it
1712	// [e.moon 13oct99] PPC-friendly
1713	m_nodeRefMap.insert(node_ref_map::value_type(ref->id(), ref));
1714
1715	// [e.moon 8nov99] call hook
1716	nodeCreated(ref);
1717}
1718
1719void NodeManager::_removeRef(
1720	media_node_id									id) {
1721	ASSERT(IsLocked());
1722
1723	D_METHOD((
1724		"NodeManager::_removeRef()\n"));
1725
1726	node_ref_map::iterator it = m_nodeRefMap.find(id);
1727
1728	// precondition: a NodeRef w/ matching ID is in the map
1729	ASSERT(it != m_nodeRefMap.end());
1730
1731	// [e.moon 8nov99] call hook
1732	nodeDeleted((*it).second);
1733
1734	// remove it
1735	m_nodeRefMap.erase(it);
1736}
1737
1738// create, add, return a NodeRef for the given external node;
1739// must not already exist
1740NodeRef* NodeManager::_addRefFor(
1741	const media_node&							node,
1742	uint32												nodeFlags,
1743	uint32												nodeImplFlags) {
1744
1745	ASSERT(IsLocked());
1746
1747	D_METHOD((
1748		"NodeManager::_addRefFor()\n"));
1749
1750	// precondition: no NodeRef yet exists for this node
1751	ASSERT(
1752		m_nodeRefMap.find(node.node) == m_nodeRefMap.end());
1753
1754//	// precondition: the node actually exists
1755//	live_node_info info;
1756//	D_ROSTER(("# roster->GetLiveNodeInfo()\n"));
1757//	ASSERT(roster->GetLiveNodeInfo(node, &info) == B_OK);
1758
1759	// * create & add ref
1760	NodeRef* ref = new NodeRef(node, this, nodeFlags, nodeImplFlags);
1761	_addRef(ref);
1762
1763	return ref;
1764}
1765
1766void NodeManager::_addConnection(
1767	Connection*										connection) {
1768
1769	ASSERT(connection);
1770	ASSERT(IsLocked());
1771
1772	D_METHOD((
1773		"NodeManager::_addConnection()\n"));
1774
1775	// precondition: not already entered in either source or dest map
1776	// +++++ a more rigorous test would be a very good thing
1777#ifdef DEBUG
1778	for(con_map::iterator it = m_conSourceMap.lower_bound(connection->sourceNode());
1779		it != m_conSourceMap.upper_bound(connection->sourceNode()); ++it) {
1780		ASSERT((*it).second->id() != connection->id());
1781	}
1782	for(con_map::iterator it = m_conDestinationMap.lower_bound(connection->destinationNode());
1783		it != m_conDestinationMap.upper_bound(connection->destinationNode()); ++it) {
1784		ASSERT((*it).second->id() != connection->id());
1785	}
1786#endif
1787
1788	// add to both maps
1789	// [e.moon 13oct99] PPC-friendly
1790	m_conSourceMap.insert(
1791		con_map::value_type(
1792			connection->sourceNode(),
1793			connection));
1794	m_conDestinationMap.insert(
1795		con_map::value_type(
1796			connection->destinationNode(),
1797			connection));
1798
1799	// [e.moon 8nov99] call hook
1800	connectionMade(connection);
1801}
1802
1803void NodeManager::_removeConnection(
1804	const Connection&							connection) {
1805
1806	ASSERT(IsLocked());
1807	con_map::iterator itSource, itDestination;
1808
1809	D_METHOD((
1810		"NodeManager::_removeConnection()\n"));
1811
1812	// [e.moon 8nov99] call hook
1813	connectionBroken(&connection);
1814
1815	// precondition: one entry in both source & dest maps
1816	// +++++ a more rigorous test would be a very good thing
1817
1818	for(
1819		itSource = m_conSourceMap.lower_bound(connection.sourceNode());
1820		itSource != m_conSourceMap.upper_bound(connection.sourceNode());
1821		++itSource)
1822		if((*itSource).second->id() == connection.id())
1823			break;
1824
1825	ASSERT(itSource != m_conSourceMap.end());
1826
1827	for(
1828		itDestination = m_conDestinationMap.lower_bound(connection.destinationNode());
1829		itDestination != m_conDestinationMap.upper_bound(connection.destinationNode());
1830		++itDestination)
1831		if((*itDestination).second->id() == connection.id())
1832			break;
1833
1834	ASSERT(itDestination != m_conDestinationMap.end());
1835
1836	// delete & remove from both maps
1837	delete (*itSource).second;
1838	m_conSourceMap.erase(itSource);
1839	m_conDestinationMap.erase(itDestination);
1840}
1841
1842void NodeManager::_addGroup(
1843	NodeGroup*										group) {
1844
1845	ASSERT(group);
1846	ASSERT(IsLocked());
1847
1848	D_METHOD((
1849		"NodeManager::_addGroup()\n"));
1850
1851	// precondition: group not already in set
1852	ASSERT(
1853		find(
1854			m_nodeGroupSet.begin(),
1855			m_nodeGroupSet.end(),
1856			group) == m_nodeGroupSet.end());
1857
1858	// add
1859	m_nodeGroupSet.push_back(group);
1860
1861//	// [e.moon 7nov99] observe
1862//	add_observer(this, group);
1863}
1864
1865void NodeManager::_removeGroup(
1866	NodeGroup*										group) {
1867
1868	ASSERT(group);
1869	ASSERT(IsLocked());
1870
1871	D_METHOD((
1872		"NodeManager::_removeGroup()\n"));
1873
1874	node_group_set::iterator it = find(
1875		m_nodeGroupSet.begin(),
1876		m_nodeGroupSet.end(),
1877		group);
1878
1879	// precondition: group in set
1880	if(it == m_nodeGroupSet.end()) {
1881		PRINT((
1882			"* NodeManager::_removeGroup(%" B_PRId32 "): group not in set.\n",
1883			group->id()));
1884		return;
1885	}
1886
1887	// remove it
1888	m_nodeGroupSet.erase(it);
1889}
1890
1891// -------------------------------------------------------- //
1892// *** Message Handlers ***
1893// -------------------------------------------------------- //
1894
1895
1896// now returns B_OK iff the message should be relayed to observers
1897// [e.moon 11oct99]
1898
1899inline status_t NodeManager::_handleNodesCreated(
1900	BMessage*											message) {
1901	ASSERT(IsLocked());
1902
1903	status_t err = B_OK;
1904
1905	// fetch number of new nodes
1906	type_code type;
1907	int32 count;
1908	err = message->GetInfo("media_node_id", &type, &count);
1909	if(err < B_OK) {
1910		PRINT((
1911			"* NodeManager::_handleNodesCreated(): GetInfo() failed:\n"
1912			"  %s\n",
1913			strerror(err)));
1914		return err;
1915	}
1916	if(!count) {
1917		PRINT((
1918			"* NodeManager::_handleNodesCreated(): no node IDs in message.\n"));
1919		return err;
1920	}
1921
1922	D_METHOD((
1923		"NodeManager::_handleNodesCreated(): %" B_PRId32 " nodes\n",
1924		count));
1925
1926	// * Create NodeRef instances for the listed nodes.
1927	//   If this is the first message received from the Media Roster,
1928	//   no connection info will be received for these nodes; store them
1929	//   for now (indexed by service-thread port) and figure the connections
1930	//   afterwards.
1931	//   These nodes are mapped by port ID because that's the only criterion
1932	//   available for matching a media_source to a node.
1933
1934	typedef map<port_id, NodeRef*> port_ref_map;
1935	port_ref_map* initialNodes = m_existingNodesInit ? 0 : new port_ref_map;
1936
1937	bool refsCreated = false;
1938
1939	for(int32 n = 0; n < count; ++n) {
1940		// fetch ID of next node
1941		int32 id;
1942		err = message->FindInt32("media_node_id", n, &id);
1943		if(err < B_OK) {
1944			PRINT((
1945				"* NodeManager::_handleNodesCreated(): FindInt32() failed:\n"
1946				"  %s", strerror(err)));
1947			continue;
1948		}
1949
1950		// look up the node
1951		media_node node;
1952		err = roster->GetNodeFor(id, &node);
1953		if(err < B_OK) {
1954			PRINT((
1955				"* NodeManager::_handleNodesCreated(): roster->GetNodeFor(%"
1956					B_PRId32 ") failed:\n"
1957				"  %s\n",
1958				id, strerror(err)));
1959			continue;
1960		}
1961
1962		// look for an existing NodeRef; if not found, create one:
1963		NodeRef* ref = 0;
1964		if(getNodeRef(node.node, &ref) < B_OK) {
1965			// create one
1966			ref = _addRefFor(
1967				node,
1968				_userFlagsForKind(node.kind), // | NodeRef::NO_ROSTER_WATCH, // +++++ e.moon 11oct99
1969				_implFlagsForKind(node.kind) | NodeRef::_CREATE_NOTIFIED);
1970
1971			refsCreated = true;
1972
1973//			// [e.moon 7nov99] call hook
1974//			nodeCreated(ref);
1975
1976		} else {
1977//			PRINT((
1978//				"* NodeManager::_handleNodesCreated():\n"
1979//				"  found existing ref for '%s' (%ld)\n",
1980//				ref->name(), id));
1981
1982
1983			// set _CREATE_NOTIFIED to prevent notification from being passed on
1984			// twice [e.moon 11oct99]
1985			if(!(ref->m_implFlags & NodeRef::_CREATE_NOTIFIED)) {
1986				ref->m_implFlags |= NodeRef::_CREATE_NOTIFIED;
1987				refsCreated = true;
1988			}
1989
1990			// release the (duplicate) media_node reference
1991			err = roster->ReleaseNode(node);
1992			if(err < B_OK) {
1993				PRINT((
1994					"* NodeManager::_handleNodesCreated(): roster->ReleaseNode(%"
1995						B_PRId32 ") failed:\n"
1996					"  %s\n",
1997					id, strerror(err)));
1998			}
1999		}
2000
2001		// add to the 'initial nodes' set if necessary
2002		// [e.moon 13oct99] PPC-friendly
2003		if(initialNodes)
2004			initialNodes->insert(
2005				port_ref_map::value_type(
2006					node.port, ref));
2007	}
2008
2009	if(initialNodes) {
2010		// populate current connections from each node in the set
2011//		PRINT((
2012//			"* NodeManager::_handleNodesCreated(): POPULATING CONNECTIONS (%ld)\n",
2013//			initialNodes->size()));
2014
2015		for(port_ref_map::const_iterator itDest = initialNodes->begin();
2016			itDest != initialNodes->end(); ++itDest) {
2017
2018			// walk each connected input for this node; find corresponding
2019			// output & fill in a new Connection instance.
2020
2021			NodeRef* destRef = (*itDest).second;
2022			ASSERT(destRef);
2023			if(!(destRef->kind() & B_BUFFER_CONSUMER))
2024				// no inputs
2025				continue;
2026
2027			vector<media_input> inputs;
2028			err = destRef->getConnectedInputs(inputs);
2029
2030			// +++++ FAILED ON STARTUP [e.moon 28sep99]; haven't reproduced yet
2031			//       [21oct99] failed again
2032			//ASSERT(err == B_OK);
2033			if(err < B_OK) {
2034				PRINT((
2035					"!!! NodeManager::_handleNodesCreated():\n"
2036					"    NodeRef('%s')::getConnectedInputs() failed:\n"
2037					"    %s\n",
2038					destRef->name(), strerror(err)));
2039
2040				continue;
2041			}
2042
2043
2044//			PRINT((" - %s: %ld inputs\n", destRef->name(), inputs.size()));
2045
2046			for(vector<media_input>::const_iterator itInput = inputs.begin();
2047				itInput != inputs.end(); ++itInput) {
2048
2049				// look for a matching source node by port ID:
2050				const media_input& input = *itInput;
2051				port_ref_map::const_iterator itSource = initialNodes->find(
2052					input.source.port);
2053
2054				if(itSource == initialNodes->end()) {
2055					// source not found!
2056					PRINT((
2057						"* NodeManager::_handleNodesCreated():\n"
2058						"  Building initial Connection set: couldn't find source node\n"
2059						"  connected to input '%s' of '%s' (source port %" B_PRId32 ").\n",
2060						input.name, destRef->name(), input.source.port));
2061					continue;
2062				}
2063
2064				// found it; fetch matching output
2065				NodeRef* sourceRef = (*itSource).second;
2066				ASSERT(sourceRef);
2067				media_output output;
2068				err = sourceRef->findOutput(input.source, &output);
2069				if(err < B_OK) {
2070					PRINT((
2071						"* NodeManager::_handleNodesCreated():\n"
2072						"  Building initial Connection set: couldn't find output\n"
2073						"  in node '%s' connected to input '%s' of '%s'.\n",
2074						sourceRef->name(),
2075						input.name, destRef->name()));
2076					continue;
2077				}
2078
2079				// sanity check
2080//				ASSERT(input.source == output.source);
2081//				ASSERT(input.destination == output.destination);
2082				// diagnostics [e.moon 11jan00]
2083				if(input.source != output.source ||
2084					input.destination != output.destination) {
2085					PRINT((
2086						"!!! NodeManager::_handleNodesCreated():\n"
2087						"    input/output mismatch for connection\n"
2088						"    '%s' (%s) -> '%s' (%s)\n"
2089						"    input.source:        port %" B_PRId32 ", ID %" B_PRId32 "\n"
2090						"    output.source:       port %" B_PRId32 ", ID %" B_PRId32 "\n"
2091						"    input.destination:   port %" B_PRId32 ", ID %" B_PRId32 "\n"
2092						"    output.destination:  port %" B_PRId32 ", ID %" B_PRId32 "\n\n",
2093						sourceRef->name(), output.name,
2094						destRef->name(), input.name,
2095						input.source.port, input.source.id,
2096						output.source.port, output.source.id,
2097						input.destination.port, input.destination.id,
2098						output.destination.port, output.destination.id));
2099					continue;
2100				}
2101
2102				// instantiate & add connection
2103
2104				Connection* con = new Connection(
2105					m_nextConID++,
2106					output.node,
2107					output.source,
2108					output.name,
2109					input.node,
2110					input.destination,
2111					input.name,
2112					input.format,
2113					0);
2114
2115				_addConnection(con);
2116
2117//				// [e.moon 7nov99] call hook
2118//				connectionMade(con);
2119
2120//				PRINT((
2121//					"* NodeManager::_handleNodesCreated(): Found initial connection:\n"
2122//					"  %s:%s -> %s:%s\n",
2123//					sourceRef->name(), con->outputName(),
2124//					destRef->name(), con->inputName()));
2125
2126			} // for(vector<media_input> ...
2127
2128		} // for(port_ref_map ...
2129
2130		// mark the ordeal as over & done with
2131		m_existingNodesInit = true;
2132		// clean up
2133		delete initialNodes;
2134	}
2135
2136	// don't relay message if no new create notifications were received [e.moon 11oct99]
2137	return refsCreated ? B_OK : B_ERROR;
2138}
2139
2140inline void NodeManager::_handleNodesDeleted(
2141	BMessage*											message) {
2142	ASSERT(IsLocked());
2143
2144	D_METHOD((
2145		"NodeManager::_handleNodesDeleted()\n"));
2146
2147	// walk the list of deleted nodes, removing & cleaning up refs
2148	// (and any straggler connections)
2149
2150	type_code type;
2151	int32 count;
2152	status_t err = message->GetInfo("media_node_id", &type, &count);
2153	if(err < B_OK) {
2154		PRINT((
2155			"* NodeManager::_handleNodesDeleted(): GetInfo() failed:\n"
2156			"  %s\n",
2157			strerror(err)));
2158		return;
2159	}
2160	if(!count)
2161		return;
2162
2163	for(int32 n = 0; n < count; n++) {
2164
2165		int32 id;
2166		err = message->FindInt32("media_node_id", n, &id);
2167		if(err < B_OK) {
2168			PRINT((
2169				"* NodeManager::_handleNodesDeleted(): FindInt32() failed\n"
2170				"  %s\n",
2171				strerror(err)));
2172			continue;
2173		}
2174
2175		// fetch ref
2176		NodeRef* ref;
2177		err = getNodeRef(id, &ref);
2178		if(err < B_OK) {
2179			PRINT((
2180				"* NodeManager::_handleNodesDeleted(): getNodeRef(%" B_PRId32
2181					") failed\n"
2182				"  %s\n",
2183				id, strerror(err)));
2184			continue;
2185		}
2186
2187		// find & remove any 'stuck' connections; notify any observers
2188		// that the connections have been removed
2189		vector<Connection> stuckConnections;
2190		ref->getInputConnections(stuckConnections);
2191		ref->getOutputConnections(stuckConnections);
2192
2193		BMessage message(B_MEDIA_CONNECTION_BROKEN);
2194
2195		for(uint32 n = 0; n < stuckConnections.size(); ++n) {
2196			Connection& c = stuckConnections[n];
2197
2198			// generate a complete B_MEDIA_CONNECTION_BROKEN message
2199			// +++++ the message format may be extended in the future -- ick
2200
2201			message.AddData("source", B_RAW_TYPE, &c.source(), sizeof(media_source));
2202			message.AddData("destination", B_RAW_TYPE, &c.destination(), sizeof(media_destination));
2203			message.AddInt32(_connectionField, c.id());
2204			message.AddInt32(_sourceNodeField, c.sourceNode());
2205			message.AddInt32(_destNodeField, c.destinationNode());
2206
2207			_removeConnection(c);
2208		}
2209
2210		// +++++ don't notify if no stuck connections were found
2211		notify(&message);
2212
2213		// ungroup if necessary
2214		if(ref->m_group) {
2215			ASSERT(!ref->isReleased());
2216			ref->m_group->removeNode(ref);
2217		}
2218
2219		// [e.moon 23oct99] mark the node released!
2220		ref->m_nodeReleased = true;
2221
2222//		// [e.moon 7nov99] call hook
2223//		nodeDeleted(ref);
2224
2225		// release it
2226		ref->release();
2227
2228	} // for(int32 n ...
2229}
2230
2231inline void NodeManager::_handleConnectionMade(
2232	BMessage*											message) {
2233	ASSERT(IsLocked());
2234	D_METHOD((
2235		"NodeManager::_handleConnectionMade()\n"));
2236	status_t err;
2237
2238	for(int32 n = 0;;++n) {
2239		media_input input;
2240		media_output output;
2241		const void* data;
2242		ssize_t dataSize;
2243
2244		// fetch output
2245		err = message->FindData("output", B_RAW_TYPE, n, &data, &dataSize);
2246		if(err < B_OK) {
2247			if(!n) {
2248				PRINT((
2249					"* NodeManager::_handleConnectionMade(): no entries in message.\n"));
2250			}
2251			break;
2252		}
2253		if(dataSize < ssize_t(sizeof(media_output))) {
2254			PRINT((
2255				"* NodeManager::_handleConnectionMade(): not enough data for output.\n"));
2256			break;
2257		}
2258		output = *(media_output*)data;
2259
2260		// fetch input
2261		err = message->FindData("input", B_RAW_TYPE, n, &data, &dataSize);
2262		if(err < B_OK) {
2263			if(!n) {
2264				PRINT((
2265					"* NodeManager::_handleConnectionMade(): no complete entries in message.\n"));
2266			}
2267			break;
2268		}
2269		if(dataSize < ssize_t(sizeof(media_input))) {
2270			PRINT((
2271				"* NodeManager::_handleConnectionMade(): not enough data for input.\n"));
2272			break;
2273		}
2274		input = *(media_input*)data;
2275
2276		// look for existing Connection instance
2277		Connection found;
2278		err = findConnection(
2279			output.node.node,
2280			output.source,
2281			&found);
2282		if(err == B_OK) {
2283			PRINT((
2284				"  - existing connection for %s -> %s found\n",
2285				found.outputName(), found.inputName()));
2286			continue;
2287		}
2288
2289		// instantiate & add Connection
2290		Connection* con = new Connection(
2291			m_nextConID++,
2292			output.node,
2293			output.source,
2294			output.name,
2295			input.node,
2296			input.destination,
2297			input.name,
2298			input.format,
2299			0);
2300
2301		_addConnection(con);
2302	}
2303}
2304
2305// augments message with source and destination node ID's
2306inline void NodeManager::_handleConnectionBroken(
2307	BMessage*											message) {
2308
2309	D_METHOD((
2310		"NodeManager::_handleConnectionBroken()\n"));
2311	status_t err;
2312
2313	// walk the listed connections
2314	for(int32 n=0;;n++) {
2315		media_source source;
2316
2317		const void* data;
2318		ssize_t dataSize;
2319
2320		// fetch source
2321		err = message->FindData("source", B_RAW_TYPE, n, &data, &dataSize);
2322		if(err < B_OK) {
2323			if(!n) {
2324				PRINT((
2325					"* NodeManager::_handleConnectionBroken(): incomplete entry in message.\n"));
2326			}
2327			break;
2328		}
2329		if(dataSize < ssize_t(sizeof(media_source))) {
2330			PRINT((
2331				"* NodeManager::_handleConnectionBroken(): not enough data for source.\n"));
2332			continue;
2333		}
2334		source = *(media_source*)data;
2335
2336		// look up the connection +++++ SLOW +++++
2337		Connection con;
2338		err = findConnection(source, &con);
2339		if(err < B_OK) {
2340			PRINT((
2341				"* NodeManager::_handleConnectionBroken(): connection not found:\n"
2342				"  %" B_PRId32 ":%" B_PRId32 "\n",
2343				source.port, source.id));
2344
2345			// add empty entry to message
2346			message->AddInt32(_connectionField, 0);
2347			message->AddInt32(_sourceNodeField, 0);
2348			message->AddInt32(_destNodeField, 0);
2349			continue;
2350		}
2351
2352		// add entry to the message
2353		message->AddInt32(_connectionField, con.id());
2354		message->AddInt32(_sourceNodeField, con.sourceNode());
2355		message->AddInt32(_destNodeField, con.destinationNode());
2356
2357//		// [e.moon 7nov99] call hook
2358//		connectionBroken(&con);
2359
2360		// home free; delete the connection
2361		_removeConnection(con);
2362
2363	} // for(int32 n ...
2364}
2365
2366inline void
2367NodeManager::_handleFormatChanged(BMessage *message)
2368{
2369	D_METHOD((
2370		"NodeManager::_handleFormatChanged()\n"));
2371	status_t err;
2372
2373	ssize_t dataSize;
2374
2375	// fetch source
2376	media_source* source;
2377	err = message->FindData("be:source", B_RAW_TYPE, (const void**)&source, &dataSize);
2378	if(err < B_OK) {
2379		PRINT((
2380			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2381		return;
2382	}
2383
2384	// fetch destination
2385	media_destination* destination;
2386	err = message->FindData("be:destination", B_RAW_TYPE, (const void**)&destination, &dataSize);
2387	if(err < B_OK) {
2388		PRINT((
2389			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2390		return;
2391	}
2392
2393	// fetch format
2394	media_format* format;
2395	err = message->FindData("be:format", B_RAW_TYPE, (const void**)&format, &dataSize);
2396	if(err < B_OK) {
2397		PRINT((
2398			"* NodeManager::_handleFormatChanged(): incomplete entry in message.\n"));
2399		return;
2400	}
2401
2402	// find matching connection
2403	for(con_map::const_iterator it = m_conSourceMap.begin();
2404		it != m_conSourceMap.end(); ++it) {
2405		if((*it).second->source() == *source) {
2406			if((*it).second->destination() != *destination) {
2407				// connection defunct
2408				return;
2409			}
2410
2411			// found
2412			(*it).second->m_format = *format;
2413
2414			// attach node IDs to message
2415			message->AddInt32(_connectionField, (*it).second->id());
2416			message->AddInt32(_sourceNodeField, (*it).second->sourceNode());
2417			message->AddInt32(_destNodeField, (*it).second->destinationNode());
2418
2419			break;
2420		}
2421	}
2422}
2423
2424
2425// return flags appropriate for an external
2426// node with the given 'kind'
2427
2428inline uint32 NodeManager::_userFlagsForKind(
2429	uint32												kind) {
2430
2431	uint32 f = 0;
2432	if(
2433//		kind & B_TIME_SOURCE ||
2434		kind & B_PHYSICAL_OUTPUT
2435		// || kind & B_SYSTEM_MIXER [now in initCommonNodes() e.moon 17nov99]
2436		)
2437		f |= (NodeRef::NO_START_STOP | NodeRef::NO_SEEK | NodeRef::NO_PREROLL);
2438
2439//	// [28sep99 e.moon] physical inputs may not be stopped for now; at
2440//	// least one sound input node (for emu10k) stops working when requested
2441//	// to stop.
2442//	// +++++ should this logic be in initCommonNodes()?
2443//	if(
2444//		kind & B_PHYSICAL_INPUT)
2445//		f |= NodeRef::NO_STOP;
2446
2447	return f;
2448}
2449
2450inline uint32 NodeManager::_implFlagsForKind(
2451	uint32												kind) {
2452
2453	return 0;
2454}
2455
2456// [e.moon 28sep99] latency updating
2457// These methods must set the recording-mode delay for
2458// any B_RECORDING nodes they handle.
2459
2460// +++++ abstract to 'for each' and 'for each from'
2461//       methods (template or callback?)
2462
2463
2464// refresh cached latency for every node in the given group
2465// (or all nodes if no group given.)
2466
2467inline void NodeManager::_updateLatencies(
2468	NodeGroup*										group) {
2469
2470	ASSERT(IsLocked());
2471	if(group) {
2472		ASSERT(group->isLocked());
2473	}
2474
2475	if(group) {
2476		for(NodeGroup::node_set::iterator it = group->m_nodes.begin();
2477			it != group->m_nodes.end(); ++it) {
2478
2479			(*it)->_updateLatency();
2480		}
2481	}
2482	else {
2483		for(node_ref_map::iterator it = m_nodeRefMap.begin();
2484			it != m_nodeRefMap.end(); ++it) {
2485
2486			(*it).second->_updateLatency();
2487		}
2488	}
2489}
2490
2491// refresh cached latency for every node attached to
2492// AND INCLUDING the given origin node.
2493// if 'recurse' is true, affects indirectly attached
2494// nodes as well.
2495
2496
2497inline void NodeManager::_updateLatenciesFrom(
2498	NodeRef*											origin,
2499	bool													recurse) {
2500
2501	ASSERT(IsLocked());
2502
2503//	PRINT(("### NodeManager::_updateLatenciesFrom()\n"));
2504
2505	origin->lock();
2506	origin->_updateLatency();
2507	origin->unlock();
2508
2509	_lockAllGroups(); // [e.moon 13oct99]
2510
2511	_for_each_state st;
2512	_do_for_each_connected(
2513		this,
2514		origin,
2515		0, // all groups
2516		recurse,
2517		mem_fun(&NodeRef::_updateLatency),
2518		&st);
2519
2520	_unlockAllGroups(); // [e.moon 13oct99]
2521}
2522
2523// a bit of unpleasantness [e.moon 13oct99]
2524void NodeManager::_lockAllGroups() {
2525
2526	ASSERT(IsLocked());
2527	for(node_group_set::iterator it = m_nodeGroupSet.begin();
2528		it != m_nodeGroupSet.end(); ++it) {
2529		(*it)->lock();
2530	}
2531}
2532
2533void NodeManager::_unlockAllGroups() {
2534	ASSERT(IsLocked());
2535	for(node_group_set::iterator it = m_nodeGroupSet.begin();
2536		it != m_nodeGroupSet.end(); ++it) {
2537		(*it)->unlock();
2538	}
2539}
2540
2541
2542// END -- NodeManager.cpp --
2543