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