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// NodeRef.h (Cortex/NodeManager)
33//
34// * PURPOSE
35//   Represents a NodeManager reference to a live Media Kit Node.
36//
37// * FEATURES UNDER CONSIDERATION +++++
38//   - lazy referencing: m_released becomes m_referenced; only reference
39//     externally created nodes once they've been grouped or connected.
40//     +++++ or disconnected? keep a'thinkin...
41//   - expose dormant_node_info
42//
43// * HISTORY
44//   e.moon		11aug99		Added kind().
45//   e.moon		9aug99		Moved position & cycle threads into NodeRef;
46//											no more 'group transport thread'.
47//   e.moon		25jun99		Begun
48
49#ifndef __NodeRef_H__
50#define __NodeRef_H__
51
52#include <MediaAddOn.h>
53#include <MediaNode.h>
54#include <String.h>
55
56#include <vector>
57
58#include "ILockable.h"
59#include "MultiInvoker.h"
60#include "ObservableHandler.h"
61#include "observe.h"
62
63#include "cortex_defs.h"
64
65__BEGIN_CORTEX_NAMESPACE
66
67class Connection;
68class NodeManager;
69class NodeGroup;
70class NodeSyncThread;
71
72class NodeRef :
73	public	ObservableHandler,
74	protected	ILockable {
75
76	typedef	ObservableHandler _inherited;
77
78	friend class NodeManager;
79	friend class NodeGroup;
80
81public:				// *** messages
82	enum message_t {
83		// OUTBOUND
84		//  nodeID: int32
85		//  target: BMessenger
86		M_OBSERVER_ADDED			=NodeRef_message_base,
87		M_OBSERVER_REMOVED,
88		M_RELEASED,
89
90		// OUTBOUND
91		// nodeID: int32
92		// groupID: int32
93		M_GROUP_CHANGED,
94
95		// nodeID: int32
96		// runMode: int32
97		M_RUN_MODE_CHANGED,
98
99		// +++++
100		M_INPUTS_CHANGED, //nyi
101		M_OUTPUTS_CHANGED,  //nyi
102
103		// OUTBOUND
104		//  * only sent to position listeners
105		//  nodeID:	         int32
106		//  when:      		   int64
107		//  position:        int64
108		M_POSITION,
109
110		// INBOUND
111		// runMode: int32
112		// delay: int64 (bigtime_t; applies only to B_RECORDING mode)
113		M_SET_RUN_MODE,
114
115		// INBOUND
116		M_PREROLL,
117
118		// INBOUND
119		// "cycling"; bool (OR "be:value"; int32)
120		M_SET_CYCLING,
121
122		// OUTBOUND
123		// "cycling"; bool
124		M_CYCLING_CHANGED
125	};
126
127public:				// *** flags
128	enum flag_t {
129		// node-level transport restrictions
130		NO_START_STOP						= 1<<1,
131		NO_SEEK									= 1<<2,
132		NO_PREROLL							= 1<<3,
133
134		// [e.moon 28sep99] useful for misbehaved nodes
135		NO_STOP									= 1<<4,
136
137		// [e.moon 11oct99]
138		// Disables media-roster connection (which currently
139		// only makes use of B_MEDIA_NODE_STOPPED.)
140		// This flag may become deprecated as the Media Kit
141		// evolves (ie. more node-level notifications come
142		// along.)
143		NO_ROSTER_WATCH					= 1<<5,
144
145		// [e.moon 14oct99]
146		// Disables position reporting (via BMediaRoster::SyncToNode().)
147		// Some system-provided nodes tend to explode when SyncToNode() is
148		// called on them (like the video-file player in BeOS R4.5.2).
149		NO_POSITION_REPORTING		= 1<<6
150	};
151
152public:				// *** dtor
153
154	// free the node (this call will result in the eventual
155	// deletion of the object.)
156	// returns B_OK on success; B_NOT_ALLOWED if release() has
157	// already been called; other error codes if the Media Roster
158	// call fails.
159
160	status_t release();
161
162	// call release() rather than deleting NodeRef objects
163	virtual ~NodeRef();
164
165public:				// *** const accessors
166	// [e.moon 13oct99] moved method definitions here to keep inline
167	// in the face of a balky PPC compiler
168	inline const media_node& node() const { return m_info.node; }
169	inline uint32 kind() const { return m_info.node.kind; }
170	inline const live_node_info& nodeInfo() const { return m_info; }
171	inline const char* name() const { return m_info.name; }
172	inline media_node_id id() const { return m_info.node.node; }
173
174public:				// *** member access
175
176	// turn cycle mode (looping) on or off
177	void setCycling(
178		bool												cycle);
179	bool isCycling() const;
180
181	// is the node running?
182	bool isRunning() const;
183
184	// was the node created via NodeManager::instantiate()?
185	bool isInternal() const;
186
187	// fetch the group, or 0 if the node is ungrouped.
188	NodeGroup* group() const;
189
190	// flag access
191	uint32 flags() const;
192	void setFlags(
193		uint32											flags);
194
195	// [e.moon 29sep99]
196	// access addon-hint info
197	// - returns B_BAD_VALUE if not an add-on node created by this NodeManager
198
199	status_t getDormantNodeInfo(
200		dormant_node_info*					outInfo);
201
202	// [e.moon 29sep99]
203	// access file being played
204	// - returns B_BAD_VALUE if not an add-on node created by this NodeManager,
205	//   or if the node has no associated file
206
207	status_t getFile(
208		entry_ref*									outFile);
209
210	// [e.moon 8dec99]
211	// set file to play
212
213	status_t setFile(
214		const entry_ref&						file,
215		bigtime_t*									outDuration=0); //nyi
216
217	// [e.moon 23oct99]
218	// returns true if the media_node has been released (call releaseNode() to
219	// make this happen.)
220
221	bool isNodeReleased() const;
222
223//		now implemented by ObservableHandler [20aug99]
224//	// has this reference been released?
225//	bool isReleased() const;
226
227public:				// *** run-mode operations
228	void setRunMode(
229		uint32											runMode,
230		bigtime_t										delay=0LL);
231	uint32 runMode() const;
232
233	bigtime_t recordingDelay() const;
234
235	// calculates the minimum amount of delay needed for
236	// B_RECORDING mode
237	// +++++ 15sep99: returns biggest_output_buffer_duration * 2
238	// +++++ 28sep99: adds downstream latency
239	bigtime_t calculateRecordingModeDelay(); //nyi
240
241public:				// *** connection access
242
243	// connection access: vector versions
244
245	status_t getInputConnections(
246		std::vector<Connection>&	ioConnections,
247		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
248
249	status_t getOutputConnections(
250		std::vector<Connection>&	ioConnections,
251		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
252
253	// connection access: flat array versions
254
255	status_t getInputConnections(
256		Connection*									outConnections,
257		int32												maxConnections,
258		int32*											outNumConnections,
259		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
260
261	status_t getOutputConnections(
262		Connection*									outConnections,
263		int32												maxConnections,
264		int32*											outNumConnections,
265		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
266
267	// +++++ connection matching by source/destination +++++
268
269public:				// *** position reporting/listening
270
271	bool positionReportsEnabled() const;
272
273	void enablePositionReports();
274	void disablePositionReports();
275
276	// Fetch the approximate current position:
277	//   Returns the last reported position, and the
278	//   performance time at which that position was reached.  If the
279	//   transport has never been started, the start position and
280	//   a performance time of 0 will be returned.  If position updating
281	//   isn't currently enabled, returns B_NOT_ALLOWED.
282
283	status_t getLastPosition(
284		bigtime_t*									outPosition,
285		bigtime_t*									outPerfTime) const;
286
287	// Subscribe to regular position reports:
288	//   Position reporting isn't rolled into the regular observer
289	//   interface because a large number of messages are generated
290	//   (the frequency can be changed; see below.)
291
292	status_t addPositionObserver(
293		BHandler*										handler);
294
295	status_t removePositionObserver(
296		BHandler*										handler);
297
298	// Set how often position updates will be sent:
299	//   Realistically, period should be > 10000 or so.
300
301	status_t setPositionUpdatePeriod(
302		bigtime_t										period);
303
304	bigtime_t positionUpdatePeriod() const;
305
306public:				// *** BMediaRoster wrappers & convenience methods
307
308	// release the media node
309	// (if allowed, will trigger the release/deletion of this object)
310	status_t releaseNode();
311
312	// calculate total (internal + downstream) latency for this node
313
314	status_t totalLatency(
315		bigtime_t*									outLatency) const;
316
317
318	// retrieve input/output matching the given destination/source.
319	// returns B_MEDIA_BAD_[SOURCE | DESTINATION] if the destination
320	// or source don't correspond to this node.
321
322	status_t findInput(
323		const media_destination&		forDestination,
324		media_input*								outInput) const;
325
326	status_t findOutput(
327		const media_source&					forSource,
328		media_output*								outOutput) const;
329
330
331	// endpoint matching (given name and/or format as 'hints').
332	// If no hints are given, returns the first free endpoint, if
333	// any exist.
334	// returns B_ERROR if no matching endpoint is found.
335
336	status_t findFreeInput(
337		media_input*								outInput,
338		media_type									type=B_MEDIA_UNKNOWN_TYPE,
339		const char*									name=0) const;
340
341	status_t findFreeInput(
342		media_input*								outInput,
343		const media_format*					format,
344		const char*									name=0) const;
345
346	status_t findFreeOutput(
347		media_output*								outOutput,
348		media_type									type=B_MEDIA_UNKNOWN_TYPE,
349		const char*									name=0) const;
350
351	status_t findFreeOutput(
352		media_output*								outOutput,
353		const media_format*					format,
354		const char*									name=0) const;
355
356	// node endpoint access: vector versions (wrappers for BMediaRoster
357	// calls.)
358
359	status_t getFreeInputs(
360		std::vector<media_input>&	ioInputs,
361		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
362
363	status_t getConnectedInputs(
364		std::vector<media_input>&	ioInputs,
365		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
366
367	status_t getFreeOutputs(
368		std::vector<media_output>&	ioOutputs,
369		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
370
371	status_t getConnectedOutputs(
372		std::vector<media_output>&	ioOutputs,
373		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
374
375
376	// node endpoint access: array versions (wrappers for BMediaRoster
377	// calls.)
378
379	status_t getFreeInputs(
380		media_input*								outInputs,
381		int32												maxInputs,
382		int32*											outNumInputs,
383		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
384
385	status_t getConnectedInputs(
386		media_input*								outInputs,
387		int32												maxInputs,
388		int32*											outNumInputs) const;
389
390	status_t getFreeOutputs(
391		media_output*								outOutputs,
392		int32												maxOutputs,
393		int32*											outNumOutputs,
394		media_type									filterType=B_MEDIA_UNKNOWN_TYPE) const;
395
396	status_t getConnectedOutputs(
397		media_output*								outOutputs,
398		int32												maxOutputs,
399		int32*											outNumOutputs) const;
400
401public:													// *** BHandler:
402	virtual void MessageReceived(
403		BMessage*								message);
404
405public:				// *** IObservable:		[20aug99]
406	virtual void observerAdded(
407		const BMessenger&				observer);
408
409	virtual void observerRemoved(
410		const BMessenger&				observer);
411
412	virtual void notifyRelease();
413
414	virtual void releaseComplete();
415
416protected:		// *** ILockable
417							//     Only WRITE locking is allowed!
418
419	bool lock(
420		lock_t type=WRITE,
421		bigtime_t timeout=B_INFINITE_TIMEOUT);
422	bool unlock(
423		lock_t type=WRITE);
424	bool isLocked(
425		lock_t type=WRITE) const;
426
427protected:			// *** ctor (accessible to NodeManager)
428	// throws runtime_error
429	NodeRef(
430		const media_node&		node,
431		NodeManager*				manager,
432		uint32							userFlags,
433		uint32							implFlags);
434
435protected:			// *** endpoint-fixing operations (no lock required)
436
437	// 'fix' (fill in node if needed) sets of inputs/outputs
438	void _fixInputs(
439		media_input*									inputs,
440		int32													count) const;
441	void _fixInputs(
442		std::vector<media_input>&		inputs) const;
443
444	void _fixOutputs(
445		media_output*									outputs,
446		int32													count) const;
447	void _fixOutputs(
448		std::vector<media_output>&		outputs) const;
449
450protected:			// *** internal/NodeManager operations (LOCK REQUIRED)
451
452	// call after instantiation to register info used to select and
453	// create this add-on node
454
455	void _setAddonHint(
456		const dormant_node_info*			info,
457		const entry_ref*							file=0);
458
459	// call to set a new group; if 0, the node must have no
460	// connections
461	void _setGroup(
462		NodeGroup*										group);
463
464	// *** NodeGroup API ***
465	//     9aug99: moved from NodeGroup
466	//     [e.moon 13oct99] removed inlines for the sake of PPC sanity
467
468	// initialize the given node's transport-state members
469	// (this may be called from the transport thread or from
470	//  an API-implementation method.)
471
472	status_t _initTransportState();
473
474	status_t _setTimeSource(
475		media_node_id					timeSourceID);
476
477	status_t _setRunMode(
478		const uint32					runMode,
479		bigtime_t							delay=0LL);
480
481	status_t _setRunModeAuto(
482		const uint32					runMode);
483
484	// seek and preroll the given node.
485	// *** this method should not be called from the transport thread
486	// (since preroll operations can block for a relatively long time.)
487	//
488	// returns B_NOT_ALLOWED if the node is running, or if its NO_PREROLL
489	// flag is set; otherwise, returns B_OK on success or a Media Roster
490	// error.
491
492	status_t _preroll(
493		bigtime_t							position);
494
495	// seek the given node if possible
496	// (this may be called from the transport thread or from
497	//  an API-implementation method.)
498
499	status_t _seek(
500		bigtime_t							position,
501		bigtime_t							when);
502
503	// seek the given (stopped) node
504	// (this may be called from the transport thread or from
505	//  an API-implementation method.)
506
507	status_t _seekStopped(
508		bigtime_t							position);
509
510	// start the given node, if possible & necessary, at
511	// the given time
512	// (this may be called from the transport thread or from
513	//  an API-implementation method.)
514
515	status_t _start(
516		bigtime_t							when);
517
518	// stop the given node (which may or may not still be
519	// a member of this group.)
520	// (this may be called from the transport thread or from
521	//  an API-implementation method.)
522
523	status_t _stop();
524
525	// roll the given node, if possible.
526	// (this may be called from the transport thread or from
527	//  an API-implementation method.)
528
529	status_t _roll(
530		bigtime_t							start,
531		bigtime_t							stop,
532		bigtime_t							position);
533
534	// refresh the node's current latency; if I reference
535	// a B_RECORDING node, update its 'producer delay'.
536
537	status_t _updateLatency();
538
539	// +++++ this method may not be needed 10aug99 +++++
540	// Figure the earliest time at which the given node can be started.
541	// Also calculates the position at which it should start from to
542	// play in sync with other nodes in the group, if the transport is
543	// running; if stopped, *outPosition will be set to the current
544	// start position.
545	// Pass the estimated amount of time needed to prepare the
546	// node for playback (ie. preroll & a little fudge factor) in
547	// startDelay.
548	//
549	// (this may be called from the transport thread or from
550	//  an API-implementation method.)
551
552	status_t _calcStartTime(
553		bigtime_t							startDelay,
554		bigtime_t*						outTime,
555		bigtime_t*						outPosition); //nyi
556
557	// *** Position reporting ***
558
559	// callers: _start(), _roll(), enablePositionReports()
560	status_t _startPositionThread();
561
562	// callers: _stop(), disablePositionReports(), dtor
563	status_t _stopPositionThread();
564
565	// [e.moon 14oct99] handle a report
566	status_t _handlePositionUpdate(
567		bigtime_t							perfTime,
568		bigtime_t							position);
569
570	// callers: _handlePositionUpdate
571	// (schedules a position update for the given time and position;
572	//  if the position overlaps a cycle, adjusts time & position
573	//  so that the notification is sent at the cycle point.)
574	status_t _schedulePositionUpdate(
575		bigtime_t							when,
576		bigtime_t							position);
577
578	// callers: _handlePositionUpdate, _initPositionThread()
579	// Send a message to all position observers
580	status_t _notifyPosition(
581		bigtime_t							when,
582		bigtime_t							position);
583
584private:										// *** members
585
586	// the node manager
587	NodeManager*							m_manager;
588
589	// owning (parent) group; may be 0 if node is not connected.
590	// A connected node always has a group, since groups allow transport
591	// operations.  New groups are created as necessary.
592	NodeGroup*								m_group;
593
594	// the node's state
595	live_node_info						m_info;
596
597	// user-definable transport behavior
598	uint32										m_flags;
599
600	// private/implementation flags
601
602	enum impl_flag_t {
603		// the node was created by NodeManager
604		_INTERNAL					= 1<<1,
605		// the node should NOT be released when this instance is destroyed
606		_NO_RELEASE				= 1<<2,
607		// notification of the node's instantiation has been received
608		// [e.moon 11oct99]
609		_CREATE_NOTIFIED	= 1<<3
610	};
611
612	uint32										m_implFlags;
613
614	// takes BMediaNode::run_mode values or 0 (wildcard:
615	// group run mode used instead)
616	// May not be B_OFFLINE; that must be specified at the group level.
617
618	uint32										m_runMode;
619
620	// only valid if m_runMode is BMediaNode::B_RECORDING
621	bigtime_t									m_recordingDelay;
622
623	// Media Roster connection [e.moon 11oct99]
624	bool											m_watching;
625
626	// hint information: this info is serialized with the object
627	// to provide 'hints' towards properly finding the same add-on
628	// node later on.  If no hint is provided, the node is assumed
629	// to be external to (not created by) the NodeManager.
630
631	struct addon_hint;
632	addon_hint*								m_addonHint;
633
634	// * position listening:
635	//   - moved from NodeGroup 9aug99
636
637	bool											m_positionReportsEnabled;
638	bool											m_positionReportsStarted;
639	bigtime_t									m_positionUpdatePeriod;
640	static const bigtime_t		s_defaultPositionUpdatePeriod			= 50000LL;
641
642	::MultiInvoker							m_positionInvoker;
643
644	bigtime_t									m_tpLastPositionUpdate;
645	bigtime_t									m_lastPosition;
646
647	// * synchronization threads
648
649	// only active if position listening has been enabled
650	NodeSyncThread*						m_positionThread;
651
652//	// only active if this node is cycling (looping)
653//	CycleSyncThread*				m_cycleSyncThread;
654//	+++++ 10aug99: moved back to NodeGroup
655
656private:										// *** transport state members
657
658	// is this node running?
659	bool											m_running;
660
661	// has the media_node (NOT this object) been released?
662	bool											m_nodeReleased;
663
664	// is this node supposed to loop?
665	bool											m_cycle;
666
667	// has the node been prepared to start?
668	bool											m_prerolled;
669
670	// when was the node started?
671	bigtime_t									m_tpStart;
672
673	// if the node has been prerolled, m_tpLastSeek will be 0 but
674	// m_lastSeekPos will reflect the node's prerolled position
675
676	bigtime_t									m_tpLastSeek;
677	bigtime_t									m_lastSeekPos;
678
679	// has a stop event been queued? [e.moon 11oct99]
680	bigtime_t									m_stopQueued;
681
682	// last known latency for this node
683	bigtime_t									m_latency;
684};
685
686__END_CORTEX_NAMESPACE
687#endif /*__NodeRef_H__*/
688