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