summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Reufer <thomas@reufer.ch>2015-04-15 19:34:34 +0200
committerThomas Reufer <thomas@reufer.ch>2015-04-15 19:34:34 +0200
commit72aa7f44996934618ea9b6869bcbff252c61f0e8 (patch)
treeb17e4e2eda5d6b247372bf37ec1c549a378c1131
parent88d83a310d80472a9809b608a7cc08bc342db456 (diff)
downloadvdr-plugin-rpihddevice-72aa7f44996934618ea9b6869bcbff252c61f0e8.tar.gz
vdr-plugin-rpihddevice-72aa7f44996934618ea9b6869bcbff252c61f0e8.tar.bz2
reworked clock stretching in live mode based on buffer usage
-rw-r--r--HISTORY1
-rw-r--r--Makefile6
-rw-r--r--omx.c131
-rw-r--r--omx.h16
-rw-r--r--omxdevice.c156
-rw-r--r--omxdevice.h16
6 files changed, 154 insertions, 172 deletions
diff --git a/HISTORY b/HISTORY
index 7475f0a..7a22637 100644
--- a/HISTORY
+++ b/HISTORY
@@ -2,6 +2,7 @@ VDR Plugin 'rpihddevice' Revision History
-----------------------------------------
- new:
+ - reworked clock stretching in live mode based on buffer usage
- added command line argument to disable OSD
- combined digital audio format options to one single setup option
- added font kerning
diff --git a/Makefile b/Makefile
index fa3ca1c..3d2167a 100644
--- a/Makefile
+++ b/Makefile
@@ -69,9 +69,9 @@ ifeq ($(DEBUG), 1)
DEFINES += -DDEBUG
endif
-DEBUG_LATENCY ?= 0
-ifeq ($(DEBUG_LATENCY), 1)
- DEFINES += -DDEBUG_LATENCY
+DEBUG_BUFFERSTAT ?= 0
+ifeq ($(DEBUG_BUFFERSTAT), 1)
+ DEFINES += -DDEBUG_BUFFERSTAT
endif
DEBUG_BUFFERS ?= 0
diff --git a/omx.c b/omx.c
index af39d1e..62107c2 100644
--- a/omx.c
+++ b/omx.c
@@ -20,6 +20,14 @@ extern "C" {
#define OMX_PRE_ROLL 0
+// default: 20x 81920 bytes, now 128x 64k (8M)
+#define OMX_VIDEO_BUFFERS 128
+#define OMX_VIDEO_BUFFERSIZE KILOBYTE(64);
+
+// default: 16x 4096 bytes, now 128x 16k (2M)
+#define OMX_AUDIO_BUFFERS 128
+#define OMX_AUDIO_BUFFERSIZE KILOBYTE(16);
+
#define OMX_INIT_STRUCT(a) \
memset(&(a), 0, sizeof(a)); \
(a).nSize = sizeof(a); \
@@ -61,7 +69,8 @@ public:
enum eEvent {
ePortSettingsChanged,
eConfigChanged,
- eEndOfStream
+ eEndOfStream,
+ eBufferEmptied
};
struct Event
@@ -88,20 +97,15 @@ public:
delete m_mutex;
}
- Event* Wait(void)
+ Event* Get(void)
{
Event* event = 0;
- while (true)
+ if (!m_events.empty())
{
- if (!m_events.empty())
- {
- m_mutex->Lock();
- event = m_events.front();
- m_events.pop();
- m_mutex->Unlock();
- break;
- }
- m_signal->Wait(10);
+ m_mutex->Lock();
+ event = m_events.front();
+ m_events.pop();
+ m_mutex->Unlock();
}
return event;
}
@@ -176,10 +180,10 @@ const char* cOmx::errStr(int err)
void cOmx::Action(void)
{
+ cTimeMs timer;
while (Running())
{
- cOmxEvents::Event* event = m_portEvents->Wait();
- if (event)
+ while (cOmxEvents::Event* event = m_portEvents->Get())
{
switch (event->event)
{
@@ -199,13 +203,71 @@ void cOmx::Action(void)
m_onEndOfStream(m_onEndOfStreamData);
break;
+ case cOmxEvents::eBufferEmptied:
+ HandlePortBufferEmptied((eOmxComponent)event->data);
+ break;
+
default:
break;
}
delete event;
}
+ cCondWait::SleepMs(10);
+
+ if (timer.TimedOut())
+ {
+ timer.Set(100);
+ Lock();
+ for (int i = BUFFERSTAT_FILTER_SIZE - 1; i > 0; i--)
+ {
+ m_usedAudioBuffers[i] = m_usedAudioBuffers[i - 1];
+ m_usedVideoBuffers[i] = m_usedVideoBuffers[i - 1];
+ }
+ Unlock();
+ }
+ }
+}
+
+bool cOmx::Poll(void)
+{
+ int freeAudioBuffers = 0, freeVideoBuffers = 0;
+ GetBufferUsage(freeAudioBuffers, freeVideoBuffers);
+ return freeAudioBuffers < 90 && freeVideoBuffers < 90;
+}
+
+void cOmx::GetBufferUsage(int &audio, int &video)
+{
+ audio = 0;
+ video = 0;
+ for (int i = 0; i < BUFFERSTAT_FILTER_SIZE; i++)
+ {
+ audio += m_usedAudioBuffers[i];
+ video += m_usedVideoBuffers[i];
+ }
+ audio = audio * 100 / BUFFERSTAT_FILTER_SIZE / OMX_AUDIO_BUFFERS;
+ video = video * 100 / BUFFERSTAT_FILTER_SIZE / OMX_AUDIO_BUFFERS;
+}
+
+void cOmx::HandlePortBufferEmptied(eOmxComponent component)
+{
+ Lock();
+
+ switch (component)
+ {
+ case eVideoDecoder:
+ m_usedVideoBuffers[0]--;
+ break;
+
+ case eAudioRender:
+ m_usedAudioBuffers[0]--;
+ break;
+
+ default:
+ ELOG("HandlePortBufferEmptied: invalid component!");
+ break;
}
+ Unlock();
}
void cOmx::HandlePortSettingsChanged(unsigned int portId)
@@ -321,10 +383,11 @@ void cOmx::HandlePortSettingsChanged(unsigned int portId)
void cOmx::OnBufferEmpty(void *instance, COMPONENT_T *comp)
{
cOmx* omx = static_cast <cOmx*> (instance);
- if (comp == omx->m_comp[eVideoDecoder])
- omx->m_freeVideoBuffers = true;
- else if (comp == omx->m_comp[eAudioRender])
- omx->m_freeAudioBuffers = true;
+ omx->m_portEvents->Add(
+ new cOmxEvents::Event(cOmxEvents::eBufferEmptied,
+ comp == omx->m_comp[eVideoDecoder] ? eVideoDecoder :
+ comp == omx->m_comp[eAudioRender] ? eAudioRender :
+ eInvalidComponent));
}
void cOmx::OnPortSettingsChanged(void *instance, COMPONENT_T *comp, OMX_U32 data)
@@ -360,8 +423,6 @@ cOmx::cOmx() :
m_setAudioStartTime(false),
m_setVideoStartTime(false),
m_setVideoDiscontinuity(false),
- m_freeAudioBuffers(true),
- m_freeVideoBuffers(true),
m_spareAudioBuffers(0),
m_spareVideoBuffers(0),
m_clockReference(eClockRefNone),
@@ -934,10 +995,10 @@ int cOmx::SetVideoCodec(cVideoCodec::eCodec codec)
OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
ELOG("failed to get video decoder port parameters!");
- // default: 20x 81920 bytes, now 64x 64k (4M)
- param.nBufferSize = KILOBYTE(64);
- param.nBufferCountActual = 64;
- m_freeVideoBuffers = true;
+ param.nBufferSize = OMX_VIDEO_BUFFERSIZE;
+ param.nBufferCountActual = OMX_VIDEO_BUFFERS;
+ for (int i = 0; i < BUFFERSTAT_FILTER_SIZE; i++)
+ m_usedVideoBuffers[i] = 0;
if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
@@ -1102,10 +1163,10 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels,
OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
ELOG("failed to get audio render port parameters!");
- // default: 16x 4096 bytes, now 128x 16k (2M)
- param.nBufferSize = KILOBYTE(16);
- param.nBufferCountActual = 128;
- m_freeAudioBuffers = true;
+ param.nBufferSize = OMX_AUDIO_BUFFERSIZE;
+ param.nBufferCountActual = OMX_AUDIO_BUFFERS;
+ for (int i = 0; i < BUFFERSTAT_FILTER_SIZE; i++)
+ m_usedAudioBuffers[i] = 0;
if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
@@ -1179,7 +1240,11 @@ OMX_BUFFERHEADERTYPE* cOmx::GetAudioBuffer(uint64_t pts)
buf->pAppPrivate = 0;
}
else
+ {
buf = ilclient_get_input_buffer(m_comp[eAudioRender], 100, 0);
+ if (buf)
+ m_usedAudioBuffers[0]++;
+ }
if (buf)
{
@@ -1195,9 +1260,6 @@ OMX_BUFFERHEADERTYPE* cOmx::GetAudioBuffer(uint64_t pts)
cOmx::PtsToTicks(pts, buf->nTimeStamp);
m_setAudioStartTime = false;
}
- else
- m_freeAudioBuffers = false;
-
Unlock();
return buf;
}
@@ -1214,7 +1276,11 @@ OMX_BUFFERHEADERTYPE* cOmx::GetVideoBuffer(uint64_t pts)
buf->pAppPrivate = 0;
}
else
+ {
buf = ilclient_get_input_buffer(m_comp[eVideoDecoder], 130, 0);
+ if (buf)
+ m_usedVideoBuffers[0]++;
+ }
if (buf)
{
@@ -1234,9 +1300,6 @@ OMX_BUFFERHEADERTYPE* cOmx::GetVideoBuffer(uint64_t pts)
m_setVideoStartTime = false;
m_setVideoDiscontinuity = false;
}
- else
- m_freeVideoBuffers = false;
-
Unlock();
return buf;
}
diff --git a/omx.h b/omx.h
index c678336..dda8eec 100644
--- a/omx.h
+++ b/omx.h
@@ -86,12 +86,14 @@ public:
OMX_BUFFERHEADERTYPE* GetAudioBuffer(uint64_t pts = 0);
OMX_BUFFERHEADERTYPE* GetVideoBuffer(uint64_t pts = 0);
- bool inline PollVideoBuffers() { return m_freeVideoBuffers; }
- bool inline PollAudioBuffers() { return m_freeAudioBuffers; }
+
+ bool Poll(void);
bool EmptyAudioBuffer(OMX_BUFFERHEADERTYPE *buf);
bool EmptyVideoBuffer(OMX_BUFFERHEADERTYPE *buf);
+ void GetBufferUsage(int &audio, int &video);
+
private:
virtual void Action(void);
@@ -109,7 +111,8 @@ private:
eVideoScheduler,
eVideoRender,
eAudioRender,
- eNumComponents
+ eNumComponents,
+ eInvalidComponent
};
enum eOmxTunnel {
@@ -139,8 +142,10 @@ private:
bool m_setVideoStartTime;
bool m_setVideoDiscontinuity;
- bool m_freeAudioBuffers;
- bool m_freeVideoBuffers;
+#define BUFFERSTAT_FILTER_SIZE 64
+
+ int m_usedAudioBuffers[BUFFERSTAT_FILTER_SIZE];
+ int m_usedVideoBuffers[BUFFERSTAT_FILTER_SIZE];
OMX_BUFFERHEADERTYPE* m_spareAudioBuffers;
OMX_BUFFERHEADERTYPE* m_spareVideoBuffers;
@@ -160,6 +165,7 @@ private:
void (*m_onStreamStart)(void*);
void *m_onStreamStartData;
+ void HandlePortBufferEmptied(eOmxComponent component);
void HandlePortSettingsChanged(unsigned int portId);
void SetBufferStallThreshold(int delayMs);
bool IsBufferStall(void);
diff --git a/omxdevice.c b/omxdevice.c
index 7ab563b..e649a5a 100644
--- a/omxdevice.c
+++ b/omxdevice.c
@@ -42,6 +42,7 @@ cOmxDevice::cOmxDevice(void (*onPrimaryDevice)(void)) :
m_omx(new cOmx()),
m_audio(new cRpiAudioDecoder(m_omx)),
m_mutex(new cMutex()),
+ m_timer(new cTimeMs()),
m_videoCodec(cVideoCodec::eInvalid),
m_liveSpeed(eNoCorrection),
m_playbackSpeed(eNormal),
@@ -52,12 +53,7 @@ cOmxDevice::cOmxDevice(void (*onPrimaryDevice)(void)) :
m_playDirection(0),
m_trickRequest(0),
m_audioPts(0),
- m_videoPts(0),
- m_audioId(0),
- m_latencySamples(0),
- m_latencyTarget(0),
- m_posMaxCorrections(0),
- m_negMaxCorrections(0)
+ m_videoPts(0)
{
}
@@ -68,6 +64,7 @@ cOmxDevice::~cOmxDevice()
delete m_omx;
delete m_audio;
delete m_mutex;
+ delete m_timer;
}
int cOmxDevice::Init(void)
@@ -255,7 +252,6 @@ int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
if (!m_hasAudio)
{
m_hasAudio = true;
- m_audioId = Id;
m_omx->SetClockReference(cOmx::eClockRefAudio);
if (!m_hasVideo)
@@ -264,9 +260,6 @@ int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
m_omx->SetClockScale(s_playbackSpeeds[m_direction][m_playbackSpeed]);
m_omx->StartClock(m_hasVideo, m_hasAudio);
}
-
- if (Transferring())
- ResetLatency();
}
int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
@@ -280,16 +273,6 @@ int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
m_audioPts = pts;
}
- if (Transferring() && pts)
- {
- if (m_audioId != Id)
- {
- ResetLatency();
- m_audioId = Id;
- }
- UpdateLatency(pts);
- }
-
int ret = Length;
int length = Length - PesPayloadOffset(Data);
@@ -310,6 +293,13 @@ int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
ret = 0;
}
m_mutex->Unlock();
+
+ if (Transferring() && !ret)
+ DBG("failed to write %d bytes of audio packet!", Length);
+
+ if (ret && Transferring())
+ AdjustLiveSpeed();
+
return ret;
}
@@ -358,9 +348,6 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool EndOfFrame)
m_omx->SetClockScale(s_playbackSpeeds[m_direction][m_playbackSpeed]);
m_omx->StartClock(m_hasVideo, m_hasAudio);
}
-
- if (Transferring())
- ResetLatency();
}
if (m_hasVideo)
@@ -371,9 +358,6 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool EndOfFrame)
if (m_trickRequest && pts && m_videoPts)
PtsTracker(PtsDiff(m_videoPts, pts));
- if (!m_hasAudio && Transferring() && pts)
- UpdateLatency(pts);
-
// skip PES header, proceed with payload towards OMX
Length -= PesPayloadOffset(Data);
Data += PesPayloadOffset(Data);
@@ -409,6 +393,13 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool EndOfFrame)
}
}
m_mutex->Unlock();
+
+ if (Transferring() && !ret)
+ DBG("failed to write %d bytes of video packet!", Length);
+
+ if (ret && Transferring())
+ AdjustLiveSpeed();
+
return ret;
}
@@ -585,103 +576,36 @@ bool cOmxDevice::HasIBPTrickSpeed(void)
return !m_hasVideo;
}
-void cOmxDevice::UpdateLatency(int64_t pts)
+void cOmxDevice::AdjustLiveSpeed(void)
{
- if (!pts || !m_omx->IsClockRunning())
- return;
-
- int64_t stc = m_omx->GetSTC();
- if (!stc || pts <= stc)
- return;
-
- for (int i = LATENCY_FILTER_SIZE - 1; i > 0; i--)
- m_latency[i] = m_latency[i - 1];
- m_latency[0] = (pts - stc) / 90;
-
- if (m_latencySamples < LATENCY_FILTER_SIZE - 1)
+ if (m_timer->TimedOut())
{
- m_latencySamples++;
- return;
- }
+ int usedBuffers, usedAudioBuffers, usedVideoBuffers;
+ m_omx->GetBufferUsage(usedAudioBuffers, usedVideoBuffers);
+ usedBuffers = m_hasAudio ? usedAudioBuffers : usedVideoBuffers;
-#ifdef DEBUG_LATENCY
- eLiveSpeed oldSpeed = m_liveSpeed;
-#endif
- int average = 0;
-
- for (int i = 0; i < LATENCY_FILTER_SIZE; i++)
- average += m_latency[i];
- average = average / LATENCY_FILTER_SIZE;
-
- if (!m_latencyTarget)
- m_latencyTarget = 1.4f * average;
+ if (usedBuffers < 5)
+ m_liveSpeed = eNegCorrection;
- if (average > 2.0f * m_latencyTarget)
- {
- if (m_liveSpeed < ePosMaxCorrection)
- {
- m_liveSpeed = ePosMaxCorrection;
- m_posMaxCorrections++;
- DBG("latency too big, speeding up...");
- }
- }
- else if (average < 0.5f * m_latencyTarget)
- {
- if (m_liveSpeed > eNegMaxCorrection)
- {
- m_liveSpeed = eNegMaxCorrection;
- m_negMaxCorrections++;
- DBG("latency too small, slowing down...");
- }
- }
- else if (average > 1.1f * m_latencyTarget)
- {
- if (m_liveSpeed < ePosMaxCorrection)
+ else if (usedBuffers > 15)
m_liveSpeed = ePosCorrection;
- }
- else if (average < 0.9f * m_latencyTarget)
- {
- if (m_liveSpeed > eNegMaxCorrection)
- m_liveSpeed = eNegCorrection;
- }
- else if (average > m_latencyTarget)
- {
- if (m_liveSpeed < eNoCorrection)
- m_liveSpeed = eNoCorrection;
- }
- else if (average < m_latencyTarget)
- {
- if (m_liveSpeed > eNoCorrection)
- m_liveSpeed = eNoCorrection;
- } else
- m_liveSpeed = eNoCorrection;
- m_omx->SetClockScale(s_liveSpeeds[m_liveSpeed]);
+ else if ((usedBuffers > 10 && m_liveSpeed == eNegCorrection) ||
+ (usedBuffers < 10 && m_liveSpeed == ePosCorrection))
+ m_liveSpeed = eNoCorrection;
-#ifdef DEBUG_LATENCY
- if (oldSpeed != m_liveSpeed)
- {
- DLOG("%s%s latency = %4dms, target = %4dms, corr = %s, "
- "max neg/pos corr = %d/%d",
- m_hasAudio ? "A" : "-", m_hasVideo ? "V" : "-",
- average, m_latencyTarget,
- m_liveSpeed == eNegMaxCorrection ? "--| " :
- m_liveSpeed == eNegCorrection ? " -| " :
- m_liveSpeed == eNoCorrection ? " | " :
- m_liveSpeed == ePosCorrection ? " |+ " :
- m_liveSpeed == ePosMaxCorrection ? " |++" : " ? ",
- m_negMaxCorrections, m_posMaxCorrections);
- }
+#ifdef DEBUG_BUFFERSTAT
+ DLOG("buffer usage: A=%3d%%, V=%3d%%, Corr=%d",
+ usedAudioBuffers, usedVideoBuffers,
+ m_liveSpeed == eNegMaxCorrection ? -2 :
+ m_liveSpeed == eNegCorrection ? -1 :
+ m_liveSpeed == eNoCorrection ? 0 :
+ m_liveSpeed == ePosCorrection ? 1 :
+ m_liveSpeed == ePosMaxCorrection ? 2 : 0);
#endif
-}
-
-void cOmxDevice::ResetLatency(void)
-{
- m_latencySamples = - LATENCY_FILTER_PREROLL;
- m_latencyTarget = 0;
- m_liveSpeed = eNoCorrection;
- m_posMaxCorrections = 0;
- m_negMaxCorrections = 0;
+ m_omx->SetClockScale(s_liveSpeeds[m_liveSpeed]);
+ m_timer->Set(1000);
+ }
}
void cOmxDevice::HandleBufferStall()
@@ -783,7 +707,7 @@ bool cOmxDevice::Poll(cPoller &Poller, int TimeoutMs)
{
cTimeMs time;
time.Set();
- while (!m_omx->PollVideoBuffers() || !m_audio->Poll())
+ while (!m_omx->Poll() || !m_audio->Poll())
{
if (time.Elapsed() >= (unsigned)TimeoutMs)
return false;
diff --git a/omxdevice.h b/omxdevice.h
index 6547631..22476d5 100644
--- a/omxdevice.h
+++ b/omxdevice.h
@@ -156,12 +156,12 @@ private:
void ApplyTrickSpeed(int trickSpeed, bool forward);
void PtsTracker(int64_t ptsDiff);
- void UpdateLatency(int64_t pts);
- void ResetLatency(void);
+ void AdjustLiveSpeed(void);
cOmx *m_omx;
cRpiAudioDecoder *m_audio;
cMutex *m_mutex;
+ cTimeMs *m_timer;
cVideoCodec::eCodec m_videoCodec;
@@ -178,18 +178,6 @@ private:
int64_t m_audioPts;
int64_t m_videoPts;
-
- uchar m_audioId;
-
-#define LATENCY_FILTER_SIZE 8
-#define LATENCY_FILTER_PREROLL 16
-
- int m_latency[LATENCY_FILTER_SIZE];
- int m_latencySamples;
- int m_latencyTarget;
-
- int m_posMaxCorrections;
- int m_negMaxCorrections;
};
#endif