xref: /haiku/src/kits/media/MediaEventLooper.cpp (revision b6de9d11ff1c98de319f0c4f93ae3ceb9a33556f)
1 /***********************************************************************
2  * AUTHOR: Marcus Overhagen
3  *   FILE: MediaEventLooper.cpp
4  *  DESCR:
5  ***********************************************************************/
6 #include <MediaEventLooper.h>
7 #include <TimeSource.h>
8 #include <scheduler.h>
9 #include "debug.h"
10 
11 // XXX The bebook says that the latency is always calculated in realtime
12 // XXX This is not currently done in this code
13 
14 /*************************************************************
15  * protected BMediaEventLooper
16  *************************************************************/
17 
18 /* virtual */
19 BMediaEventLooper::~BMediaEventLooper()
20 {
21 	CALLED();
22 	// don't call Quit(); here
23 }
24 
25 /* explicit */
26 BMediaEventLooper::BMediaEventLooper(uint32 apiVersion) :
27 	BMediaNode("called by BMediaEventLooper"),
28 	fControlThread(-1),
29 	fCurrentPriority(B_URGENT_PRIORITY),
30 	fSetPriority(B_URGENT_PRIORITY),
31 	fRunState(B_UNREGISTERED),
32 	fEventLatency(0),
33 	fSchedulingLatency(0),
34 	fBufferDuration(0),
35 	fOfflineTime(0),
36 	fApiVersion(apiVersion)
37 {
38 	CALLED();
39 	fEventQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry,this);
40 	fRealTimeQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry,this);
41 }
42 
43 /* virtual */ void
44 BMediaEventLooper::NodeRegistered()
45 {
46 	CALLED();
47 	// don't call Run(); here, must be done by the derived class (yes, that's stupid)
48 }
49 
50 
51 /* virtual */ void
52 BMediaEventLooper::Start(bigtime_t performance_time)
53 {
54 	CALLED();
55 	// This hook function is called when a node is started
56 	// by a call to the BMediaRoster. The specified
57 	// performanceTime, the time at which the node
58 	// should start running, may be in the future.
59 	fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_START));
60 }
61 
62 
63 /* virtual */ void
64 BMediaEventLooper::Stop(bigtime_t performance_time,
65 						bool immediate)
66 {
67 	CALLED();
68 	// This hook function is called when a node is stopped
69 	// by a call to the BMediaRoster. The specified performanceTime,
70 	// the time at which the node should stop, may be in the future.
71 	// If immediate is true, your node should ignore the performanceTime
72 	// value and synchronously stop performance. When Stop() returns,
73 	// you're promising not to write into any BBuffers you may have
74 	// received from your downstream consumers, and you promise not
75 	// to send any more buffers until Start() is called again.
76 
77 	if (immediate) {
78 		// always be sure to add to the front of the queue so we can make sure it is
79 		// handled before any buffers are sent!
80 		performance_time = fEventQueue.FirstEventTime();
81 		performance_time = (performance_time == B_INFINITE_TIMEOUT) ? 0 : performance_time - 1;
82 	}
83 	fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_STOP));
84 }
85 
86 
87 /* virtual */ void
88 BMediaEventLooper::Seek(bigtime_t media_time,
89 						bigtime_t performance_time)
90 {
91 	CALLED();
92 	// This hook function is called when a node is asked to seek to
93 	// the specified mediaTime by a call to the BMediaRoster.
94 	// The specified performanceTime, the time at which the node
95 	// should begin the seek operation, may be in the future.
96 	fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_SEEK, NULL,
97 		BTimedEventQueue::B_NO_CLEANUP, 0, media_time, NULL));
98 }
99 
100 
101 /* virtual */ void
102 BMediaEventLooper::TimeWarp(bigtime_t at_real_time,
103 							bigtime_t to_performance_time)
104 {
105 	CALLED();
106 	// This hook function is called when the time source to which the
107 	// node is slaved is repositioned (via a seek operation) such that
108 	// there will be a sudden jump in the performance time progression
109 	// as seen by the node. The to_performance_time argument indicates
110 	// the new performance time; the change should occur at the real
111 	// time specified by the at_real_time argument.
112 
113 	// place in the realtime queue
114 	fRealTimeQueue.AddEvent(media_timed_event(at_real_time,	BTimedEventQueue::B_WARP,
115 		NULL, BTimedEventQueue::B_NO_CLEANUP, 0, to_performance_time, NULL));
116 
117 	// BeBook: Your implementation of TimeWarp() should call through to BMediaNode::TimeWarp()
118 	// BeBook: as well as all other inherited forms of TimeWarp()
119 	// XXX should we do this here?
120 	BMediaNode::TimeWarp(at_real_time, to_performance_time);
121 }
122 
123 
124 /* virtual */ status_t
125 BMediaEventLooper::AddTimer(bigtime_t at_performance_time,
126 							int32 cookie)
127 {
128 	CALLED();
129 	// XXX what do we need to do here?
130 	return BMediaNode::AddTimer(at_performance_time,cookie);
131 }
132 
133 
134 /* virtual */ void
135 BMediaEventLooper::SetRunMode(run_mode mode)
136 {
137 	CALLED();
138 	// The SetRunMode() hook function is called when someone requests that your node's run mode be changed.
139 
140 	// bump or reduce priority when switching from/to offline run mode
141 	int32 priority;
142 	priority = (mode == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority;
143 	if (priority != fCurrentPriority) {
144 		fCurrentPriority = priority;
145 		if(fControlThread > 0) {
146 			set_thread_priority(fControlThread, fCurrentPriority);
147 			fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
148 		}
149 	}
150 
151 	BMediaNode::SetRunMode(mode);
152 }
153 
154 
155 /* virtual */ void
156 BMediaEventLooper::CleanUpEvent(const media_timed_event *event)
157 {
158 	CALLED();
159 	// Implement this function to clean up after custom events you've created
160 	// and added to your queue. It's called when a custom event is removed from
161 	// the queue, to let you handle any special tidying-up that the event might require.
162 }
163 
164 
165 /* virtual */ bigtime_t
166 BMediaEventLooper::OfflineTime()
167 {
168 	CALLED();
169 	return fOfflineTime;
170 }
171 
172 
173 /* virtual */ void
174 BMediaEventLooper::ControlLoop()
175 {
176 	CALLED();
177 
178 	bool is_realtime = false;
179 	status_t err;
180 	bigtime_t latency;
181 	bigtime_t waituntil;
182 	for (;;) {
183 		// while there are no events or it is not time for the earliest event,
184 		// process messages using WaitForMessages. Whenever this funtion times out,
185 		// we need to handle the next event
186 		for (;;) {
187 			if (RunState() == B_QUITTING)
188 				return;
189 			// BMediaEventLooper compensates your performance time by adding the event latency
190 			// (see SetEventLatency()) and the scheduling latency (or, for real-time events,
191 			// only the scheduling latency).
192 			// latency = fOut.downstream_latency + fOut.processing_latency + fSchedulingLatency;
193 			// XXX well, fix this later
194 			latency = fEventLatency + fSchedulingLatency;
195 
196 			if (fEventQueue.HasEvents() && (TimeSource()->Now() - latency) >= fEventQueue.FirstEventTime()) {
197 				is_realtime = false;
198 				break;
199 			}
200 			if (fRealTimeQueue.HasEvents() && (TimeSource()->RealTimeFor(TimeSource()->Now(),fSchedulingLatency)) >= fRealTimeQueue.FirstEventTime()) {
201 				is_realtime = true;
202 				break;
203 			}
204 			waituntil = B_INFINITE_TIMEOUT;
205 			if (fEventQueue.HasEvents()) {
206 				waituntil = TimeSource()->RealTimeFor(fEventQueue.FirstEventTime(), latency);
207 				is_realtime = false;
208 			}
209 			if (fRealTimeQueue.HasEvents()) {
210 				bigtime_t temp;
211 				temp = TimeSource()->RealTimeFor(TimeSource()->PerformanceTimeFor(fRealTimeQueue.FirstEventTime()), fSchedulingLatency);
212 				if (temp < waituntil) {
213 					waituntil = temp;
214 					is_realtime = true;
215 				}
216 			}
217 			err = WaitForMessage(waituntil);
218 			if (err == B_TIMED_OUT)
219 				break;
220 		}
221 
222 		/// we have timed out - so handle the next event
223 		media_timed_event event;
224 		if (is_realtime)
225 			err = fRealTimeQueue.RemoveFirstEvent(&event);
226 		else
227 			err = fEventQueue.RemoveFirstEvent(&event);
228 
229 		if (err == B_OK) {
230 			bigtime_t lateness;
231 			if (is_realtime)
232 				lateness = TimeSource()->RealTime() - event.event_time;
233 			else
234 				lateness = TimeSource()->Now() - event.event_time;
235 			DispatchEvent(&event,lateness,is_realtime);
236 		}
237 	}
238 }
239 
240 
241 thread_id
242 BMediaEventLooper::ControlThread()
243 {
244 	CALLED();
245 	return fControlThread;
246 }
247 
248 /*************************************************************
249  * protected BMediaEventLooper
250  *************************************************************/
251 
252 
253 BTimedEventQueue *
254 BMediaEventLooper::EventQueue()
255 {
256 	CALLED();
257 	return &fEventQueue;
258 }
259 
260 
261 BTimedEventQueue *
262 BMediaEventLooper::RealTimeQueue()
263 {
264 	CALLED();
265 	return &fRealTimeQueue;
266 }
267 
268 
269 int32
270 BMediaEventLooper::Priority() const
271 {
272 	CALLED();
273 	return fCurrentPriority;
274 }
275 
276 
277 int32
278 BMediaEventLooper::RunState() const
279 {
280 	CALLED();
281 	return fRunState;
282 }
283 
284 
285 bigtime_t
286 BMediaEventLooper::EventLatency() const
287 {
288 	CALLED();
289 	return fEventLatency;
290 }
291 
292 
293 bigtime_t
294 BMediaEventLooper::BufferDuration() const
295 {
296 	CALLED();
297 	return fBufferDuration;
298 }
299 
300 
301 bigtime_t
302 BMediaEventLooper::SchedulingLatency() const
303 {
304 	CALLED();
305 	return fSchedulingLatency;
306 }
307 
308 
309 status_t
310 BMediaEventLooper::SetPriority(int32 priority)
311 {
312 	CALLED();
313 
314 	// clamp to a valid value
315 	if (priority < 1)
316 		priority = 1;
317 
318 	if (priority > 120)
319 		priority = 120;
320 
321 	fSetPriority = priority;
322 	fCurrentPriority = (RunMode() == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority;
323 
324 	if(fControlThread > 0) {
325 		set_thread_priority(fControlThread, fCurrentPriority);
326 		fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
327 	}
328 
329 	return B_OK;
330 }
331 
332 
333 void
334 BMediaEventLooper::SetRunState(run_state state)
335 {
336 	CALLED();
337 
338 	// don't allow run state changes while quitting,
339 	// also needed for correct terminating of the ControlLoop()
340 	if (fRunState == B_QUITTING && state != B_TERMINATED)
341 		return;
342 
343 	fRunState = state;
344 }
345 
346 
347 void
348 BMediaEventLooper::SetEventLatency(bigtime_t latency)
349 {
350 	CALLED();
351 	// clamp to a valid value
352 	if (latency < 0)
353 		latency = 0;
354 
355 	fEventLatency = latency;
356 }
357 
358 
359 void
360 BMediaEventLooper::SetBufferDuration(bigtime_t duration)
361 {
362 	CALLED();
363 	fBufferDuration = duration;
364 }
365 
366 
367 void
368 BMediaEventLooper::SetOfflineTime(bigtime_t offTime)
369 {
370 	CALLED();
371 	fOfflineTime = offTime;
372 }
373 
374 
375 void
376 BMediaEventLooper::Run()
377 {
378 	CALLED();
379 
380 	if (fControlThread != -1)
381 		return; // thread already running
382 
383 	char threadName[32];
384 	sprintf(threadName, "%.20s control", Name());
385 	fControlThread = spawn_thread(_ControlThreadStart, threadName, fCurrentPriority, this);
386 	resume_thread(fControlThread);
387 
388 	// get latency information
389 	fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
390 }
391 
392 
393 void
394 BMediaEventLooper::Quit()
395 {
396 	CALLED();
397 	status_t err;
398 
399 	if (fRunState == B_TERMINATED)
400 		return;
401 
402 	SetRunState(B_QUITTING);
403 
404 	close_port(ControlPort());
405 	if (fControlThread != -1)
406 		wait_for_thread(fControlThread, &err);
407 	fControlThread = -1;
408 
409 	SetRunState(B_TERMINATED);
410 }
411 
412 
413 void
414 BMediaEventLooper::DispatchEvent(const media_timed_event *event,
415 								 bigtime_t lateness,
416 								 bool realTimeEvent)
417 {
418 	CALLED();
419 
420 	HandleEvent(event,lateness,realTimeEvent);
421 
422 	switch (event->type) {
423 		case BTimedEventQueue::B_START:
424 			SetRunState(B_STARTED);
425 			break;
426 
427 		case BTimedEventQueue::B_STOP:
428 			SetRunState(B_STOPPED);
429 			break;
430 
431 		case BTimedEventQueue::B_SEEK:
432 			/* nothing */
433 			break;
434 
435 		case BTimedEventQueue::B_WARP:
436 			/* nothing */
437 			break;
438 
439 		default:
440 			break;
441 	}
442 
443 }
444 
445 /*************************************************************
446  * private BMediaEventLooper
447  *************************************************************/
448 
449 
450 /* static */ int32
451 BMediaEventLooper::_ControlThreadStart(void *arg)
452 {
453 	CALLED();
454 	((BMediaEventLooper *)arg)->SetRunState(B_STOPPED);
455 	((BMediaEventLooper *)arg)->ControlLoop();
456 	((BMediaEventLooper *)arg)->SetRunState(B_QUITTING);
457 	return 0;
458 }
459 
460 
461 /* static */ void
462 BMediaEventLooper::_CleanUpEntry(const media_timed_event *event,
463 								 void *context)
464 {
465 	CALLED();
466 	((BMediaEventLooper *)context)->_DispatchCleanUp(event);
467 }
468 
469 
470 void
471 BMediaEventLooper::_DispatchCleanUp(const media_timed_event *event)
472 {
473 	CALLED();
474 
475 	// this function to clean up after custom events you've created
476 	if (event->cleanup >= BTimedEventQueue::B_USER_CLEANUP)
477 		CleanUpEvent(event);
478 }
479 
480 /*
481 // unimplemented
482 BMediaEventLooper::BMediaEventLooper(const BMediaEventLooper &)
483 BMediaEventLooper &BMediaEventLooper::operator=(const BMediaEventLooper &)
484 */
485 
486 /*************************************************************
487  * protected BMediaEventLooper
488  *************************************************************/
489 
490 
491 status_t
492 BMediaEventLooper::DeleteHook(BMediaNode *node)
493 {
494 	CALLED();
495 	// this is the DeleteHook that gets called by the media server
496 	// before the media node is deleted
497 	Quit();
498 	return BMediaNode::DeleteHook(node);
499 }
500 
501 /*************************************************************
502  * private BMediaEventLooper
503  *************************************************************/
504 
505 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_0(int32 arg,...) { return B_ERROR; }
506 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_1(int32 arg,...) { return B_ERROR; }
507 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_2(int32 arg,...) { return B_ERROR; }
508 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_3(int32 arg,...) { return B_ERROR; }
509 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_4(int32 arg,...) { return B_ERROR; }
510 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_5(int32 arg,...) { return B_ERROR; }
511 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_6(int32 arg,...) { return B_ERROR; }
512 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_7(int32 arg,...) { return B_ERROR; }
513 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_8(int32 arg,...) { return B_ERROR; }
514 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_9(int32 arg,...) { return B_ERROR; }
515 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_10(int32 arg,...) { return B_ERROR; }
516 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_11(int32 arg,...) { return B_ERROR; }
517 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_12(int32 arg,...) { return B_ERROR; }
518 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_13(int32 arg,...) { return B_ERROR; }
519 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_14(int32 arg,...) { return B_ERROR; }
520 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_15(int32 arg,...) { return B_ERROR; }
521 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_16(int32 arg,...) { return B_ERROR; }
522 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_17(int32 arg,...) { return B_ERROR; }
523 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_18(int32 arg,...) { return B_ERROR; }
524 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_19(int32 arg,...) { return B_ERROR; }
525 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_20(int32 arg,...) { return B_ERROR; }
526 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_21(int32 arg,...) { return B_ERROR; }
527 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_22(int32 arg,...) { return B_ERROR; }
528 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_23(int32 arg,...) { return B_ERROR; }
529 
530