repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / cortex / NodeManager / NodeGroup.h
blob0c08f0a20cddacba8b8d8670050fc60a4cf8ba30
1 /*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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)
34 // * PURPOSE
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 +++++
41 // 22jul99
42 // ----------------------------
43 // +++++
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 // ----------------------------
59 // 14jul99
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.
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
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.)
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.)
95 // * HISTORY
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"
103 #include "observe.h"
104 #include "ILockable.h"
106 // +++++ [e.moon 3dec99] need to include these for calcLatencyFn impl
107 // +++++ YUCK
108 #include "NodeRef.h"
109 #include <MediaRoster.h>
111 #include <vector>
113 #include <Locker.h>
114 #include <MediaNode.h>
115 #include <String.h>
117 class BTimeSource;
119 #include "cortex_defs.h"
121 #if CORTEX_XML
122 #include "IPersistent.h"
123 #endif
125 __BEGIN_CORTEX_NAMESPACE
127 class NodeManager;
128 class NodeRef;
130 class GroupCycleThread;
132 class NodeGroup :
133 public ObservableHandler,
134 public ILockable {
136 typedef ObservableHandler _inherited;
138 friend class NodeManager;
139 friend class NodeRef;
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,
149 // groupID: int32
150 // nodeID: int32
151 M_NODE_ADDED,
152 M_NODE_REMOVED,
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,
162 // groupID: int32
163 // timeSourceID: int32 +++++ should be a node?
164 M_TIME_SOURCE_CHANGED,
166 // groupID: int32
167 // runMode: int32
168 M_RUN_MODE_CHANGED,
170 // Set a new time source:
171 // timeSourceNode: media_node
172 M_SET_TIME_SOURCE,
174 // Set a run mode
175 // runMode: int32 (must be a valid run mode -- not 0!)
176 M_SET_RUN_MODE,
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:
184 M_PREROLL,
185 M_START,
186 M_STOP,
187 M_ROLL // [e.moon 11oct99]
190 public: // *** types
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
202 // [em 1feb00] flags
203 enum flag_t {
204 // no new nodes may be added
205 GROUP_LOCKED = 1
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
214 // call fails.
216 status_t release();
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
228 // name access
229 const char* name() const;
230 status_t setName(const char* name);
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;
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.)
247 status_t addNode(
248 NodeRef* node);
250 status_t removeNode(
251 NodeRef* node);
253 status_t removeNode(
254 uint32 index);
256 // group flag access
258 uint32 groupFlags() const;
260 status_t setGroupFlags(
261 uint32 flags);
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.
321 status_t preroll();
323 // Start all nodes in the group:
324 // Nodes with the NO_START_STOP flag aren't molested.
326 status_t start();
328 // Stop all nodes in the group:
329 // Nodes with the NO_START_STOP flag aren't molested.
331 status_t stop();
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.
337 status_t roll();
339 public: // *** TIME SOURCE & RUN-MODE OPERATIONS
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.
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.
351 status_t getTimeSource(
352 media_node* outTimeSource) const;
354 status_t setTimeSource(
355 const media_node& timeSource);
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.
361 // Special case: if the run mode is B_OFFLINE, it will be
362 // applied to all nodes in the group.
364 status_t setRunMode(
365 BMediaNode::run_mode mode); //nyi
367 BMediaNode::run_mode runMode() const; //nyi
369 public: // *** BHandler
370 virtual void MessageReceived(
371 BMessage* message);
373 #if CORTEX_XML
374 public: // *** IPersistent
375 // +++++
377 // Default constructor
378 NodeGroup();
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!
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;
405 protected: // *** ctor (accessible to NodeManager)
406 NodeGroup(
407 const char* name,
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(
421 NodeRef* ref);
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);
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);
434 private: // *** transport helpers (LOCK REQUIRED)
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.)
441 status_t _preroll();
443 // Start all nodes in the group; this is the implementation of
444 // start().
446 // (this may be called from the transport thread or from
447 // an API-implementation method.)
449 status_t _start();
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.
455 // (this may be called from the transport thread or from
456 // an API-implementation method.)
458 status_t _stop();
460 // Roll all nodes in the group; this is the implementation of
461 // roll().
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
479 // void _initPort();
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) {
491 ASSERT(r);
492 if(!(r->node().kind & B_BUFFER_PRODUCER)) {
493 // node can't incur latency
494 return;
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;
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)));
517 else
518 latency += add;
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?
536 bool _cycleValid();
538 // initialize the next cycle
539 void _cycleInit(
540 bigtime_t startTime);
542 // add a ref to the cycle set (in proper order, based on latency)
543 void _cycleAddRef(
544 NodeRef* ref);
546 // remove a ref from the cycle set
547 void _cycleRemoveRef(
548 NodeRef* ref);
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);
555 void _cycleThread();
557 // cycle service: seek all nodes & initiate next cycle
558 void _handleCycleService();
560 private: // *** members
562 // lock
563 mutable BLocker m_lock;
565 // parent object
566 NodeManager* m_manager;
568 // unique group ID (non-0)
569 const uint32 m_id;
570 static uint32 s_nextID;
572 // group name
573 BString m_name;
575 // group contents
576 typedef std::vector<NodeRef*> node_set;
577 node_set m_nodes;
579 // flags & state
580 uint32 m_flags;
581 transport_state_t m_transportState;
583 // default run mode applied to all nodes with a wildcard (0)
584 // run mode.
585 BMediaNode::run_mode m_runMode;
587 // current time source
588 media_node m_timeSource;
589 BTimeSource* m_timeSourceObj;
591 // slated to die?
592 bool m_released;
594 // ---------------------------
595 // 10aug99
596 // cycle thread implementation
597 // ---------------------------
599 enum cycle_thread_msg_t {
600 _CYCLE_STOP,
601 _CYCLE_END_CHANGED,
602 _CYCLE_LATENCY_CHANGED
605 thread_id m_cycleThread;
606 port_id m_cyclePort;
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
638 // position state
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__*/