omxplayer/OMXClock.cpp
2014-07-24 15:23:38 +01:00

581 lines
13 KiB
C++

/*
* Copyright (C) 2005-2013 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#if (defined HAVE_CONFIG_H) && (!defined WIN32)
#include "config.h"
#elif defined(_WIN32)
#include "system.h"
#endif
#if defined(HAVE_OMXLIB)
//#include "video/VideoReferenceClock.h"
//#include "settings/Settings.h"
#include "OMXClock.h"
#define OMX_PRE_ROLL 200
#define TP(speed) ((speed) < 0 || (speed) > 4*DVD_PLAYSPEED_NORMAL)
OMXClock::OMXClock()
{
m_dllAvFormat.Load();
m_pause = false;
m_omx_speed = DVD_PLAYSPEED_NORMAL;
m_WaitMask = 0;
m_eState = OMX_TIME_ClockStateStopped;
m_eClock = OMX_TIME_RefClockNone;
m_last_media_time = 0.0f;
m_last_media_time_read = 0.0f;
pthread_mutex_init(&m_lock, NULL);
}
OMXClock::~OMXClock()
{
OMXDeinitialize();
m_dllAvFormat.Unload();
pthread_mutex_destroy(&m_lock);
}
void OMXClock::Lock()
{
pthread_mutex_lock(&m_lock);
}
void OMXClock::UnLock()
{
pthread_mutex_unlock(&m_lock);
}
void OMXClock::OMXSetClockPorts(OMX_TIME_CONFIG_CLOCKSTATETYPE *clock, bool has_video, bool has_audio)
{
if(m_omx_clock.GetComponent() == NULL)
return;
if(!clock)
return;
clock->nWaitMask = 0;
if(has_audio)
{
clock->nWaitMask |= OMX_CLOCKPORT0;
}
if(has_video)
{
clock->nWaitMask |= OMX_CLOCKPORT1;
}
}
bool OMXClock::OMXSetReferenceClock(bool has_audio, bool lock /* = true */)
{
if(lock)
Lock();
bool ret = true;
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock;
OMX_INIT_STRUCTURE(refClock);
if(has_audio)
refClock.eClock = OMX_TIME_RefClockAudio;
else
refClock.eClock = OMX_TIME_RefClockVideo;
if (refClock.eClock != m_eClock)
{
CLog::Log(LOGNOTICE, "OMXClock using %s as reference", refClock.eClock == OMX_TIME_RefClockVideo ? "video" : "audio");
omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeActiveRefClock, &refClock);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::OMXSetReferenceClock error setting OMX_IndexConfigTimeActiveRefClock");
ret = false;
}
m_eClock = refClock.eClock;
}
m_last_media_time = 0.0f;
if(lock)
UnLock();
return ret;
}
bool OMXClock::OMXInitialize()
{
std::string componentName = "";
m_pause = false;
componentName = "OMX.broadcom.clock";
if(!m_omx_clock.Initialize((const std::string)componentName, OMX_IndexParamOtherInit))
return false;
return true;
}
void OMXClock::OMXDeinitialize()
{
if(m_omx_clock.GetComponent() == NULL)
return;
m_omx_clock.Deinitialize();
m_omx_speed = DVD_PLAYSPEED_NORMAL;
m_last_media_time = 0.0f;
}
bool OMXClock::OMXStateExecute(bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(lock)
Lock();
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
if(m_omx_clock.GetState() != OMX_StateExecuting)
{
OMXStateIdle(false);
omx_err = m_omx_clock.SetStateForComponent(OMX_StateExecuting);
if (omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::StateExecute m_omx_clock.SetStateForComponent\n");
if(lock)
UnLock();
return false;
}
}
m_last_media_time = 0.0f;
if(lock)
UnLock();
return true;
}
void OMXClock::OMXStateIdle(bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return;
if(lock)
Lock();
if(m_omx_clock.GetState() != OMX_StateIdle)
m_omx_clock.SetStateForComponent(OMX_StateIdle);
m_last_media_time = 0.0f;
if(lock)
UnLock();
}
COMXCoreComponent *OMXClock::GetOMXClock()
{
return &m_omx_clock;
}
bool OMXClock::OMXStop(bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(lock)
Lock();
CLog::Log(LOGDEBUG, "OMXClock::OMXStop\n");
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
OMX_TIME_CONFIG_CLOCKSTATETYPE clock;
OMX_INIT_STRUCTURE(clock);
clock.eState = OMX_TIME_ClockStateStopped;
clock.nOffset = ToOMXTime(-1000LL * OMX_PRE_ROLL);
omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::Stop error setting OMX_IndexConfigTimeClockState\n");
if(lock)
UnLock();
return false;
}
m_eState = clock.eState;
m_last_media_time = 0.0f;
if(lock)
UnLock();
return true;
}
bool OMXClock::OMXStep(int steps /* = 1 */, bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(lock)
Lock();
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
OMX_PARAM_U32TYPE param;
OMX_INIT_STRUCTURE(param);
param.nPortIndex = OMX_ALL;
param.nU32 = steps;
omx_err = m_omx_clock.SetConfig(OMX_IndexConfigSingleStep, &param);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::Error setting OMX_IndexConfigSingleStep\n");
if(lock)
UnLock();
return false;
}
m_last_media_time = 0.0f;
if(lock)
UnLock();
CLog::Log(LOGDEBUG, "OMXClock::Step (%d)", steps);
return true;
}
bool OMXClock::OMXReset(bool has_video, bool has_audio, bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(lock)
Lock();
if(!OMXSetReferenceClock(has_audio, false))
{
if(lock)
UnLock();
return false;
}
if (m_eState == OMX_TIME_ClockStateStopped)
{
OMX_TIME_CONFIG_CLOCKSTATETYPE clock;
OMX_INIT_STRUCTURE(clock);
clock.eState = OMX_TIME_ClockStateWaitingForStartTime;
clock.nOffset = ToOMXTime(-1000LL * OMX_PRE_ROLL);
OMXSetClockPorts(&clock, has_video, has_audio);
if(clock.nWaitMask)
{
OMX_ERRORTYPE omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::OMXReset error setting OMX_IndexConfigTimeClockState\n");
if(lock)
UnLock();
return false;
}
CLog::Log(LOGDEBUG, "OMXClock::OMXReset audio / video : %d / %d wait mask %d->%d state : %d->%d\n",
has_audio, has_video, m_WaitMask, clock.nWaitMask, m_eState, clock.eState);
if (m_eState != OMX_TIME_ClockStateStopped)
m_WaitMask = clock.nWaitMask;
m_eState = clock.eState;
}
}
m_last_media_time = 0.0f;
if(lock)
UnLock();
return true;
}
double OMXClock::OMXMediaTime(bool lock /* = true */)
{
double pts = 0.0;
if(m_omx_clock.GetComponent() == NULL)
return 0;
double now = GetAbsoluteClock();
if (now - m_last_media_time_read > DVD_MSEC_TO_TIME(100) || m_last_media_time == 0.0)
{
if(lock)
Lock();
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
OMX_INIT_STRUCTURE(timeStamp);
timeStamp.nPortIndex = m_omx_clock.GetInputPort();
omx_err = m_omx_clock.GetConfig(OMX_IndexConfigTimeCurrentMediaTime, &timeStamp);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::MediaTime error getting OMX_IndexConfigTimeCurrentMediaTime\n");
if(lock)
UnLock();
return 0;
}
pts = FromOMXTime(timeStamp.nTimestamp);
//CLog::Log(LOGINFO, "OMXClock::MediaTime %.2f (%.2f, %.2f)", pts, m_last_media_time, now - m_last_media_time_read);
m_last_media_time = pts;
m_last_media_time_read = now;
if(lock)
UnLock();
}
else
{
double speed = m_pause ? 0.0 : (double)m_omx_speed / DVD_PLAYSPEED_NORMAL;
pts = m_last_media_time + (now - m_last_media_time_read) * speed;
//CLog::Log(LOGINFO, "OMXClock::MediaTime cached %.2f (%.2f, %.2f)", pts, m_last_media_time, now - m_last_media_time_read);
}
return pts;
}
double OMXClock::OMXClockAdjustment(bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return 0;
if(lock)
Lock();
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
double pts = 0;
OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
OMX_INIT_STRUCTURE(timeStamp);
timeStamp.nPortIndex = m_omx_clock.GetInputPort();
omx_err = m_omx_clock.GetConfig(OMX_IndexConfigClockAdjustment, &timeStamp);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::MediaTime error getting OMX_IndexConfigClockAdjustment\n");
if(lock)
UnLock();
return 0;
}
pts = (double)FromOMXTime(timeStamp.nTimestamp);
//CLog::Log(LOGINFO, "OMXClock::ClockAdjustment %.0f %.0f\n", (double)FromOMXTime(timeStamp.nTimestamp), pts);
if(lock)
UnLock();
return pts;
}
// Set the media time, so calls to get media time use the updated value,
// useful after a seek so mediatime is updated immediately (rather than waiting for first decoded packet)
bool OMXClock::OMXMediaTime(double pts, bool lock /* = true*/)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(lock)
Lock();
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
OMX_INDEXTYPE index;
OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
OMX_INIT_STRUCTURE(timeStamp);
timeStamp.nPortIndex = m_omx_clock.GetInputPort();
if(m_eClock == OMX_TIME_RefClockAudio)
index = OMX_IndexConfigTimeCurrentAudioReference;
else
index = OMX_IndexConfigTimeCurrentVideoReference;
timeStamp.nTimestamp = ToOMXTime(pts);
omx_err = m_omx_clock.SetConfig(index, &timeStamp);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::OMXMediaTime error setting %s", index == OMX_IndexConfigTimeCurrentAudioReference ?
"OMX_IndexConfigTimeCurrentAudioReference":"OMX_IndexConfigTimeCurrentVideoReference");
if(lock)
UnLock();
return false;
}
CLog::Log(LOGDEBUG, "OMXClock::OMXMediaTime set config %s = %.2f", index == OMX_IndexConfigTimeCurrentAudioReference ?
"OMX_IndexConfigTimeCurrentAudioReference":"OMX_IndexConfigTimeCurrentVideoReference", pts);
m_last_media_time = 0.0f;
if(lock)
UnLock();
return true;
}
bool OMXClock::OMXPause(bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(!m_pause)
{
if(lock)
Lock();
if (OMXSetSpeed(0, false, true))
m_pause = true;
m_last_media_time = 0.0f;
if(lock)
UnLock();
}
return m_pause == true;
}
bool OMXClock::OMXResume(bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(m_pause)
{
if(lock)
Lock();
if (OMXSetSpeed(m_omx_speed, false, true))
m_pause = false;
m_last_media_time = 0.0f;
if(lock)
UnLock();
}
return m_pause == false;
}
bool OMXClock::OMXSetSpeed(int speed, bool lock /* = true */, bool pause_resume /* = false */)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(lock)
Lock();
CLog::Log(LOGDEBUG, "OMXClock::OMXSetSpeed(%.2f) pause_resume:%d", (float)speed / (float)DVD_PLAYSPEED_NORMAL, pause_resume);
if (pause_resume)
{
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
OMX_TIME_CONFIG_SCALETYPE scaleType;
OMX_INIT_STRUCTURE(scaleType);
if (TP(speed))
scaleType.xScale = 0; // for trickplay we just pause, and single step
else
scaleType.xScale = (speed << 16) / DVD_PLAYSPEED_NORMAL;
omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeScale, &scaleType);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::OMXSetSpeed error setting OMX_IndexConfigTimeClockState\n");
if(lock)
UnLock();
return false;
}
}
if (!pause_resume)
m_omx_speed = speed;
m_last_media_time = 0.0f;
if(lock)
UnLock();
return true;
}
bool OMXClock::HDMIClockSync(bool lock /* = true */)
{
if(m_omx_clock.GetComponent() == NULL)
return false;
if(lock)
Lock();
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
OMX_CONFIG_LATENCYTARGETTYPE latencyTarget;
OMX_INIT_STRUCTURE(latencyTarget);
latencyTarget.nPortIndex = OMX_ALL;
latencyTarget.bEnabled = OMX_TRUE;
latencyTarget.nFilter = 10;
latencyTarget.nTarget = 0;
latencyTarget.nShift = 3;
latencyTarget.nSpeedFactor = -60;
latencyTarget.nInterFactor = 100;
latencyTarget.nAdjCap = 100;
omx_err = m_omx_clock.SetConfig(OMX_IndexConfigLatencyTarget, &latencyTarget);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "OMXClock::Speed error setting OMX_IndexConfigLatencyTarget\n");
if(lock)
UnLock();
return false;
}
m_last_media_time = 0.0f;
if(lock)
UnLock();
return true;
}
void OMXClock::OMXSleep(unsigned int dwMilliSeconds)
{
struct timespec req;
req.tv_sec = dwMilliSeconds / 1000;
req.tv_nsec = (dwMilliSeconds % 1000) * 1000000;
while ( nanosleep(&req, &req) == -1 && errno == EINTR && (req.tv_nsec > 0 || req.tv_sec > 0));
}
static int64_t CurrentHostCounter(void)
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return( ((int64_t)now.tv_sec * 1000000000L) + now.tv_nsec );
}
int64_t OMXClock::GetAbsoluteClock()
{
return CurrentHostCounter()/1000;
}
double OMXClock::GetClock(bool interpolated /*= true*/)
{
return GetAbsoluteClock();
}
#endif