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 // NodeGroup.h (Cortex/NodeManager)
35 // Represents a logical group of media kit nodes.
36 // Provides group-level operations (transport control
37 // and serialization, for example.)
39 // * NODE SYNC/LOOPING +++++
42 // ----------------------------
44 // - cycling support needs to be thoroughly defined; ie. what
45 // can it do, and what can it NOT do.
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.
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.]
58 // ----------------------------
61 // How about handling addition of a node to a group while it's
62 // playing? The "effects insert" scenario:
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.
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
83 // (pad is the estimated amount of time taken to stop, break
84 // & make connections, etc. It can probably be determined in
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.)
96 // e.moon 29sep99 Made thread control variables 'volatile'.
97 // e.moon 6jul99 Begun
99 #ifndef __NodeGroup_H__
100 #define __NodeGroup_H__
102 #include "ObservableHandler.h"
104 #include "ILockable.h"
106 // +++++ [e.moon 3dec99] need to include these for calcLatencyFn impl
109 #include <MediaRoster.h>
114 #include <MediaNode.h>
119 #include "cortex_defs.h"
122 #include "IPersistent.h"
125 __BEGIN_CORTEX_NAMESPACE
130 class GroupCycleThread
;
133 public ObservableHandler
,
136 typedef ObservableHandler _inherited
;
138 friend class NodeManager
;
139 friend class NodeRef
;
141 public: // *** messages
144 // target: BMessenger
145 M_OBSERVER_ADDED
=NodeGroup_message_base
,
155 // transportState: int32
156 // ++++++ include the following?
158 // [mediaStart: bigtime_t] only sent if changed
159 // [mediaEnd: bigtime_t] only sent if changed
160 M_TRANSPORT_STATE_CHANGED
,
163 // timeSourceID: int32 +++++ should be a node?
164 M_TIME_SOURCE_CHANGED
,
170 // Set a new time source:
171 // timeSourceNode: media_node
175 // runMode: int32 (must be a valid run mode -- not 0!)
178 // Set new start/end position:
179 // position: bigtime_t (int64)
180 M_SET_START_POSITION
, //K
181 M_SET_END_POSITION
, //L
183 // Transport controls:
187 M_ROLL
// [e.moon 11oct99]
193 enum transport_state_t
{
198 TRANSPORT_ROLLING
, // [e.moon 11oct99]
204 // no new nodes may be added
208 public: // *** ctor/dtor
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
218 // call release() rather than deleting NodeGroup objects
219 virtual ~NodeGroup();
221 public: // *** const accessors
222 // [e.moon 13oct99] moved method definition here to keep inline
223 // in the face of a balky PPC compiler
224 inline uint32
id() const { return m_id
; }
226 public: // *** content accessors
229 const char* name() const;
230 status_t
setName(const char* name
);
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;
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.)
258 uint32
groupFlags() const;
260 status_t
setGroupFlags(
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;
267 public: // *** TRANSPORT POSITIONING
269 // Fetch the current transport state
271 transport_state_t
transportState() const;
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).
279 // The starting time can't be changed in the B_OFFLINE run mode
280 // (this call will return an error.)
282 status_t
setStartPosition(
283 bigtime_t start
); //nyi
285 // Fetch the starting position:
287 bigtime_t
startPosition() const; //nyi
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!
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.)
303 status_t
setEndPosition(
304 bigtime_t end
); //nyi
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.
310 bigtime_t
endPosition() const; //nyi
312 public: // *** TRANSPORT OPERATIONS
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.
319 // Returns B_NOT_ALLOWED if the transport is running.
323 // Start all nodes in the group:
324 // Nodes with the NO_START_STOP flag aren't molested.
328 // Stop all nodes in the group:
329 // Nodes with the NO_START_STOP flag aren't molested.
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.
339 public: // *** TIME SOURCE & RUN-MODE OPERATIONS
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.
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.
351 status_t
getTimeSource(
352 media_node
* outTimeSource
) const;
354 status_t
setTimeSource(
355 const media_node
& timeSource
);
358 // Sets the default run mode for the group. This will be
359 // applied to every node with a wildcard (0) run mode.
361 // Special case: if the run mode is B_OFFLINE, it will be
362 // applied to all nodes in the group.
365 BMediaNode::run_mode mode
); //nyi
367 BMediaNode::run_mode
runMode() const; //nyi
369 public: // *** BHandler
370 virtual void MessageReceived(
374 public: // *** IPersistent
377 // Default constructor
380 #endif /*CORTEX_XML*/
382 public: // *** IObservable: [19aug99]
383 virtual void observerAdded(
384 const BMessenger
& observer
);
386 virtual void observerRemoved(
387 const BMessenger
& observer
);
389 virtual void notifyRelease();
391 virtual void releaseComplete();
393 public: // *** ILockable: [21jul99]
394 // Each NodeGroup has a semaphore (BLocker).
395 // Only WRITE locking is allowed!
399 bigtime_t timeout
=B_INFINITE_TIMEOUT
);
403 lock_t type
=WRITE
) const;
405 protected: // *** ctor (accessible to NodeManager)
408 NodeManager
* manager
,
409 BMediaNode::run_mode runMode
=BMediaNode::B_INCREASE_LATENCY
);
411 protected: // *** internal operations
413 static uint32
NextID();
415 protected: // *** ref->group communication (LOCK REQUIRED)
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(
423 // when a cycling node's latency changes, call this method.
424 // +++++ shouldn't there be a general latency-change hook?
425 void _refLatencyChanged(
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]
434 private: // *** transport helpers (LOCK REQUIRED)
436 // Preroll all nodes in the group; this is the implementation
438 // *** this method should not be called from the transport thread
439 // (since preroll operations can block for a relatively long time.)
443 // Start all nodes in the group; this is the implementation of
446 // (this may be called from the transport thread or from
447 // an API-implementation method.)
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
455 // (this may be called from the transport thread or from
456 // an API-implementation method.)
460 // Roll all nodes in the group; this is the implementation of
463 // (this may be called from the transport thread or from
464 // an API-implementation method.)
466 status_t
_roll(); //nyi [11oct99 e.moon]
468 // State transition; notify listeners
469 inline void _changeState(
470 transport_state_t to
);
472 // Enforce a state transition, and notify listeners
473 inline void _changeState(
474 transport_state_t from
,
475 transport_state_t to
);
478 private: // *** transport thread guts
480 // void _initThread();
482 // static status_t _TransportThread(void* user);
483 // void _transportThread();
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
;
489 calcLatencyFn(bigtime_t
& _m
) : maxLatency(_m
) {}
490 void operator()(NodeRef
* r
) {
492 if(!(r
->node().kind
& B_BUFFER_PRODUCER
)) {
493 // node can't incur latency
499 BMediaRoster::Roster()->GetLatencyFor(
504 "* calcLatencyFn: GetLatencyFor() failed: %s\n",
509 err
= BMediaRoster::Roster()->GetInitialLatencyFor(
514 "* calcLatencyFn: GetInitialLatencyFor() failed: %s\n",
519 if(latency
> maxLatency
)
520 maxLatency
= latency
;
524 friend class calcLatencyFn
;
526 protected: // *** cycle thread & helpers (LOCK REQUIRED)
528 // set up the cycle thread (including its kernel port)
529 status_t
_initCycleThread();
531 // shut down the cycle thread/port
532 status_t
_destroyCycleThread();
534 // 1) do the current positions specify a valid cycle region?
535 // 2) are any nodes in the group cycle-enabled?
538 // initialize the next cycle
540 bigtime_t startTime
);
542 // add a ref to the cycle set (in proper order, based on latency)
546 // remove a ref from the cycle set
547 void _cycleRemoveRef(
550 // fetches the next cycle boundary (performance time of next loop)
551 bigtime_t
_cycleBoundary() const;
553 // cycle thread impl.
554 static status_t
_CycleThread(void* user
);
557 // cycle service: seek all nodes & initiate next cycle
558 void _handleCycleService();
560 private: // *** members
563 mutable BLocker m_lock
;
566 NodeManager
* m_manager
;
568 // unique group ID (non-0)
570 static uint32 s_nextID
;
576 typedef std::vector
<NodeRef
*> node_set
;
581 transport_state_t m_transportState
;
583 // default run mode applied to all nodes with a wildcard (0)
585 BMediaNode::run_mode m_runMode
;
587 // current time source
588 media_node m_timeSource
;
589 BTimeSource
* m_timeSourceObj
;
594 // ---------------------------
596 // cycle thread implementation
597 // ---------------------------
599 enum cycle_thread_msg_t
{
602 _CYCLE_LATENCY_CHANGED
605 thread_id m_cycleThread
;
607 bool m_cycleThreadDone
;
609 // when did the current cycle begin?
610 bigtime_t m_cycleStart
;
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
;
616 // performance time at which the next cycle begins
617 bigtime_t m_cycleBoundary
;
619 // the set of nodes currently in cycle mode
620 // ordered by latency (largest first)
621 node_set m_cycleNodes
;
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
;
628 // max latency of any cycling node
629 bigtime_t m_cycleMaxLatency
;
631 // the minimum allowed loop time
632 static const bigtime_t s_minCyclePeriod
= 1000LL;
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
639 volatile bigtime_t m_startPosition
;
640 volatile bigtime_t m_endPosition
;
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
;
650 __END_CORTEX_NAMESPACE
651 #endif /*__NodeGroup_H__*/