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 // NodeGroup.h (Cortex/NodeManager) 33 // 34 // * PURPOSE 35 // Represents a logical group of media kit nodes. 36 // Provides group-level operations (transport control 37 // and serialization, for example.) 38 // 39 // * NODE SYNC/LOOPING +++++ 40 // 41 // 22jul99 42 // ---------------------------- 43 // +++++ 44 // - cycling support needs to be thoroughly defined; ie. what 45 // can it do, and what can it NOT do. 46 // 47 // For example, a change in node latency will likely confuse 48 // the cycling mechanism. Is there any possible way to avoid 49 // this without explicit help from the node? The roster sends 50 // no notification, and even if it did the message might not 51 // arrive in time. Polling is possible, but ...ick. 52 // 53 // [this is assuming the 'just-in-time' cycling mechanism: 54 // seeks are queued as late as possible; if the latency increases 55 // significantly, the seek will be queued TOO late and the 56 // node will get out of sync.] 57 // 58 // ---------------------------- 59 // 14jul99 60 // 61 // How about handling addition of a node to a group while it's 62 // playing? The "effects insert" scenario: 63 // 64 // 1) you have a producer node, followed by (0..*) filters in 65 // series, followed by a consumer 66 // 2) the transport is started 67 // 3) you wish to connect a new filter in the chain with minimal 68 // interruption -- preferably a pause in output, resuming 69 // with media time & perf. time still in sync. 70 // 71 // Process: 72 // - instantiate the filter & do any setup needed 73 // - [tmStart = current media time; tpStart = current perf. time] 74 // - stop the transport; break the connection at the insert 75 // point and connect the new filter 76 // - calculate the new latency 77 // - cue a seek for all nodes: 78 // to tmStart+latency+pad, 79 // at tpStart+latency+pad - 1 80 // - cue a start for all nodes: 81 // at tpStart+latency+pad 82 // 83 // (pad is the estimated amount of time taken to stop, break 84 // & make connections, etc. It can probably be determined in 85 // a test loop.) 86 // 87 // With the current NodeManager grouping behavior, this operation 88 // would split the NodeGroup, then (provided the filter insertion 89 // works) join it again. This is such a common procedure, though, 90 // that an 'insert' operation at the NodeManager level would be 91 // pretty damn handy. So would a 'remove insert' operation (given 92 // a node with a single input and output, connect its input's source 93 // to its output's destination, with the original format.) 94 // 95 // * HISTORY 96 // e.moon 29sep99 Made thread control variables 'volatile'. 97 // e.moon 6jul99 Begun 98 99 #ifndef __NodeGroup_H__ 100 #define __NodeGroup_H__ 101 102 #include "ObservableHandler.h" 103 #include "observe.h" 104 #include "ILockable.h" 105 106 // +++++ [e.moon 3dec99] need to include these for calcLatencyFn impl 107 // +++++ YUCK 108 #include "NodeRef.h" 109 #include <MediaRoster.h> 110 111 #include <vector> 112 113 #include <Locker.h> 114 #include <MediaNode.h> 115 #include <String.h> 116 117 class BTimeSource; 118 119 #include "cortex_defs.h" 120 121 #if CORTEX_XML 122 #include "IPersistent.h" 123 #endif 124 125 __BEGIN_CORTEX_NAMESPACE 126 127 class NodeManager; 128 class NodeRef; 129 130 class GroupCycleThread; 131 132 class NodeGroup : 133 public ObservableHandler, 134 public ILockable { 135 136 typedef ObservableHandler _inherited; 137 138 friend class NodeManager; 139 friend class NodeRef; 140 141 public: // *** messages 142 enum message_t { 143 // groupID: int32 144 // target: BMessenger 145 M_OBSERVER_ADDED =NodeGroup_message_base, 146 M_OBSERVER_REMOVED, 147 M_RELEASED, 148 149 // groupID: int32 150 // nodeID: int32 151 M_NODE_ADDED, 152 M_NODE_REMOVED, 153 154 // groupID: int32 155 // transportState: int32 156 // ++++++ include the following? 157 // runMode: int32 158 // [mediaStart: bigtime_t] only sent if changed 159 // [mediaEnd: bigtime_t] only sent if changed 160 M_TRANSPORT_STATE_CHANGED, 161 162 // groupID: int32 163 // timeSourceID: int32 +++++ should be a node? 164 M_TIME_SOURCE_CHANGED, 165 166 // groupID: int32 167 // runMode: int32 168 M_RUN_MODE_CHANGED, 169 170 // Set a new time source: 171 // timeSourceNode: media_node 172 M_SET_TIME_SOURCE, 173 174 // Set a run mode 175 // runMode: int32 (must be a valid run mode -- not 0!) 176 M_SET_RUN_MODE, 177 178 // Set new start/end position: 179 // position: bigtime_t (int64) 180 M_SET_START_POSITION, //K 181 M_SET_END_POSITION, //L 182 183 // Transport controls: 184 M_PREROLL, 185 M_START, 186 M_STOP, 187 M_ROLL // [e.moon 11oct99] 188 }; 189 190 public: // *** types 191 192 // transport state 193 enum transport_state_t { 194 TRANSPORT_INVALID, 195 TRANSPORT_STOPPED, 196 TRANSPORT_STARTING, 197 TRANSPORT_RUNNING, 198 TRANSPORT_ROLLING, // [e.moon 11oct99] 199 TRANSPORT_STOPPING 200 }; 201 202 // [em 1feb00] flags 203 enum flag_t { 204 // no new nodes may be added 205 GROUP_LOCKED = 1 206 }; 207 208 public: // *** ctor/dtor 209 210 // free the group, including all nodes within it 211 // (this call will result in the eventual deletion of the object.) 212 // returns B_OK on success; B_NOT_ALLOWED if release() has 213 // already been called; other error codes if the Media Roster 214 // call fails. 215 216 status_t release(); 217 218 // call release() rather than deleting NodeGroup objects 219 virtual ~NodeGroup(); 220 221 public: // *** const accessors 222 // [e.moon 13oct99] moved method definition here to keep inline 223 // in the face of a balky PPC compiler id()224 inline uint32 id() const { return m_id; } 225 226 public: // *** content accessors 227 228 // name access 229 const char* name() const; 230 status_t setName(const char* name); 231 232 // node access 233 // - you can write-lock the group during sets of calls to these methods; 234 // this ensures that the node set won't change. The methods do lock 235 // the group internally, so locking isn't explicitly required. 236 uint32 countNodes() const; 237 NodeRef* nodeAt( 238 uint32 index) const; 239 240 // add/remove nodes: 241 // - you may only add a node with no current group. 242 // - nodes added during playback will be started; 243 // nodes removed during playback will be stopped (unless 244 // the NO_START_STOP transport restriction flag is set 245 // for a given node.) 246 247 status_t addNode( 248 NodeRef* node); 249 250 status_t removeNode( 251 NodeRef* node); 252 253 status_t removeNode( 254 uint32 index); 255 256 // group flag access 257 258 uint32 groupFlags() const; 259 260 status_t setGroupFlags( 261 uint32 flags); 262 263 // returns true if one or more nodes in the group have cycling 264 // enabled, and the start- and end-positions are valid 265 bool canCycle() const; 266 267 public: // *** TRANSPORT POSITIONING 268 269 // Fetch the current transport state 270 271 transport_state_t transportState() const; 272 273 // Set the starting media time: 274 // This is the point at which playback will begin in any media 275 // files/documents being played by the nodes in this group. 276 // When cycle mode is enabled, this is the point to which each 277 // node will be seek'd at the end of each cycle (loop). 278 // 279 // The starting time can't be changed in the B_OFFLINE run mode 280 // (this call will return an error.) 281 282 status_t setStartPosition( 283 bigtime_t start); //nyi 284 285 // Fetch the starting position: 286 287 bigtime_t startPosition() const; //nyi 288 289 // Set the ending media time: 290 // This is the point at which playback will end relative to 291 // media documents begin played by the nodes in this group; 292 // in cycle mode, this specifies the loop point. If the 293 // ending time is less than or equal to the starting time, 294 // the transport will continue until stopped manually. 295 // If the end position is changed while the transport is playing, 296 // it must take effect retroactively (if it's before the current 297 // position and looping is enabled, all nodes must 'warp' to 298 // the proper post-loop position.) +++++ echk! 299 // 300 // The ending time can't be changed if run mode is B_OFFLINE and 301 // the transport is running (this call will return an error.) 302 303 status_t setEndPosition( 304 bigtime_t end); //nyi 305 306 // Fetch the end position: 307 // Note that if the end position is less than or equal to the start 308 // position, it's ignored. 309 310 bigtime_t endPosition() const; //nyi 311 312 public: // *** TRANSPORT OPERATIONS 313 314 // Preroll the group: 315 // Seeks, then prerolls, each node in the group (honoring the 316 // NO_SEEK and NO_PREROLL flags.) This ensures that the group 317 // can start as quickly as possible. 318 // 319 // Returns B_NOT_ALLOWED if the transport is running. 320 321 status_t preroll(); 322 323 // Start all nodes in the group: 324 // Nodes with the NO_START_STOP flag aren't molested. 325 326 status_t start(); 327 328 // Stop all nodes in the group: 329 // Nodes with the NO_START_STOP flag aren't molested. 330 331 status_t stop(); 332 333 // Roll all nodes in the group: 334 // Queues a start and stop atomically (via BMediaRoster::RollNode()). 335 // Returns B_NOT_ALLOWED if endPosition <= startPosition. 336 337 status_t roll(); 338 339 public: // *** TIME SOURCE & RUN-MODE OPERATIONS 340 341 // getTimeSource(): 342 // returns B_ERROR if no time source has been set; otherwise, 343 // returns the node ID of the current time source for all 344 // nodes in the group. 345 // 346 // setTimeSource(): 347 // Calls SetTimeSourceFor() on every node in the group. 348 // The group must be stopped; B_NOT_ALLOWED will be returned 349 // if the state is TRANSPORT_RUNNING or TRANSPORT_ROLLING. 350 351 status_t getTimeSource( 352 media_node* outTimeSource) const; 353 354 status_t setTimeSource( 355 const media_node& timeSource); 356 357 // run mode access: 358 // Sets the default run mode for the group. This will be 359 // applied to every node with a wildcard (0) run mode. 360 // 361 // Special case: if the run mode is B_OFFLINE, it will be 362 // applied to all nodes in the group. 363 364 status_t setRunMode( 365 BMediaNode::run_mode mode); //nyi 366 367 BMediaNode::run_mode runMode() const; //nyi 368 369 public: // *** BHandler 370 virtual void MessageReceived( 371 BMessage* message); 372 373 #if CORTEX_XML 374 public: // *** IPersistent 375 // +++++ 376 377 // Default constructor 378 NodeGroup(); 379 380 #endif /*CORTEX_XML*/ 381 382 public: // *** IObservable: [19aug99] 383 virtual void observerAdded( 384 const BMessenger& observer); 385 386 virtual void observerRemoved( 387 const BMessenger& observer); 388 389 virtual void notifyRelease(); 390 391 virtual void releaseComplete(); 392 393 public: // *** ILockable: [21jul99] 394 // Each NodeGroup has a semaphore (BLocker). 395 // Only WRITE locking is allowed! 396 397 bool lock( 398 lock_t type=WRITE, 399 bigtime_t timeout=B_INFINITE_TIMEOUT); 400 bool unlock( 401 lock_t type=WRITE); 402 bool isLocked( 403 lock_t type=WRITE) const; 404 405 protected: // *** ctor (accessible to NodeManager) 406 NodeGroup( 407 const char* name, 408 NodeManager* manager, 409 BMediaNode::run_mode runMode=BMediaNode::B_INCREASE_LATENCY); 410 411 protected: // *** internal operations 412 413 static uint32 NextID(); 414 415 protected: // *** ref->group communication (LOCK REQUIRED) 416 417 // When a NodeRef's cycle state (ie. looping or not looping) 418 // changes, it must pass that information on via this method. 419 // +++++ group cycle thread 420 void _refCycleChanged( 421 NodeRef* ref); 422 423 // when a cycling node's latency changes, call this method. 424 // +++++ shouldn't there be a general latency-change hook? 425 void _refLatencyChanged( 426 NodeRef* ref); 427 428 // when a NodeRef receives notification that it has been stopped, 429 // but is labeled as still running, it must call this method. 430 // [e.moon 11oct99: roll/B_OFFLINE support] 431 void _refStopped( 432 NodeRef* ref); 433 434 private: // *** transport helpers (LOCK REQUIRED) 435 436 // Preroll all nodes in the group; this is the implementation 437 // of preroll(). 438 // *** this method should not be called from the transport thread 439 // (since preroll operations can block for a relatively long time.) 440 441 status_t _preroll(); 442 443 // Start all nodes in the group; this is the implementation of 444 // start(). 445 // 446 // (this may be called from the transport thread or from 447 // an API-implementation method.) 448 449 status_t _start(); 450 451 // Stop all nodes in the group; this is the implementation of 452 // stop(). Fails if the run mode is B_OFFLINE; use _roll() instead 453 // in that case. 454 // 455 // (this may be called from the transport thread or from 456 // an API-implementation method.) 457 458 status_t _stop(); 459 460 // Roll all nodes in the group; this is the implementation of 461 // roll(). 462 // 463 // (this may be called from the transport thread or from 464 // an API-implementation method.) 465 466 status_t _roll(); //nyi [11oct99 e.moon] 467 468 // State transition; notify listeners 469 inline void _changeState( 470 transport_state_t to); 471 472 // Enforce a state transition, and notify listeners 473 inline void _changeState( 474 transport_state_t from, 475 transport_state_t to); 476 477 478 private: // *** transport thread guts 479 // void _initPort(); 480 // void _initThread(); 481 // 482 // static status_t _TransportThread(void* user); 483 // void _transportThread(); 484 485 // functor: calculates latency of each node it's handed, caching 486 // the largest one found; includes initial latency if nodes report it. 487 class calcLatencyFn { public: 488 bigtime_t& maxLatency; calcLatencyFn(bigtime_t & _m)489 calcLatencyFn(bigtime_t& _m) : maxLatency(_m) {} operator()490 void operator()(NodeRef* r) { 491 ASSERT(r); 492 if(!(r->node().kind & B_BUFFER_PRODUCER)) { 493 // node can't incur latency 494 return; 495 } 496 497 bigtime_t latency; 498 status_t err = 499 BMediaRoster::Roster()->GetLatencyFor( 500 r->node(), 501 &latency); 502 if(err < B_OK) { 503 PRINT(( 504 "* calcLatencyFn: GetLatencyFor() failed: %s\n", 505 strerror(err))); 506 return; 507 } 508 bigtime_t add; 509 err = BMediaRoster::Roster()->GetInitialLatencyFor( 510 r->node(), 511 &add); 512 if(err < B_OK) { 513 PRINT(( 514 "* calcLatencyFn: GetInitialLatencyFor() failed: %s\n", 515 strerror(err))); 516 } 517 else 518 latency += add; 519 if(latency > maxLatency) 520 maxLatency = latency; 521 } 522 }; 523 524 friend class calcLatencyFn; 525 526 protected: // *** cycle thread & helpers (LOCK REQUIRED) 527 528 // set up the cycle thread (including its kernel port) 529 status_t _initCycleThread(); 530 531 // shut down the cycle thread/port 532 status_t _destroyCycleThread(); 533 534 // 1) do the current positions specify a valid cycle region? 535 // 2) are any nodes in the group cycle-enabled? 536 bool _cycleValid(); 537 538 // initialize the next cycle 539 void _cycleInit( 540 bigtime_t startTime); 541 542 // add a ref to the cycle set (in proper order, based on latency) 543 void _cycleAddRef( 544 NodeRef* ref); 545 546 // remove a ref from the cycle set 547 void _cycleRemoveRef( 548 NodeRef* ref); 549 550 // fetches the next cycle boundary (performance time of next loop) 551 bigtime_t _cycleBoundary() const; 552 553 // cycle thread impl. 554 static status_t _CycleThread(void* user); 555 void _cycleThread(); 556 557 // cycle service: seek all nodes & initiate next cycle 558 void _handleCycleService(); 559 560 private: // *** members 561 562 // lock 563 mutable BLocker m_lock; 564 565 // parent object 566 NodeManager* m_manager; 567 568 // unique group ID (non-0) 569 const uint32 m_id; 570 static uint32 s_nextID; 571 572 // group name 573 BString m_name; 574 575 // group contents 576 typedef std::vector<NodeRef*> node_set; 577 node_set m_nodes; 578 579 // flags & state 580 uint32 m_flags; 581 transport_state_t m_transportState; 582 583 // default run mode applied to all nodes with a wildcard (0) 584 // run mode. 585 BMediaNode::run_mode m_runMode; 586 587 // current time source 588 media_node m_timeSource; 589 BTimeSource* m_timeSourceObj; 590 591 // slated to die? 592 bool m_released; 593 594 // --------------------------- 595 // 10aug99 596 // cycle thread implementation 597 // --------------------------- 598 599 enum cycle_thread_msg_t { 600 _CYCLE_STOP, 601 _CYCLE_END_CHANGED, 602 _CYCLE_LATENCY_CHANGED 603 }; 604 605 thread_id m_cycleThread; 606 port_id m_cyclePort; 607 bool m_cycleThreadDone; 608 609 // when did the current cycle begin? 610 bigtime_t m_cycleStart; 611 612 // performance time at which the current cycle settings will 613 // be applied (ie. the first seek should be queued) 614 bigtime_t m_cycleDeadline; 615 616 // performance time at which the next cycle begins 617 bigtime_t m_cycleBoundary; 618 619 // the set of nodes currently in cycle mode 620 // ordered by latency (largest first) 621 node_set m_cycleNodes; 622 623 // count of nodes that have completed this cycle 624 // (once complete, deadline and boundary are reset, and any 625 // deferred start/end positions are applied.) 626 uint32 m_cycleNodesComplete; 627 628 // max latency of any cycling node 629 bigtime_t m_cycleMaxLatency; 630 631 // the minimum allowed loop time 632 static const bigtime_t s_minCyclePeriod = 1000LL; 633 634 // the amount of time to allow for Media Roster calls 635 // (StartNode, SeekNode, etc) to be handled 636 static const bigtime_t s_rosterLatency = 1000LL; // +++++ probably high 637 638 // position state 639 volatile bigtime_t m_startPosition; 640 volatile bigtime_t m_endPosition; 641 642 // changed positions deferred in cycle mode 643 volatile bool m_newStart; 644 volatile bigtime_t m_newStartPosition; 645 volatile bool m_newEnd; 646 volatile bigtime_t m_newEndPosition; 647 }; 648 649 650 __END_CORTEX_NAMESPACE 651 #endif /*__NodeGroup_H__*/ 652