2 * Copyright (c) 1999-2000, Eric Moon.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
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.
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.
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.
32 // NodeManager.h (Cortex)
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.
41 // A new group is created with the following information:
42 // - time source (defaults to the DAC time source)
43 // - a user-provided name
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.)
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
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.
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
72 // e.moon 11aug99 Expanded findConnection() methods.
73 // e.moon 6jul99 Begun
75 #ifndef __NodeManager_H__
76 #define __NodeManager_H__
78 #include "ILockable.h"
79 #include "ObservableLooper.h"
83 #include <MediaDefs.h>
84 #include <MediaNode.h>
91 #include "cortex_defs.h"
92 __BEGIN_CORTEX_NAMESPACE
99 public ObservableLooper
,
102 // primary parent class:
103 typedef ObservableLooper _inherited
;
105 friend class NodeGroup
;
106 friend class NodeRef
;
107 friend class Connection
;
109 public: // *** messages
111 // NodeManager retransmits Media Roster messages to its listeners,
112 // after processing each message.
114 /// B_MEDIA_CONNECTION_BROKEN
115 // This message, as sent by the Media Roster, contains only
116 // source/destination information. NodeManager adds these fields
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
123 // __destination_node_id: media_node_id of the node corresponding to
124 // the source; 0 if no matching Connection was
127 // B_MEDIA_FORMAT_CHANGED
128 // NodeManager add these fields as above:
131 // __destination_node_id
133 enum outbound_message_t
{
134 M_OBSERVER_ADDED
=NodeManager_message_base
,
142 // groupID: int32 x2 (the first is the original)
149 public: // *** default group names
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
;
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.
166 virtual void nodeCreated(
169 virtual void nodeDeleted(
172 virtual void connectionMade(
173 Connection
* connection
);
175 virtual void connectionBroken(
176 const Connection
* connection
);
178 virtual void connectionFailed(
179 const media_output
& output
,
180 const media_input
& input
,
181 const media_format
& format
,
184 public: // *** ctor/dtor
187 bool useAddOnHost
=false);
189 // don't directly delete NodeManager;
190 // use IObservable::release()
191 virtual ~NodeManager();
193 public: // *** const members
195 // cached roster pointer
196 ::BMediaRoster
* const roster
;
198 public: // *** operations
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.)
208 NodeRef
** outRef
) const;
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.
215 status_t
findConnection(
217 const media_source
& source
,
218 Connection
* outConnection
) const;
220 status_t
findConnection(
222 const media_destination
& destination
,
223 Connection
* outConnection
) const;
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
230 status_t
findConnection(
231 media_node_id sourceNode
,
232 media_node_id destinationNode
,
233 Connection
* outConnection
) const;
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.
243 media_node_id nodeB
);
246 // implementation of above
247 class _find_route_state
;
248 bool _find_route_recurse(
250 media_node_id target
,
251 _find_route_state
* state
);
254 // fetches Connection corresponding to a given source or
255 // destination; Returns an invalid connection and B_BAD_VALUE if
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.
265 status_t
findConnection(
266 const media_source
& source
,
267 Connection
* outConnection
) const;
269 status_t
findConnection(
270 const media_destination
& destination
,
271 Connection
* outConnection
) const;
273 // fetch NodeRefs for system nodes (if a particular node doesn't
274 // exist, these methods return 0)
276 NodeRef
* audioInputNode() const;
277 NodeRef
* videoInputNode() const;
278 NodeRef
* audioMixerNode() const;
279 NodeRef
* audioOutputNode() const;
280 NodeRef
* videoOutputNode() const;
284 NodeGroup
* createGroup(
286 BMediaNode::run_mode runMode
=BMediaNode::B_INCREASE_LATENCY
);
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.
293 uint32
countGroups() const;
297 // look up a group by unique ID; returns B_BAD_VALUE if no
298 // matching group was found
302 NodeGroup
** outGroup
) const;
304 // look up a group by name; returns B_NAME_NOT_FOUND if
305 // no group matching the name was found.
309 NodeGroup
** outGroup
) const;
311 // merge the given source group to the given destination;
312 // empties and releases the source group
314 status_t
mergeGroups(
315 NodeGroup
* sourceGroup
,
316 NodeGroup
* destinationGroup
);
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
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.
334 NodeRef
* outsideNode
,
335 NodeGroup
** outGroup
);
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.
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
351 status_t
instantiate(
352 const dormant_node_info
& info
,
354 bigtime_t timeout
=B_INFINITE_TIMEOUT
,
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.
363 status_t
instantiate(
364 const entry_ref
& file
,
365 uint64 requireNodeKinds
,
367 bigtime_t timeout
=B_INFINITE_TIMEOUT
,
369 bigtime_t
* outDuration
=0);
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.
380 // the most flexible form of connect(): set the template
381 // format as you would for BMediaRoster::Connect().
384 const media_output
& output
,
385 const media_input
& input
,
386 const media_format
& templateFormat
,
387 Connection
* outConnection
=0);
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.
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).
398 const media_output
& output
,
399 const media_input
& input
,
400 Connection
* outConnection
=0);
402 // disconnects the connection represented by the provided
403 // Connection object. if successful, returns B_OK.
406 const Connection
& connection
);
408 public: // *** node/connection iteration
409 // *** MUST BE LOCKED for any of these calls
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.)
420 // if you want to stop iterating, call this method to avoid leaking
422 void disposeRefCookie(
425 status_t
getNextConnection(
426 Connection
* outConnection
,
429 void disposeConnectionCookie(
432 public: // *** BHandler impl
433 void MessageReceived(BMessage
* message
); //nyi
435 public: // *** IObservable hooks
436 virtual void observerAdded(
437 const BMessenger
& observer
);
439 virtual void observerRemoved(
440 const BMessenger
& observer
);
442 virtual void notifyRelease();
444 virtual void releaseComplete();
447 public: // *** ILockable impl.
450 bigtime_t timeout
=B_INFINITE_TIMEOUT
);
455 virtual bool isLocked(
456 lock_t type
=WRITE
) const;
459 protected: // *** internal operations (LOCK REQUIRED)
462 void _initCommonNodes();
469 // create, add, return a NodeRef for the given external node;
470 // must not already exist
472 const media_node
& node
,
474 uint32 nodeImplFlags
=0);
477 Connection
* connection
);
478 void _removeConnection(
479 const Connection
& connection
);
487 inline void _clearGroup(
490 inline void _freeConnection(
491 Connection
* connection
);
495 // now returns B_OK iff the message should be relayed to observers
497 inline status_t
_handleNodesCreated(
500 inline void _handleNodesDeleted(
503 inline void _handleConnectionMade(
506 inline void _handleConnectionBroken(
509 inline void _handleFormatChanged(
511 // return flags appropriate for an external
512 // node with the given 'kind'
514 inline uint32
_userFlagsForKind(
517 inline uint32
_implFlagsForKind(
520 // [e.moon 28sep99] latency updating
521 // These methods must set the recording-mode delay for
522 // any B_RECORDING nodes they handle.
524 // +++++ abstract to 'for each' and 'for each from'
525 // methods (template or callback?)
528 // refresh cached latency for every node in the given group
529 // (or all nodes if no group given.)
531 inline void _updateLatencies(
532 NodeGroup
* group
=0);
534 // refresh cached latency for every node attached to
535 // AND INCLUDING the given origin node.
536 // if 'recurse' is true, affects indirectly attached
539 inline void _updateLatenciesFrom(
541 bool recurse
=false);
543 // a bit of unpleasantness [e.moon 13oct99]
544 inline void _lockAllGroups();
545 inline void _unlockAllGroups();
547 private: // *** internal messages
550 _M_GROUP_RELEASED
= NodeManager_int_message_base
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
;
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
;
564 // the NodeGroup store
565 typedef std::vector
<NodeGroup
*> node_group_set
;
566 node_group_set m_nodeGroupSet
;
568 // common system nodes
569 NodeRef
* m_audioInputNode
;
570 NodeRef
* m_videoInputNode
;
571 NodeRef
* m_audioMixerNode
;
572 NodeRef
* m_audioOutputNode
;
573 NodeRef
* m_videoOutputNode
;
575 // next unique connection ID
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
;
583 // true if nodes should be launched via an external application
588 __END_CORTEX_NAMESPACE
589 #endif /*__NodeManager_H__*/