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.h (Cortex)
33//
34// * PURPOSE
35//   Provides a Media Kit application with a straightforward
36//   way to keep track of media nodes and the connections
37//   between them.  Nodes are collected into sets via the
38//   NodeGroup class; these sets can be controlled in tandem.
39//
40// * GROUPING NOTES
41//   A new group is created with the following information:
42//   - time source (defaults to the DAC time source)
43//   - a user-provided name
44//
45//   New nodes can be added to a group via NodeGroup methods.  When a
46//   node is added to a group, it will automatically be assigned the
47//   group's time source.  Unless the node has a run mode set, it will
48//   also be assigned the group's run mode.  (If the group is in B_OFFLINE
49//   mode, this will be assigned to all nodes even if they specify something
50//   else.)  If a node is added to a group whose transport is running, it
51//   will automatically be seeked and started (unless one or both of those
52//   operations has been disabled.)
53//
54// * SYNCHRONIZATION NOTES
55//   Each NodeManager object, including all the NodeGroup and NodeRef
56//   objects in its care, is synchronized by a single semaphore.
57//   Most operations in these three classes require that the object
58//   be locked.
59//
60// * UI HOOKS
61//   NodeManager resends any Media Roster messages to all observers
62//   *after* processing them: the NodeRef corresponding to a newly-
63//   created node, for example, must exist by the time that a
64//   NodeManager observer receives B_MEDIA_NODE_CREATED.
65//
66//
67// * HISTORY
68//   e.moon		7nov99		1) added hooks for Media Roster message processing
69//											2) improved NodeGroup handling
70//   e.moon		6nov99		safe node instantiation (via addon-host
71//											application)
72//   e.moon		11aug99		Expanded findConnection() methods.
73//   e.moon		6jul99		Begun
74
75#ifndef __NodeManager_H__
76#define __NodeManager_H__
77
78#include "ILockable.h"
79#include "ObservableLooper.h"
80#include "observe.h"
81
82#include <Looper.h>
83#include <MediaDefs.h>
84#include <MediaNode.h>
85
86#include <vector>
87#include <map>
88
89class BMediaRoster;
90
91#include "cortex_defs.h"
92__BEGIN_CORTEX_NAMESPACE
93
94class Connection;
95class NodeGroup;
96class NodeRef;
97
98class NodeManager :
99	public	ObservableLooper,
100	public	ILockable {
101
102	// primary parent class:
103	typedef	ObservableLooper _inherited;
104
105	friend class NodeGroup;
106	friend class NodeRef;
107	friend class Connection;
108
109public:				// *** messages
110	// [13aug99]
111	//   NodeManager retransmits Media Roster messages to its listeners,
112	//   after processing each message.
113	//
114	///  B_MEDIA_CONNECTION_BROKEN
115	//     This message, as sent by the Media Roster, contains only
116	//     source/destination information.  NodeManager adds these fields
117	//     to the message:
118	//     __connection_id:        (uint32) id of the Connection; 0 if no
119	//                             matching Connection was found.
120	//     __source_node_id:			 media_node_id of the node corresponding to
121	//                        		 the source; 0 if no matching Connection was
122	//                        		 found.
123	//     __destination_node_id:	 media_node_id of the node corresponding to
124	//                         		 the source; 0 if no matching Connection was
125	//                         		 found.
126	//
127	//   B_MEDIA_FORMAT_CHANGED
128	//     NodeManager add these fields as above:
129	//     __connection_id
130	//     __source_node_id
131	//     __destination_node_id
132
133	enum outbound_message_t {
134		M_OBSERVER_ADDED						=NodeManager_message_base,
135		M_OBSERVER_REMOVED,
136		M_RELEASED,
137
138		// groupID: int32
139		M_GROUP_CREATED,
140		M_GROUP_DELETED,
141
142		// groupID: int32 x2 (the first is the original)
143		M_GROUP_SPLIT,
144
145		// groupID: int32 x2
146		M_GROUPS_MERGED
147	};
148
149public:				// *** default group names
150
151	static const char* const			s_defaultGroupPrefix;
152	static const char* const			s_timeSourceGroup;
153	static const char* const			s_audioInputGroup;
154	static const char* const			s_videoInputGroup;
155	static const char* const			s_audioMixerGroup;
156	static const char* const			s_videoOutputGroup;
157
158public:				// *** hooks
159
160	// [e.moon 7nov99] these hooks are called during processing of
161	// BMediaRoster messages, before any notification is sent to
162	// observers.  For example, if a B_MEDIA_NODES_CREATED message
163	// were received describing 3 new nodes, nodeCreated() would be
164	// called 3 times before the notification was sent.
165
166	virtual void nodeCreated(
167		NodeRef*											ref);
168
169	virtual void nodeDeleted(
170		const NodeRef*								ref);
171
172	virtual void connectionMade(
173		Connection*										connection);
174
175	virtual void connectionBroken(
176		const Connection*							connection);
177
178	virtual void connectionFailed(
179		const media_output &							output,
180		const media_input &								input,
181		const media_format &							format,
182		status_t										error);
183
184public:				// *** ctor/dtor
185
186	NodeManager(
187		bool													useAddOnHost=false);
188
189	// don't directly delete NodeManager;
190	// use IObservable::release()
191	virtual ~NodeManager();
192
193public:				// *** const members
194
195	// cached roster pointer
196	::BMediaRoster*	const						roster;
197
198public:				// *** operations
199
200	// * ACCESS
201
202	// fetches NodeRef corresponding to a given ID; returns
203	// B_BAD_VALUE if no matching entry was found (and writes
204	// a 0 into the provided pointer.)
205
206	status_t getNodeRef(
207		media_node_id									id,
208		NodeRef**											outRef) const;
209
210	// [13aug99]
211	// fetches Connection corresponding to a given source/destination
212	// on a given node.  Returns an invalid connection and B_BAD_VALUE
213	// if no matching connection was found.
214
215	status_t findConnection(
216		media_node_id									node,
217		const media_source&						source,
218		Connection*										outConnection) const;
219
220	status_t findConnection(
221		media_node_id									node,
222		const media_destination&			destination,
223		Connection*										outConnection) const;
224
225	// [e.moon 28sep99]
226	// fetches a Connection matching the given source and destination
227	// nodes.  Returns an invalid connection and B_BAD_VALUE if
228	// no matching connection was found
229
230	status_t findConnection(
231		media_node_id									sourceNode,
232		media_node_id									destinationNode,
233		Connection*										outConnection) const;
234
235	// [e.moon 28sep99]
236	// tries to find a route from 'nodeA' to 'nodeB'; returns
237	// true if one exists, false if not.  If nodeA and nodeB
238	// are the same node, only returns true if it's actually
239	// connected to itself.
240
241	bool findRoute(
242		media_node_id									nodeA,
243		media_node_id									nodeB);
244
245private:
246	// implementation of above
247	class _find_route_state;
248	bool _find_route_recurse(
249		NodeRef*													origin,
250		media_node_id											target,
251		_find_route_state*								state);
252
253public:
254	// fetches Connection corresponding to a given source or
255	// destination; Returns an invalid connection and B_BAD_VALUE if
256	// none found.
257	// *   Note: this is the slowest possible way to look up a
258	//     connection.  Since the low-level source/destination
259	//     structures don't include a node ID, and a destination
260	//     port can differ from its node's control port, a linear
261	//     search of all known connections is performed.  Only
262	//     use these methods if you have no clue what node the
263	//     connection corresponds to.
264
265	status_t findConnection(
266		const media_source&						source,
267		Connection*										outConnection) const;
268
269	status_t findConnection(
270		const media_destination&			destination,
271		Connection*										outConnection) const;
272
273	// fetch NodeRefs for system nodes (if a particular node doesn't
274	// exist, these methods return 0)
275
276	NodeRef* audioInputNode() const;
277	NodeRef* videoInputNode() const;
278	NodeRef* audioMixerNode() const;
279	NodeRef* audioOutputNode() const;
280	NodeRef* videoOutputNode() const;
281
282	// * GROUP CREATION
283
284	NodeGroup* createGroup(
285		const char*										name,
286		BMediaNode::run_mode					runMode=BMediaNode::B_INCREASE_LATENCY);
287
288	// fetch groups by index
289	// - you can write-lock the manager during sets of calls to these methods;
290	//   this ensures that the group set won't change.  The methods do lock
291	//   the group internally, so locking isn't explicitly required.
292
293	uint32 countGroups() const;
294	NodeGroup* groupAt(
295		uint32												index) const;
296
297	// look up a group by unique ID; returns B_BAD_VALUE if no
298	// matching group was found
299
300	status_t findGroup(
301		uint32												id,
302		NodeGroup**										outGroup) const;
303
304	// look up a group by name; returns B_NAME_NOT_FOUND if
305	// no group matching the name was found.
306
307	status_t findGroup(
308		const char*										name,
309		NodeGroup**										outGroup) const;
310
311	// merge the given source group to the given destination;
312	// empties and releases the source group
313
314	status_t mergeGroups(
315		NodeGroup*										sourceGroup,
316		NodeGroup*										destinationGroup);
317
318	// [e.moon 28sep99]
319	// split group: given two nodes currently in the same group
320	// that are not connected (directly OR indirectly),
321	// this method removes outsideNode, and all nodes connected
322	// to outsideNode, from the common group.  These nodes are
323	// then added to a new group, returned in 'outGroup'.  The
324	// new group has " split" appended to the end of the original
325	// group's name.
326	//
327	// Returns B_NOT_ALLOWED if any of the above conditions aren't
328	// met (ie. the nodes are in different groups or an indirect
329	// route exists from one to the other), or B_OK if the group
330	// was split successfully.
331
332	status_t splitGroup(
333		NodeRef*											insideNode,
334		NodeRef*											outsideNode,
335		NodeGroup**										outGroup);
336
337	// * INSTANTIATION & CONNECTION
338	//   Use these calls rather than the associated BMediaRoster()
339	//   methods to assure that the nodes and connections you set up
340	//   can be properly serialized & reconstituted.
341
342	// basic BMediaRoster::InstantiateDormantNode() wrapper
343	// - writes a 0 into *outRef if the instantiation fails
344	// - [e.moon 23oct99]
345	//   returns B_BAD_INDEX if InstantiateDormantNode() returns
346	//   success, but doesn't hand back a viable media_node
347	// - [e.moon 6nov99] +++++ 'distributed' instantiate:
348	//   wait for an external app to create the node; allow for
349	//   failure.
350
351	status_t instantiate(
352		const dormant_node_info&			info,
353		NodeRef**											outRef=0,
354		bigtime_t											timeout=B_INFINITE_TIMEOUT,
355		uint32												nodeFlags=0);
356
357	// SniffRef/Instantiate.../SetRefFor: a one-call interface
358	// to create a node capable of playing a given media file.
359	// - writes a 0 into *outRef if the instantiation fails; on the
360	//   other hand, if instantiation succeeds, but SetRefFor() fails,
361	//   a NodeRef will still be returned.
362
363	status_t instantiate(
364		const entry_ref&							file,
365		uint64												requireNodeKinds,
366		NodeRef**											outRef,
367		bigtime_t											timeout=B_INFINITE_TIMEOUT,
368		uint32												nodeFlags=0,
369		bigtime_t*										outDuration=0);
370
371	// use this method to reference nodes created within your
372	// application.  These nodes can't be automatically reconstituted
373	// by the cortex serializer yet.
374
375	status_t reference(
376		BMediaNode*										node,
377		NodeRef**											outRef,
378		uint32												nodeFlags=0);
379
380	// the most flexible form of connect(): set the template
381	// format as you would for BMediaRoster::Connect().
382
383	status_t connect(
384		const media_output&						output,
385		const media_input&						input,
386		const media_format&						templateFormat,
387		Connection*										outConnection=0);
388
389	// format-guessing form of connect(): tries to find
390	// a common format between output & input before connection;
391	// returns B_MEDIA_BAD_FORMAT if no common format type found.
392	//
393	// NOTE: the specifics of the input and output formats are ignored;
394	//       this method only looks at the format type, and properly
395	//       handles wildcards at that level (B_MEDIA_NO_TYPE).
396
397	status_t connect(
398		const media_output&						output,
399		const media_input&						input,
400		Connection*										outConnection=0);
401
402	// disconnects the connection represented by the provided
403	// Connection object.  if successful, returns B_OK.
404
405	status_t disconnect(
406		const Connection&							connection);
407
408public:				// *** node/connection iteration
409							// *** MUST BE LOCKED for any of these calls
410
411	// usage:
412	//   For the first call, pass 'cookie' a pointer to a void* set to 0.
413	//   Returns B_BAD_INDEX when the set of nodes has been exhausted (and
414	//   invalidates the cookie, so don't try to use it after this point.)
415
416	status_t getNextRef(
417		NodeRef**											outRef,
418		void**												cookie);
419
420	// if you want to stop iterating, call this method to avoid leaking
421	// memory
422	void disposeRefCookie(
423		void**												cookie);
424
425	status_t getNextConnection(
426		Connection*										outConnection,
427		void**												cookie);
428
429	void disposeConnectionCookie(
430		void**												cookie);
431
432public:				// *** BHandler impl
433	void MessageReceived(BMessage*	message); //nyi
434
435public:				// *** IObservable hooks
436	virtual void observerAdded(
437		const BMessenger&				observer);
438
439	virtual void observerRemoved(
440		const BMessenger&				observer);
441
442	virtual void notifyRelease();
443
444	virtual void releaseComplete();
445
446
447public:				// *** ILockable impl.
448	virtual bool lock(
449		lock_t												type=WRITE,
450		bigtime_t											timeout=B_INFINITE_TIMEOUT);
451
452	virtual bool unlock(
453		lock_t												type=WRITE);
454
455	virtual bool isLocked(
456		lock_t												type=WRITE) const;
457
458
459protected:			// *** internal operations (LOCK REQUIRED)
460
461
462	void _initCommonNodes();
463
464	void _addRef(
465		NodeRef*											ref);
466	void _removeRef(
467		media_node_id									id);
468
469	// create, add, return a NodeRef for the given external node;
470	// must not already exist
471	NodeRef* _addRefFor(
472		const media_node&							node,
473		uint32												nodeFlags,
474		uint32												nodeImplFlags=0);
475
476	void _addConnection(
477		Connection*										connection);
478	void _removeConnection(
479		const Connection&							connection);
480
481	void _addGroup(
482		NodeGroup*										group);
483	void _removeGroup(
484		NodeGroup*										group);
485
486	// dtor helpers
487	inline void _clearGroup(
488		NodeGroup*										group);
489
490	inline void _freeConnection(
491		Connection*										connection);
492
493	// message handlers
494
495	// now returns B_OK iff the message should be relayed to observers
496	// [e.moon 11oct99]
497	inline status_t _handleNodesCreated(
498		BMessage*											message);
499
500	inline void _handleNodesDeleted(
501		BMessage*											message);
502
503	inline void _handleConnectionMade(
504		BMessage*											message);
505
506	inline void _handleConnectionBroken(
507		BMessage*											message);
508
509	inline void _handleFormatChanged(
510		BMessage*											message);
511	// return flags appropriate for an external
512	// node with the given 'kind'
513
514	inline uint32 _userFlagsForKind(
515		uint32												kind);
516
517	inline uint32 _implFlagsForKind(
518		uint32												kind);
519
520	// [e.moon 28sep99] latency updating
521	// These methods must set the recording-mode delay for
522	// any B_RECORDING nodes they handle.
523
524	// +++++ abstract to 'for each' and 'for each from'
525	//       methods (template or callback?)
526
527
528	// refresh cached latency for every node in the given group
529	// (or all nodes if no group given.)
530
531	inline void _updateLatencies(
532		NodeGroup*										group =0);
533
534	// refresh cached latency for every node attached to
535	// AND INCLUDING the given origin node.
536	// if 'recurse' is true, affects indirectly attached
537	// nodes as well.
538
539	inline void _updateLatenciesFrom(
540		NodeRef*											origin,
541		bool													recurse =false);
542
543	// a bit of unpleasantness [e.moon 13oct99]
544	inline void _lockAllGroups();
545	inline void _unlockAllGroups();
546
547private:				// *** internal messages
548	enum int_message_t {
549		//  groupID: int32
550		_M_GROUP_RELEASED					= NodeManager_int_message_base
551	};
552
553private:				// *** members
554	// the main NodeRef store
555	typedef std::map<media_node_id, NodeRef*> node_ref_map;
556	node_ref_map					m_nodeRefMap;
557
558	// the Connection stores (connections are indexed by both
559	// source and destination node ID)
560	typedef std::multimap<media_node_id, Connection*> con_map;
561	con_map								m_conSourceMap;
562	con_map								m_conDestinationMap;
563
564	// the NodeGroup store
565	typedef std::vector<NodeGroup*> node_group_set;
566	node_group_set				m_nodeGroupSet;
567
568	// common system nodes
569	NodeRef*							m_audioInputNode;
570	NodeRef*							m_videoInputNode;
571	NodeRef*							m_audioMixerNode;
572	NodeRef*							m_audioOutputNode;
573	NodeRef*							m_videoOutputNode;
574
575	// next unique connection ID
576	uint32								m_nextConID;
577
578	// false until the first 'nodes created' message is
579	// received from the Media Roster, and pre-existing connection
580	// info is filled in:
581	bool									m_existingNodesInit;
582
583	// true if nodes should be launched via an external application
584	// (using AddOnHost)
585	bool									m_useAddOnHost;
586};
587
588__END_CORTEX_NAMESPACE
589#endif /*__NodeManager_H__*/
590