* Copyright (c) 2015 Dario Casalinuovo <b.vitruvio@gmail.com>
* Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files or portions
* thereof (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice
* in the binary, as well as this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <MediaEventLooper.h>
#include <TimeSource.h>
#include <scheduler.h>
#include <Buffer.h>
#include <ServerInterface.h>
#include "MediaDebug.h"
* protected BMediaEventLooper
*************************************************************/
BMediaEventLooper::~BMediaEventLooper()
{
CALLED();
if (fControlThread != -1) {
printf("You MUST call BMediaEventLooper::Quit() in your destructor!\n");
Quit();
}
}
BMediaEventLooper::BMediaEventLooper(uint32 apiVersion) :
BMediaNode("called by BMediaEventLooper"),
fControlThread(-1),
fCurrentPriority(B_URGENT_PRIORITY),
fSetPriority(B_URGENT_PRIORITY),
fRunState(B_UNREGISTERED),
fEventLatency(0),
fSchedulingLatency(0),
fBufferDuration(0),
fOfflineTime(0),
fApiVersion(apiVersion)
{
CALLED();
fEventQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this);
fRealTimeQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this);
}
void
BMediaEventLooper::NodeRegistered()
{
CALLED();
Run();
}
void
BMediaEventLooper::Start(bigtime_t performance_time)
{
CALLED();
fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_START));
}
void
BMediaEventLooper::Stop(bigtime_t performance_time,
bool immediate)
{
CALLED();
if (immediate) {
performance_time = 0;
}
fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_STOP));
}
void
BMediaEventLooper::Seek(bigtime_t media_time,
bigtime_t performance_time)
{
CALLED();
fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_SEEK, NULL,
BTimedEventQueue::B_NO_CLEANUP, 0, media_time, NULL));
}
void
BMediaEventLooper::TimeWarp(bigtime_t at_real_time,
bigtime_t to_performance_time)
{
CALLED();
fRealTimeQueue.AddEvent(media_timed_event(at_real_time, BTimedEventQueue::B_WARP,
NULL, BTimedEventQueue::B_NO_CLEANUP, 0, to_performance_time, NULL));
BMediaNode::TimeWarp(at_real_time, to_performance_time);
}
status_t
BMediaEventLooper::AddTimer(bigtime_t at_performance_time,
int32 cookie)
{
CALLED();
media_timed_event event(at_performance_time,
BTimedEventQueue::B_TIMER, NULL,
BTimedEventQueue::B_EXPIRE_TIMER);
event.data = cookie;
return EventQueue()->AddEvent(event);
}
void
BMediaEventLooper::SetRunMode(run_mode mode)
{
CALLED();
int32 priority;
priority = (mode == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority;
if (priority != fCurrentPriority) {
fCurrentPriority = priority;
if (fControlThread > 0) {
set_thread_priority(fControlThread, fCurrentPriority);
fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n",
fSchedulingLatency);
}
}
BMediaNode::SetRunMode(mode);
}
void
BMediaEventLooper::CleanUpEvent(const media_timed_event *event)
{
CALLED();
}
bigtime_t
BMediaEventLooper::OfflineTime()
{
CALLED();
return fOfflineTime;
}
void
BMediaEventLooper::ControlLoop()
{
CALLED();
status_t err = B_OK;
bigtime_t waitUntil = B_INFINITE_TIMEOUT;
bool hasRealtime = false;
bool hasEvent = false;
fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
while (RunState() != B_QUITTING) {
if (err == B_TIMED_OUT
|| err == B_WOULD_BLOCK) {
media_timed_event event;
if (hasEvent)
err = fEventQueue.RemoveFirstEvent(&event);
else if (hasRealtime)
err = fRealTimeQueue.RemoveFirstEvent(&event);
if (err == B_OK) {
bigtime_t lateness = TimeSource()->RealTime() - waitUntil;
DispatchEvent(&event, lateness, hasRealtime);
}
} else if (err != B_OK)
return;
hasRealtime = fRealTimeQueue.HasEvents();
hasEvent = fEventQueue.HasEvents();
if (hasEvent) {
waitUntil = TimeSource()->RealTimeFor(
fEventQueue.FirstEventTime(),
fEventLatency + fSchedulingLatency);
} else if (!hasRealtime)
waitUntil = B_INFINITE_TIMEOUT;
if (hasRealtime) {
bigtime_t realtimeWait = fRealTimeQueue.FirstEventTime()
- fSchedulingLatency;
if (!hasEvent || realtimeWait <= waitUntil) {
waitUntil = realtimeWait;
hasEvent = false;
} else
hasRealtime = false;
}
if (waitUntil != B_INFINITE_TIMEOUT
&& TimeSource()->RealTime() >= waitUntil) {
err = WaitForMessage(0);
} else
err = WaitForMessage(waitUntil);
}
}
thread_id
BMediaEventLooper::ControlThread()
{
CALLED();
return fControlThread;
}
* protected BMediaEventLooper
*************************************************************/
BTimedEventQueue *
BMediaEventLooper::EventQueue()
{
CALLED();
return &fEventQueue;
}
BTimedEventQueue *
BMediaEventLooper::RealTimeQueue()
{
CALLED();
return &fRealTimeQueue;
}
int32
BMediaEventLooper::Priority() const
{
CALLED();
return fCurrentPriority;
}
int32
BMediaEventLooper::RunState() const
{
PRINT(6, "CALLED BMediaEventLooper::RunState()\n");
return fRunState;
}
bigtime_t
BMediaEventLooper::EventLatency() const
{
CALLED();
return fEventLatency;
}
bigtime_t
BMediaEventLooper::BufferDuration() const
{
CALLED();
return fBufferDuration;
}
bigtime_t
BMediaEventLooper::SchedulingLatency() const
{
CALLED();
return fSchedulingLatency;
}
status_t
BMediaEventLooper::SetPriority(int32 priority)
{
CALLED();
if (priority < 5)
priority = 5;
if (priority > 120)
priority = 120;
fSetPriority = priority;
fCurrentPriority = (RunMode() == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority;
if (fControlThread > 0) {
set_thread_priority(fControlThread, fCurrentPriority);
fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n",
fSchedulingLatency);
}
return B_OK;
}
void
BMediaEventLooper::SetRunState(run_state state)
{
CALLED();
if (fRunState == B_QUITTING && state != B_TERMINATED)
return;
fRunState = state;
}
void
BMediaEventLooper::SetEventLatency(bigtime_t latency)
{
CALLED();
if (latency < 0)
latency = 0;
fEventLatency = latency;
write_port_etc(ControlPort(), GENERAL_PURPOSE_WAKEUP, 0, 0, B_TIMEOUT, 0);
}
void
BMediaEventLooper::SetBufferDuration(bigtime_t duration)
{
CALLED();
if (duration < 0)
duration = 0;
fBufferDuration = duration;
}
void
BMediaEventLooper::SetOfflineTime(bigtime_t offTime)
{
CALLED();
fOfflineTime = offTime;
}
void
BMediaEventLooper::Run()
{
CALLED();
if (fControlThread != -1)
return;
SetRunState(B_STOPPED);
char threadName[32];
sprintf(threadName, "%.20s control", Name());
fControlThread = spawn_thread(_ControlThreadStart, threadName, fCurrentPriority, this);
resume_thread(fControlThread);
fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
}
void
BMediaEventLooper::Quit()
{
CALLED();
if (fRunState == B_TERMINATED)
return;
SetRunState(B_QUITTING);
close_port(ControlPort());
if (fControlThread != -1) {
status_t err;
wait_for_thread(fControlThread, &err);
fControlThread = -1;
}
SetRunState(B_TERMINATED);
}
void
BMediaEventLooper::DispatchEvent(const media_timed_event *event,
bigtime_t lateness,
bool realTimeEvent)
{
PRINT(6, "CALLED BMediaEventLooper::DispatchEvent()\n");
HandleEvent(event, lateness, realTimeEvent);
switch (event->type) {
case BTimedEventQueue::B_START:
SetRunState(B_STARTED);
break;
case BTimedEventQueue::B_STOP:
SetRunState(B_STOPPED);
break;
case BTimedEventQueue::B_SEEK:
break;
case BTimedEventQueue::B_WARP:
break;
case BTimedEventQueue::B_TIMER:
TimerExpired(event->event_time, event->data);
break;
default:
break;
}
_DispatchCleanUp(event);
}
* private BMediaEventLooper
*************************************************************/
int32
BMediaEventLooper::_ControlThreadStart(void *arg)
{
CALLED();
((BMediaEventLooper *)arg)->SetRunState(B_STOPPED);
((BMediaEventLooper *)arg)->ControlLoop();
((BMediaEventLooper *)arg)->SetRunState(B_QUITTING);
return 0;
}
void
BMediaEventLooper::_CleanUpEntry(const media_timed_event *event,
void *context)
{
PRINT(6, "CALLED BMediaEventLooper::_CleanUpEntry()\n");
((BMediaEventLooper *)context)->_DispatchCleanUp(event);
}
void
BMediaEventLooper::_DispatchCleanUp(const media_timed_event *event)
{
PRINT(6, "CALLED BMediaEventLooper::_DispatchCleanUp()\n");
if (event->cleanup >= BTimedEventQueue::B_USER_CLEANUP)
CleanUpEvent(event);
}
// unimplemented
BMediaEventLooper::BMediaEventLooper(const BMediaEventLooper &)
BMediaEventLooper &BMediaEventLooper::operator=(const BMediaEventLooper &)
*/
* protected BMediaEventLooper
*************************************************************/
status_t
BMediaEventLooper::DeleteHook(BMediaNode *node)
{
CALLED();
Quit();
return BMediaNode::DeleteHook(node);
}
* private BMediaEventLooper
*************************************************************/
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_0(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_1(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_2(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_3(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_4(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_5(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_6(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_7(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_8(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_9(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_10(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_11(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_12(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_13(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_14(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_15(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_16(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_17(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_18(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_19(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_20(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_21(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_22(int32 arg,...) { return B_ERROR; }
status_t BMediaEventLooper::_Reserved_BMediaEventLooper_23(int32 arg,...) { return B_ERROR; }