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