mirror of
				https://github.com/vdr-projects/vdr.git
				synced 2025-03-01 10:50:46 +00:00 
			
		
		
		
	Improved handling frame numbers to have a smoother progress display during replay of recordings with B-frames
This commit is contained in:
		| @@ -3328,6 +3328,8 @@ Thomas Reufer <thomas@reufer.ch> | ||||
|  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 <EikeSauer@t-online.de> | ||||
|  for reporting a problem with channels that need more than 5 TS packets for detecting | ||||
|   | ||||
							
								
								
									
										2
									
								
								HISTORY
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								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). | ||||
|   | ||||
							
								
								
									
										66
									
								
								dvbplayer.c
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								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); | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										67
									
								
								menu.c
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								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(); | ||||
|   | ||||
							
								
								
									
										3
									
								
								menu.h
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								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); | ||||
|   | ||||
							
								
								
									
										7
									
								
								player.h
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								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); | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user