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