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