From be4cdcf17027e2f6f5482fb13d273693145d11a1 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Thu, 22 Dec 2016 11:33:12 +0100 Subject: [PATCH] Improved handling frame numbers to have a smoother progress display during replay of recordings with B-frames --- CONTRIBUTORS | 2 ++ HISTORY | 2 ++ dvbplayer.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++----- dvbplayer.h | 6 ++++- menu.c | 67 ++++++++++++++++++++++++++++------------------------ menu.h | 3 ++- player.h | 7 +++++- ringbuffer.c | 5 ++-- ringbuffer.h | 6 +++-- 9 files changed, 120 insertions(+), 44 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index a695bc11..d89a6037 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3328,6 +3328,8 @@ Thomas Reufer for making the cPlayer member functions FramesPerSecond, GetIndex and GetReplayMode 'const' for fixing resuming replay at a given position, which was off by one frame + for improving handling frame numbers to have a smoother progress display during + replay of recordings with B-frames Eike Sauer for reporting a problem with channels that need more than 5 TS packets for detecting diff --git a/HISTORY b/HISTORY index 4edf138c..722cebfe 100644 --- a/HISTORY +++ b/HISTORY @@ -8857,3 +8857,5 @@ Video Disk Recorder Revision History 'const' (thanks to Thomas Reufer). - Fixed resuming replay at a given position, which was off by one frame (thanks to Thomas Reufer). +- Improved handling frame numbers to have a smoother progress display during + replay of recordings with B-frames (thanks to Thomas Reufer). diff --git a/dvbplayer.c b/dvbplayer.c index e5c9270b..6db497e3 100644 --- a/dvbplayer.c +++ b/dvbplayer.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbplayer.c 4.2 2016/12/22 09:40:30 kls Exp $ + * $Id: dvbplayer.c 4.3 2016/12/22 10:43:10 kls Exp $ */ #include "dvbplayer.h" @@ -17,13 +17,14 @@ // --- cPtsIndex ------------------------------------------------------------- -#define PTSINDEX_ENTRIES 500 +#define PTSINDEX_ENTRIES 1024 class cPtsIndex { private: struct tPtsIndex { uint32_t pts; // no need for 33 bit - some devices don't even supply the msb int index; + bool independent; }; tPtsIndex pi[PTSINDEX_ENTRIES]; int w, r; @@ -33,8 +34,9 @@ public: cPtsIndex(void); void Clear(void); bool IsEmpty(void); - void Put(uint32_t Pts, int Index); + void Put(uint32_t Pts, int Index, bool Independent); int FindIndex(uint32_t Pts); + int FindFrameNumber(uint32_t Pts); }; cPtsIndex::cPtsIndex(void) @@ -55,10 +57,11 @@ bool cPtsIndex::IsEmpty(void) return w == r; } -void cPtsIndex::Put(uint32_t Pts, int Index) +void cPtsIndex::Put(uint32_t Pts, int Index, bool Independent) { cMutexLock MutexLock(&mutex); pi[w].pts = Pts; + pi[w].independent = Independent; pi[w].index = Index; w = (w + 1) % PTSINDEX_ENTRIES; if (w == r) @@ -87,6 +90,36 @@ int cPtsIndex::FindIndex(uint32_t Pts) return Index; } +int cPtsIndex::FindFrameNumber(uint32_t Pts) +{ + cMutexLock MutexLock(&mutex); + if (w == r) + return lastFound; // replay always starts at an I frame + bool Valid = false; + int d; + int FrameNumber = 0; + int UnplayedIFrame = 2; // GOPs may intersect, so we're looping until we found two unplayed I frames + for (int i = r; i != w && UnplayedIFrame; ) { + d = Pts - pi[i].pts; + if (d > 0x7FFFFFFF) + d = 0xFFFFFFFF - d; // handle rollover + if (d > 0) { + if (pi[i].independent) { + FrameNumber = pi[i].index; // an I frame's index represents its frame number + Valid = true; + } + else + FrameNumber++; // for every played non-I frame, increase frame number + } + else + if (pi[i].independent) + --UnplayedIFrame; + if (++i >= PTSINDEX_ENTRIES) + i = 0; + } + return Valid ? FrameNumber : FindIndex(Pts); // fall back during trick speeds +} + // --- cNonBlockingFileReader ------------------------------------------------ class cNonBlockingFileReader : public cThread { @@ -251,6 +284,7 @@ public: virtual double FramesPerSecond(void) { return framesPerSecond; } virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId); virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false); + virtual bool GetFrameNumber(int &Current, int &Total); virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed); }; @@ -606,7 +640,7 @@ void cDvbPlayer::Action(void) pc = playFrame->Count(); if (p) { if (playFrame->Index() >= 0 && playFrame->Pts() != 0) - ptsIndex.Put(playFrame->Pts(), playFrame->Index()); + ptsIndex.Put(playFrame->Pts(), playFrame->Index(), playFrame->Independent()); if (firstPacket) { if (isPesRecording) { PlayPes(NULL, 0); @@ -880,7 +914,7 @@ void cDvbPlayer::Goto(int Index, bool Still) if (playMode == pmPause) DevicePlay(); DeviceStillPicture(b, r); - ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index); + ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index, true); } playMode = pmStill; readIndex = Index; @@ -925,6 +959,17 @@ bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame) return false; } +bool cDvbPlayer::GetFrameNumber(int &Current, int &Total) +{ + if (index) { + Current = ptsIndex.FindFrameNumber(DeviceGetSTC()); + Total = index->Last(); + return true; + } + Current = Total = -1; + return false; +} + bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed) { Play = (playMode == pmPlay || playMode == pmFast); @@ -1011,6 +1056,15 @@ bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame) return false; } +bool cDvbPlayerControl::GetFrameNumber(int &Current, int &Total) +{ + if (player) { + player->GetFrameNumber(Current, Total); + return true; + } + return false; +} + bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed) { return player && player->GetReplayMode(Play, Forward, Speed); diff --git a/dvbplayer.h b/dvbplayer.h index ef6f1fce..2fe6e4c9 100644 --- a/dvbplayer.h +++ b/dvbplayer.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbplayer.h 4.1 2015/08/02 13:01:44 kls Exp $ + * $Id: dvbplayer.h 4.2 2016/12/22 10:36:50 kls Exp $ */ #ifndef __DVBPLAYER_H @@ -50,6 +50,10 @@ public: bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false); // Returns the current and total frame index, optionally snapped to the // nearest I-frame. + bool GetFrameNumber(int &Current, int &Total); + // Returns the current and total frame number. In contrast to GetIndex(), + // this function respects the chronological order of frames, which is + // different from its index for streams containing B frames (e.g. H264) bool GetReplayMode(bool &Play, bool &Forward, int &Speed); // Returns the current replay mode (if applicable). // 'Play' tells whether we are playing or pausing, 'Forward' tells whether diff --git a/menu.c b/menu.c index 48889a48..30c95f8d 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 4.18 2016/12/13 12:49:10 kls Exp $ + * $Id: menu.c 4.19 2016/12/22 11:00:13 kls Exp $ */ #include "menu.h" @@ -5435,6 +5435,7 @@ cReplayControl::cReplayControl(bool PauseLive) lastPlay = lastForward = false; lastSpeed = -2; // an invalid value timeoutShow = 0; + lastProgressUpdate = 0; timeSearchActive = false; cRecording Recording(fileName); cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true); @@ -5583,41 +5584,43 @@ void cReplayControl::ShowMode(void) bool cReplayControl::ShowProgress(bool Initial) { int Current, Total; - - if (GetIndex(Current, Total) && Total > 0) { - if (!visible) { - displayReplay = Skins.Current()->DisplayReplay(modeOnly); - displayReplay->SetMarks(&marks); - SetNeedsFastResponse(true); - visible = true; - } - if (Initial) { - if (*fileName) { - LOCK_RECORDINGS_READ; - if (const cRecording *Recording = Recordings->GetByName(fileName)) - displayReplay->SetRecording(Recording); + if (Initial || time(NULL) - lastProgressUpdate >= 1) { + if (GetFrameNumber(Current, Total) && Total > 0) { + if (!visible) { + displayReplay = Skins.Current()->DisplayReplay(modeOnly); + displayReplay->SetMarks(&marks); + SetNeedsFastResponse(true); + visible = true; } - lastCurrent = lastTotal = -1; - } - if (Current != lastCurrent || Total != lastTotal) { - if (Setup.ShowRemainingTime || Total != lastTotal) { - int Index = Total; - if (Setup.ShowRemainingTime) - Index = Current - Index; - displayReplay->SetTotal(IndexToHMSF(Index, false, FramesPerSecond())); + if (Initial) { + if (*fileName) { + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(fileName)) + displayReplay->SetRecording(Recording); + } + lastCurrent = lastTotal = -1; + } + if (Current != lastCurrent || Total != lastTotal) { + time(&lastProgressUpdate); + if (Setup.ShowRemainingTime || Total != lastTotal) { + int Index = Total; + if (Setup.ShowRemainingTime) + Index = Current - Index; + displayReplay->SetTotal(IndexToHMSF(Index, false, FramesPerSecond())); + if (!Initial) + displayReplay->Flush(); + } + displayReplay->SetProgress(Current, Total); if (!Initial) displayReplay->Flush(); - } - displayReplay->SetProgress(Current, Total); - if (!Initial) + displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, FramesPerSecond())); displayReplay->Flush(); - displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, FramesPerSecond())); - displayReplay->Flush(); - lastCurrent = Current; + lastCurrent = Current; + } + lastTotal = Total; + ShowMode(); + return true; } - lastTotal = Total; - ShowMode(); - return true; } return false; } @@ -5858,6 +5861,8 @@ eOSState cReplayControl::ProcessKey(eKeys Key) return osEnd; if (Key == kNone && !marksModified) marks.Update(); + if (Key != kNone) + lastProgressUpdate = 0; if (visible) { if (timeoutShow && time(NULL) > timeoutShow) { Hide(); diff --git a/menu.h b/menu.h index 3f2878b3..9a971ad0 100644 --- a/menu.h +++ b/menu.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 4.4 2015/09/13 14:17:56 kls Exp $ + * $Id: menu.h 4.5 2016/12/22 10:55:36 kls Exp $ */ #ifndef __MENU_H @@ -300,6 +300,7 @@ private: bool lastPlay, lastForward; int lastSpeed; time_t timeoutShow; + time_t lastProgressUpdate; bool timeSearchActive, timeSearchHide; int timeSearchTime, timeSearchPos; void TimeSearchDisplay(void); diff --git a/player.h b/player.h index e53a952d..aeb8af8f 100644 --- a/player.h +++ b/player.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: player.h 4.1 2016/12/22 09:22:27 kls Exp $ + * $Id: player.h 4.2 2016/12/22 10:38:11 kls Exp $ */ #ifndef __PLAYER_H @@ -57,6 +57,10 @@ public: virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { return false; } // Returns the current and total frame index, optionally snapped to the // nearest I-frame. + virtual bool GetFrameNumber(int &Current, int &Total) { return false; } + // Returns the current and total frame number. In contrast to GetIndex(), + // this function respects the chronological order of frames, which is + // different from its index for streams containing B frames (e.g. H264) virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed) { return false; } // Returns the current replay mode (if applicable). // 'Play' tells whether we are playing or pausing, 'Forward' tells whether @@ -100,6 +104,7 @@ public: ///< string. The default implementation returns an empty string. double FramesPerSecond(void) const { return player->FramesPerSecond(); } bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) const { return player->GetIndex(Current, Total, SnapToIFrame); } + bool GetFrameNumber(int &Current, int &Total) const { return player->GetFrameNumber(Current, Total); } bool GetReplayMode(bool &Play, bool &Forward, int &Speed) const { return player->GetReplayMode(Play, Forward, Speed); } static void Launch(cControl *Control); static void Attach(void); diff --git a/ringbuffer.c b/ringbuffer.c index abe78990..d33a4719 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -7,7 +7,7 @@ * Parts of this file were inspired by the 'ringbuffy.c' from the * LinuxDVB driver (see linuxtv.org). * - * $Id: ringbuffer.c 2.5 2012/09/22 11:26:49 kls Exp $ + * $Id: ringbuffer.c 4.1 2016/12/22 10:26:13 kls Exp $ */ #include "ringbuffer.h" @@ -390,12 +390,13 @@ void cRingBufferLinear::Del(int Count) // --- cFrame ---------------------------------------------------------------- -cFrame::cFrame(const uchar *Data, int Count, eFrameType Type, int Index, uint32_t Pts) +cFrame::cFrame(const uchar *Data, int Count, eFrameType Type, int Index, uint32_t Pts, bool Independent) { count = abs(Count); type = Type; index = Index; pts = Pts; + independent = Type == ftAudio ? true : Independent; if (Count < 0) data = (uchar *)Data; else { diff --git a/ringbuffer.h b/ringbuffer.h index 1fff611c..9699bbc8 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ringbuffer.h 2.5 2013/02/16 15:20:37 kls Exp $ + * $Id: ringbuffer.h 4.1 2016/12/22 10:26:13 kls Exp $ */ #ifndef __RINGBUFFER_H @@ -113,8 +113,9 @@ private: eFrameType type; int index; uint32_t pts; + bool independent; public: - cFrame(const uchar *Data, int Count, eFrameType = ftUnknown, int Index = -1, uint32_t Pts = 0); + cFrame(const uchar *Data, int Count, eFrameType = ftUnknown, int Index = -1, uint32_t Pts = 0, bool independent = false); ///< Creates a new cFrame object. ///< If Count is negative, the cFrame object will take ownership of the given ///< Data. Otherwise it will allocate Count bytes of memory and copy Data. @@ -124,6 +125,7 @@ public: eFrameType Type(void) const { return type; } int Index(void) const { return index; } uint32_t Pts(void) const { return pts; } + bool Independent(void) const { return independent; } }; class cRingBufferFrame : public cRingBuffer {