xref: /haiku/src/apps/cortex/NodeManager/NodeGroup.h (revision 19ae20e67e91fc09cc9fc5c0e60e21e24e7a53eb)
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