1 /*
2 * Copyright (c) 2015 Dario Casalinuovo <b.vitruvio@gmail.com>
3 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files or portions
7 * thereof (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge,
9 * publish, distribute, sublicense, and/or sell copies of the Software,
10 * and to permit persons to whom the Software is furnished to do so, subject
11 * to the following conditions:
12 *
13 * * Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above copyright notice
17 * in the binary, as well as this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided with
19 * the distribution.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 *
29 */
30
31 #include <MediaEventLooper.h>
32 #include <TimeSource.h>
33 #include <scheduler.h>
34 #include <Buffer.h>
35 #include <ServerInterface.h>
36 #include "MediaDebug.h"
37
38 /*************************************************************
39 * protected BMediaEventLooper
40 *************************************************************/
41
42 /* virtual */
~BMediaEventLooper()43 BMediaEventLooper::~BMediaEventLooper()
44 {
45 CALLED();
46
47 // don't call Quit(); here, except if the user was stupid
48 if (fControlThread != -1) {
49 printf("You MUST call BMediaEventLooper::Quit() in your destructor!\n");
50 Quit();
51 }
52 }
53
54 /* explicit */
BMediaEventLooper(uint32 apiVersion)55 BMediaEventLooper::BMediaEventLooper(uint32 apiVersion) :
56 BMediaNode("called by BMediaEventLooper"),
57 fControlThread(-1),
58 fCurrentPriority(B_URGENT_PRIORITY),
59 fSetPriority(B_URGENT_PRIORITY),
60 fRunState(B_UNREGISTERED),
61 fEventLatency(0),
62 fSchedulingLatency(0),
63 fBufferDuration(0),
64 fOfflineTime(0),
65 fApiVersion(apiVersion)
66 {
67 CALLED();
68 fEventQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this);
69 fRealTimeQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this);
70 }
71
72 /* virtual */ void
NodeRegistered()73 BMediaEventLooper::NodeRegistered()
74 {
75 CALLED();
76 // Calling Run() should be done by the derived class,
77 // at least that's how it is documented by the BeBook.
78 // It appears that BeOS R5 called it here. Calling Run()
79 // twice doesn't hurt, and some nodes need it to be called here.
80 Run();
81 }
82
83
84 /* virtual */ void
Start(bigtime_t performance_time)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
Stop(bigtime_t performance_time,bool immediate)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
Seek(bigtime_t media_time,bigtime_t performance_time)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
TimeWarp(bigtime_t at_real_time,bigtime_t to_performance_time)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
AddTimer(bigtime_t at_performance_time,int32 cookie)157 BMediaEventLooper::AddTimer(bigtime_t at_performance_time,
158 int32 cookie)
159 {
160 CALLED();
161
162 media_timed_event event(at_performance_time,
163 BTimedEventQueue::B_TIMER, NULL,
164 BTimedEventQueue::B_EXPIRE_TIMER);
165 event.data = cookie;
166 return EventQueue()->AddEvent(event);
167 }
168
169
170 /* virtual */ void
SetRunMode(run_mode mode)171 BMediaEventLooper::SetRunMode(run_mode mode)
172 {
173 CALLED();
174 // The SetRunMode() hook function is called when someone requests that your node's run mode be changed.
175
176 // bump or reduce priority when switching from/to offline run mode
177 int32 priority;
178 priority = (mode == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority;
179 if (priority != fCurrentPriority) {
180 fCurrentPriority = priority;
181 if (fControlThread > 0) {
182 set_thread_priority(fControlThread, fCurrentPriority);
183 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
184 printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n",
185 fSchedulingLatency);
186 }
187 }
188
189 BMediaNode::SetRunMode(mode);
190 }
191
192
193 /* virtual */ void
CleanUpEvent(const media_timed_event * event)194 BMediaEventLooper::CleanUpEvent(const media_timed_event *event)
195 {
196 CALLED();
197 // Implement this function to clean up after custom events you've created
198 // and added to your queue. It's called when a custom event is removed from
199 // the queue, to let you handle any special tidying-up that the event might require.
200 }
201
202
203 /* virtual */ bigtime_t
OfflineTime()204 BMediaEventLooper::OfflineTime()
205 {
206 CALLED();
207 return fOfflineTime;
208 }
209
210
211 /* virtual */ void
ControlLoop()212 BMediaEventLooper::ControlLoop()
213 {
214 CALLED();
215
216 status_t err = B_OK;
217 bigtime_t waitUntil = B_INFINITE_TIMEOUT;
218 bool hasRealtime = false;
219 bool hasEvent = false;
220
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
225 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
226 while (RunState() != B_QUITTING) {
227 if (err == B_TIMED_OUT
228 || err == B_WOULD_BLOCK) {
229 // NOTE: The reference for doing the lateness calculus this way can
230 // be found in the BeBook article "A BMediaEventLooper Example".
231 // The value which we are going to calculate, is referred there as
232 // 'lateness'.
233 media_timed_event event;
234 if (hasEvent)
235 err = fEventQueue.RemoveFirstEvent(&event);
236 else if (hasRealtime)
237 err = fRealTimeQueue.RemoveFirstEvent(&event);
238
239 if (err == B_OK) {
240 // The general idea of lateness is to allow
241 // the client code to detect when the buffer
242 // is handled late or early.
243 bigtime_t lateness = TimeSource()->RealTime() - waitUntil;
244
245 DispatchEvent(&event, lateness, hasRealtime);
246 }
247 } else if (err != B_OK)
248 return;
249
250 // BMediaEventLooper compensates your performance time by adding
251 // the event latency (see SetEventLatency()) and the scheduling
252 // latency (or, for real-time events, only the scheduling latency).
253
254 hasRealtime = fRealTimeQueue.HasEvents();
255 hasEvent = fEventQueue.HasEvents();
256
257 if (hasEvent) {
258 waitUntil = TimeSource()->RealTimeFor(
259 fEventQueue.FirstEventTime(),
260 fEventLatency + fSchedulingLatency);
261 } else if (!hasRealtime)
262 waitUntil = B_INFINITE_TIMEOUT;
263
264 if (hasRealtime) {
265 bigtime_t realtimeWait = fRealTimeQueue.FirstEventTime()
266 - fSchedulingLatency;
267
268 if (!hasEvent || realtimeWait <= waitUntil) {
269 waitUntil = realtimeWait;
270 hasEvent = false;
271 } else
272 hasRealtime = false;
273 }
274
275 if (waitUntil != B_INFINITE_TIMEOUT
276 && TimeSource()->RealTime() >= waitUntil) {
277 err = WaitForMessage(0);
278 } else
279 err = WaitForMessage(waitUntil);
280 }
281 }
282
283
284 thread_id
ControlThread()285 BMediaEventLooper::ControlThread()
286 {
287 CALLED();
288 return fControlThread;
289 }
290
291 /*************************************************************
292 * protected BMediaEventLooper
293 *************************************************************/
294
295
296 BTimedEventQueue *
EventQueue()297 BMediaEventLooper::EventQueue()
298 {
299 CALLED();
300 return &fEventQueue;
301 }
302
303
304 BTimedEventQueue *
RealTimeQueue()305 BMediaEventLooper::RealTimeQueue()
306 {
307 CALLED();
308 return &fRealTimeQueue;
309 }
310
311
312 int32
Priority() const313 BMediaEventLooper::Priority() const
314 {
315 CALLED();
316 return fCurrentPriority;
317 }
318
319
320 int32
RunState() const321 BMediaEventLooper::RunState() const
322 {
323 PRINT(6, "CALLED BMediaEventLooper::RunState()\n");
324 return fRunState;
325 }
326
327
328 bigtime_t
EventLatency() const329 BMediaEventLooper::EventLatency() const
330 {
331 CALLED();
332 return fEventLatency;
333 }
334
335
336 bigtime_t
BufferDuration() const337 BMediaEventLooper::BufferDuration() const
338 {
339 CALLED();
340 return fBufferDuration;
341 }
342
343
344 bigtime_t
SchedulingLatency() const345 BMediaEventLooper::SchedulingLatency() const
346 {
347 CALLED();
348 return fSchedulingLatency;
349 }
350
351
352 status_t
SetPriority(int32 priority)353 BMediaEventLooper::SetPriority(int32 priority)
354 {
355 CALLED();
356
357 // clamp to a valid value
358 if (priority < 5)
359 priority = 5;
360
361 if (priority > 120)
362 priority = 120;
363
364 fSetPriority = priority;
365 fCurrentPriority = (RunMode() == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority;
366
367 if (fControlThread > 0) {
368 set_thread_priority(fControlThread, fCurrentPriority);
369 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
370 printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n",
371 fSchedulingLatency);
372 }
373
374 return B_OK;
375 }
376
377
378 void
SetRunState(run_state state)379 BMediaEventLooper::SetRunState(run_state state)
380 {
381 CALLED();
382
383 // don't allow run state changes while quitting,
384 // also needed for correct terminating of the ControlLoop()
385 if (fRunState == B_QUITTING && state != B_TERMINATED)
386 return;
387
388 fRunState = state;
389 }
390
391
392 void
SetEventLatency(bigtime_t latency)393 BMediaEventLooper::SetEventLatency(bigtime_t latency)
394 {
395 CALLED();
396 // clamp to a valid value
397 if (latency < 0)
398 latency = 0;
399
400 fEventLatency = latency;
401 write_port_etc(ControlPort(), GENERAL_PURPOSE_WAKEUP, 0, 0, B_TIMEOUT, 0);
402 }
403
404
405 void
SetBufferDuration(bigtime_t duration)406 BMediaEventLooper::SetBufferDuration(bigtime_t duration)
407 {
408 CALLED();
409
410 if (duration < 0)
411 duration = 0;
412
413 fBufferDuration = duration;
414 }
415
416
417 void
SetOfflineTime(bigtime_t offTime)418 BMediaEventLooper::SetOfflineTime(bigtime_t offTime)
419 {
420 CALLED();
421 fOfflineTime = offTime;
422 }
423
424
425 void
Run()426 BMediaEventLooper::Run()
427 {
428 CALLED();
429
430 if (fControlThread != -1)
431 return; // thread already running
432
433 // until now, the run state is B_UNREGISTERED, but we need to start in B_STOPPED state.
434 SetRunState(B_STOPPED);
435
436 char threadName[32];
437 sprintf(threadName, "%.20s control", Name());
438 fControlThread = spawn_thread(_ControlThreadStart, threadName, fCurrentPriority, this);
439 resume_thread(fControlThread);
440
441 // get latency information
442 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
443 }
444
445
446 void
Quit()447 BMediaEventLooper::Quit()
448 {
449 CALLED();
450
451 if (fRunState == B_TERMINATED)
452 return;
453
454 SetRunState(B_QUITTING);
455 close_port(ControlPort());
456 if (fControlThread != -1) {
457 status_t err;
458 wait_for_thread(fControlThread, &err);
459 fControlThread = -1;
460 }
461 SetRunState(B_TERMINATED);
462 }
463
464
465 void
DispatchEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)466 BMediaEventLooper::DispatchEvent(const media_timed_event *event,
467 bigtime_t lateness,
468 bool realTimeEvent)
469 {
470 PRINT(6, "CALLED BMediaEventLooper::DispatchEvent()\n");
471
472 HandleEvent(event, lateness, realTimeEvent);
473
474 switch (event->type) {
475 case BTimedEventQueue::B_START:
476 SetRunState(B_STARTED);
477 break;
478
479 case BTimedEventQueue::B_STOP:
480 SetRunState(B_STOPPED);
481 break;
482
483 case BTimedEventQueue::B_SEEK:
484 /* nothing */
485 break;
486
487 case BTimedEventQueue::B_WARP:
488 /* nothing */
489 break;
490
491 case BTimedEventQueue::B_TIMER:
492 TimerExpired(event->event_time, event->data);
493 break;
494
495 default:
496 break;
497 }
498
499 _DispatchCleanUp(event);
500 }
501
502 /*************************************************************
503 * private BMediaEventLooper
504 *************************************************************/
505
506
507 /* static */ int32
_ControlThreadStart(void * arg)508 BMediaEventLooper::_ControlThreadStart(void *arg)
509 {
510 CALLED();
511 ((BMediaEventLooper *)arg)->SetRunState(B_STOPPED);
512 ((BMediaEventLooper *)arg)->ControlLoop();
513 ((BMediaEventLooper *)arg)->SetRunState(B_QUITTING);
514 return 0;
515 }
516
517
518 /* static */ void
_CleanUpEntry(const media_timed_event * event,void * context)519 BMediaEventLooper::_CleanUpEntry(const media_timed_event *event,
520 void *context)
521 {
522 PRINT(6, "CALLED BMediaEventLooper::_CleanUpEntry()\n");
523 ((BMediaEventLooper *)context)->_DispatchCleanUp(event);
524 }
525
526
527 void
_DispatchCleanUp(const media_timed_event * event)528 BMediaEventLooper::_DispatchCleanUp(const media_timed_event *event)
529 {
530 PRINT(6, "CALLED BMediaEventLooper::_DispatchCleanUp()\n");
531
532 // this function to clean up after custom events you've created
533 if (event->cleanup >= BTimedEventQueue::B_USER_CLEANUP)
534 CleanUpEvent(event);
535 }
536
537 /*
538 // unimplemented
539 BMediaEventLooper::BMediaEventLooper(const BMediaEventLooper &)
540 BMediaEventLooper &BMediaEventLooper::operator=(const BMediaEventLooper &)
541 */
542
543 /*************************************************************
544 * protected BMediaEventLooper
545 *************************************************************/
546
547
548 status_t
DeleteHook(BMediaNode * node)549 BMediaEventLooper::DeleteHook(BMediaNode *node)
550 {
551 CALLED();
552 // this is the DeleteHook that gets called by the media server
553 // before the media node is deleted
554 Quit();
555 return BMediaNode::DeleteHook(node);
556 }
557
558 /*************************************************************
559 * private BMediaEventLooper
560 *************************************************************/
561
_Reserved_BMediaEventLooper_0(int32 arg,...)562 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_0(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_1(int32 arg,...)563 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_1(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_2(int32 arg,...)564 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_2(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_3(int32 arg,...)565 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_3(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_4(int32 arg,...)566 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_4(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_5(int32 arg,...)567 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_5(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_6(int32 arg,...)568 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_6(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_7(int32 arg,...)569 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_7(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_8(int32 arg,...)570 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_8(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_9(int32 arg,...)571 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_9(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_10(int32 arg,...)572 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_10(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_11(int32 arg,...)573 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_11(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_12(int32 arg,...)574 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_12(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_13(int32 arg,...)575 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_13(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_14(int32 arg,...)576 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_14(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_15(int32 arg,...)577 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_15(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_16(int32 arg,...)578 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_16(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_17(int32 arg,...)579 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_17(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_18(int32 arg,...)580 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_18(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_19(int32 arg,...)581 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_19(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_20(int32 arg,...)582 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_20(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_21(int32 arg,...)583 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_21(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_22(int32 arg,...)584 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_22(int32 arg,...) { return B_ERROR; }
_Reserved_BMediaEventLooper_23(int32 arg,...)585 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_23(int32 arg,...) { return B_ERROR; }
586
587