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