xref: /haiku/src/apps/cortex/NodeManager/NodeManager.h (revision 98057dd02a2411868fd4c35f7a48d20acfd92c23)
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 
89 class BMediaRoster;
90 
91 #include "cortex_defs.h"
92 __BEGIN_CORTEX_NAMESPACE
93 
94 class Connection;
95 class NodeGroup;
96 class NodeRef;
97 
98 class 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 
109 public:				// *** 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 
149 public:				// *** 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 
158 public:				// *** 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 
184 public:				// *** ctor/dtor
185 
186 	NodeManager(
187 		bool													useAddOnHost=false);
188 
189 	// don't directly delete NodeManager;
190 	// use IObservable::release()
191 	virtual ~NodeManager();
192 
193 public:				// *** const members
194 
195 	// cached roster pointer
196 	::BMediaRoster*	const						roster;
197 
198 public:				// *** 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 
245 private:
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 
253 public:
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 
408 public:				// *** 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 
432 public:				// *** BHandler impl
433 	void MessageReceived(BMessage*	message); //nyi
434 
435 public:				// *** 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 
447 public:				// *** 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 
459 protected:			// *** 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 
547 private:				// *** internal messages
548 	enum int_message_t {
549 		//  groupID: int32
550 		_M_GROUP_RELEASED					= NodeManager_int_message_base
551 	};
552 
553 private:				// *** 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