2 * Copyright (c) 2015 Dario Casalinuovo <b.vitruvio@gmail.com>
3 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
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:
13 * * Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
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
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
31 #include <MediaEventLooper.h>
32 #include <TimeSource.h>
33 #include <scheduler.h>
35 #include <ServerInterface.h>
38 /*************************************************************
39 * protected BMediaEventLooper
40 *************************************************************/
43 BMediaEventLooper::~BMediaEventLooper()
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");
55 BMediaEventLooper::BMediaEventLooper(uint32 apiVersion
) :
56 BMediaNode("called by BMediaEventLooper"),
58 fCurrentPriority(B_URGENT_PRIORITY
),
59 fSetPriority(B_URGENT_PRIORITY
),
60 fRunState(B_UNREGISTERED
),
62 fSchedulingLatency(0),
65 fApiVersion(apiVersion
)
68 fEventQueue
.SetCleanupHook(BMediaEventLooper::_CleanUpEntry
, this);
69 fRealTimeQueue
.SetCleanupHook(BMediaEventLooper::_CleanUpEntry
, this);
73 BMediaEventLooper::NodeRegistered()
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.
85 BMediaEventLooper::Start(bigtime_t performance_time
)
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
));
97 BMediaEventLooper::Stop(bigtime_t performance_time
,
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.
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;
115 fEventQueue
.AddEvent(media_timed_event(performance_time
, BTimedEventQueue::B_STOP
));
120 BMediaEventLooper::Seek(bigtime_t media_time
,
121 bigtime_t performance_time
)
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
));
134 BMediaEventLooper::TimeWarp(bigtime_t at_real_time
,
135 bigtime_t to_performance_time
)
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.
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
));
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
);
156 /* virtual */ status_t
157 BMediaEventLooper::AddTimer(bigtime_t at_performance_time
,
162 media_timed_event
event(at_performance_time
,
163 BTimedEventQueue::B_TIMER
, NULL
,
164 BTimedEventQueue::B_EXPIRE_TIMER
);
166 return EventQueue()->AddEvent(event
);
171 BMediaEventLooper::SetRunMode(run_mode mode
)
174 // The SetRunMode() hook function is called when someone requests that your node's run mode be changed.
176 // bump or reduce priority when switching from/to offline run mode
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",
189 BMediaNode::SetRunMode(mode
);
194 BMediaEventLooper::CleanUpEvent(const media_timed_event
*event
)
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.
203 /* virtual */ bigtime_t
204 BMediaEventLooper::OfflineTime()
212 BMediaEventLooper::ControlLoop()
217 bigtime_t waitUntil
= B_INFINITE_TIMEOUT
;
218 bool hasRealtime
= false;
219 bool hasEvent
= false;
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
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
233 media_timed_event event
;
235 err
= fEventQueue
.RemoveFirstEvent(&event
);
236 else if (hasRealtime
)
237 err
= fRealTimeQueue
.RemoveFirstEvent(&event
);
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
;
245 DispatchEvent(&event
, lateness
, hasRealtime
);
247 } else if (err
!= B_OK
)
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).
254 hasRealtime
= fRealTimeQueue
.HasEvents();
255 hasEvent
= fEventQueue
.HasEvents();
258 waitUntil
= TimeSource()->RealTimeFor(
259 fEventQueue
.FirstEventTime(),
260 fEventLatency
+ fSchedulingLatency
);
261 } else if (!hasRealtime
)
262 waitUntil
= B_INFINITE_TIMEOUT
;
265 bigtime_t realtimeWait
= fRealTimeQueue
.FirstEventTime()
266 - fSchedulingLatency
;
268 if (!hasEvent
|| realtimeWait
<= waitUntil
) {
269 waitUntil
= realtimeWait
;
275 if (waitUntil
!= B_INFINITE_TIMEOUT
276 && TimeSource()->RealTime() >= waitUntil
) {
277 err
= WaitForMessage(0);
279 err
= WaitForMessage(waitUntil
);
285 BMediaEventLooper::ControlThread()
288 return fControlThread
;
291 /*************************************************************
292 * protected BMediaEventLooper
293 *************************************************************/
297 BMediaEventLooper::EventQueue()
305 BMediaEventLooper::RealTimeQueue()
308 return &fRealTimeQueue
;
313 BMediaEventLooper::Priority() const
316 return fCurrentPriority
;
321 BMediaEventLooper::RunState() const
323 PRINT(6, "CALLED BMediaEventLooper::RunState()\n");
329 BMediaEventLooper::EventLatency() const
332 return fEventLatency
;
337 BMediaEventLooper::BufferDuration() const
340 return fBufferDuration
;
345 BMediaEventLooper::SchedulingLatency() const
348 return fSchedulingLatency
;
353 BMediaEventLooper::SetPriority(int32 priority
)
357 // clamp to a valid value
364 fSetPriority
= priority
;
365 fCurrentPriority
= (RunMode() == B_OFFLINE
) ? min_c(B_NORMAL_PRIORITY
, fSetPriority
) : fSetPriority
;
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",
379 BMediaEventLooper::SetRunState(run_state state
)
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
)
393 BMediaEventLooper::SetEventLatency(bigtime_t latency
)
396 // clamp to a valid value
400 fEventLatency
= latency
;
401 write_port_etc(ControlPort(), GENERAL_PURPOSE_WAKEUP
, 0, 0, B_TIMEOUT
, 0);
406 BMediaEventLooper::SetBufferDuration(bigtime_t duration
)
413 fBufferDuration
= duration
;
418 BMediaEventLooper::SetOfflineTime(bigtime_t offTime
)
421 fOfflineTime
= offTime
;
426 BMediaEventLooper::Run()
430 if (fControlThread
!= -1)
431 return; // thread already running
433 // until now, the run state is B_UNREGISTERED, but we need to start in B_STOPPED state.
434 SetRunState(B_STOPPED
);
437 sprintf(threadName
, "%.20s control", Name());
438 fControlThread
= spawn_thread(_ControlThreadStart
, threadName
, fCurrentPriority
, this);
439 resume_thread(fControlThread
);
441 // get latency information
442 fSchedulingLatency
= estimate_max_scheduling_latency(fControlThread
);
447 BMediaEventLooper::Quit()
451 if (fRunState
== B_TERMINATED
)
454 SetRunState(B_QUITTING
);
455 close_port(ControlPort());
456 if (fControlThread
!= -1) {
458 wait_for_thread(fControlThread
, &err
);
461 SetRunState(B_TERMINATED
);
466 BMediaEventLooper::DispatchEvent(const media_timed_event
*event
,
470 PRINT(6, "CALLED BMediaEventLooper::DispatchEvent()\n");
472 HandleEvent(event
, lateness
, realTimeEvent
);
474 switch (event
->type
) {
475 case BTimedEventQueue::B_START
:
476 SetRunState(B_STARTED
);
479 case BTimedEventQueue::B_STOP
:
480 SetRunState(B_STOPPED
);
483 case BTimedEventQueue::B_SEEK
:
487 case BTimedEventQueue::B_WARP
:
491 case BTimedEventQueue::B_TIMER
:
492 TimerExpired(event
->event_time
, event
->data
);
499 _DispatchCleanUp(event
);
502 /*************************************************************
503 * private BMediaEventLooper
504 *************************************************************/
508 BMediaEventLooper::_ControlThreadStart(void *arg
)
511 ((BMediaEventLooper
*)arg
)->SetRunState(B_STOPPED
);
512 ((BMediaEventLooper
*)arg
)->ControlLoop();
513 ((BMediaEventLooper
*)arg
)->SetRunState(B_QUITTING
);
519 BMediaEventLooper::_CleanUpEntry(const media_timed_event
*event
,
522 PRINT(6, "CALLED BMediaEventLooper::_CleanUpEntry()\n");
523 ((BMediaEventLooper
*)context
)->_DispatchCleanUp(event
);
528 BMediaEventLooper::_DispatchCleanUp(const media_timed_event
*event
)
530 PRINT(6, "CALLED BMediaEventLooper::_DispatchCleanUp()\n");
532 // this function to clean up after custom events you've created
533 if (event
->cleanup
>= BTimedEventQueue::B_USER_CLEANUP
)
539 BMediaEventLooper::BMediaEventLooper(const BMediaEventLooper &)
540 BMediaEventLooper &BMediaEventLooper::operator=(const BMediaEventLooper &)
543 /*************************************************************
544 * protected BMediaEventLooper
545 *************************************************************/
549 BMediaEventLooper::DeleteHook(BMediaNode
*node
)
552 // this is the DeleteHook that gets called by the media server
553 // before the media node is deleted
555 return BMediaNode::DeleteHook(node
);
558 /*************************************************************
559 * private BMediaEventLooper
560 *************************************************************/
562 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_0(int32 arg
,...) { return B_ERROR
; }
563 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_1(int32 arg
,...) { return B_ERROR
; }
564 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_2(int32 arg
,...) { return B_ERROR
; }
565 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_3(int32 arg
,...) { return B_ERROR
; }
566 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_4(int32 arg
,...) { return B_ERROR
; }
567 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_5(int32 arg
,...) { return B_ERROR
; }
568 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_6(int32 arg
,...) { return B_ERROR
; }
569 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_7(int32 arg
,...) { return B_ERROR
; }
570 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_8(int32 arg
,...) { return B_ERROR
; }
571 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_9(int32 arg
,...) { return B_ERROR
; }
572 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_10(int32 arg
,...) { return B_ERROR
; }
573 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_11(int32 arg
,...) { return B_ERROR
; }
574 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_12(int32 arg
,...) { return B_ERROR
; }
575 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_13(int32 arg
,...) { return B_ERROR
; }
576 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_14(int32 arg
,...) { return B_ERROR
; }
577 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_15(int32 arg
,...) { return B_ERROR
; }
578 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_16(int32 arg
,...) { return B_ERROR
; }
579 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_17(int32 arg
,...) { return B_ERROR
; }
580 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_18(int32 arg
,...) { return B_ERROR
; }
581 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_19(int32 arg
,...) { return B_ERROR
; }
582 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_20(int32 arg
,...) { return B_ERROR
; }
583 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_21(int32 arg
,...) { return B_ERROR
; }
584 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_22(int32 arg
,...) { return B_ERROR
; }
585 status_t
BMediaEventLooper::_Reserved_BMediaEventLooper_23(int32 arg
,...) { return B_ERROR
; }