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