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