diff options
| author | Thomas Reufer <thomas@reufer.ch> | 2015-04-15 19:34:34 +0200 |
|---|---|---|
| committer | Thomas Reufer <thomas@reufer.ch> | 2015-04-15 19:34:34 +0200 |
| commit | 72aa7f44996934618ea9b6869bcbff252c61f0e8 (patch) | |
| tree | b17e4e2eda5d6b247372bf37ec1c549a378c1131 | |
| parent | 88d83a310d80472a9809b608a7cc08bc342db456 (diff) | |
| download | vdr-plugin-rpihddevice-72aa7f44996934618ea9b6869bcbff252c61f0e8.tar.gz vdr-plugin-rpihddevice-72aa7f44996934618ea9b6869bcbff252c61f0e8.tar.bz2 | |
reworked clock stretching in live mode based on buffer usage
| -rw-r--r-- | HISTORY | 1 | ||||
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | omx.c | 131 | ||||
| -rw-r--r-- | omx.h | 16 | ||||
| -rw-r--r-- | omxdevice.c | 156 | ||||
| -rw-r--r-- | omxdevice.h | 16 |
6 files changed, 154 insertions, 172 deletions
@@ -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 @@ -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 @@ -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, ¶m) != 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, ¶m) != OMX_ErrorNone) @@ -1102,10 +1163,10 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, OMX_IndexParamPortDefinition, ¶m) != 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, ¶m) != 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; } @@ -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 |
