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