TV Standards option for vdr-1.5.x

Message ID 45F726A9.60604@gmx.de
State New
Headers

Commit Message

Reinhard Nissl March 13, 2007, 10:33 p.m. UTC
  Hi,

Stone wrote:

> That plugin is only beneficial if you have a dual PAL/NTSC environment. 
> It does not help with the frames per second calculatons at all.

You may want to try the attached patch. It determines FramesPerSec by
having a look at the first picture of the recording and falls back to
the FRAMESPERSEC macro otherwise.

The current implementation requires that the recording was taken with
cVideoRepacker enabled. Furthermore it must be MPEG2.

The file marks.vdr still uses FRAMEPERSEC for compatibility. Plugins
like dvd, burn, mp3, mplayer, etc. still use FRAMESPERSEC.

Although this patch is against VDR-1.4.6, it applies with some offset
also against VDR-1.5.1.

Bye.
  

Comments

C.Y.M March 15, 2007, 3:48 a.m. UTC | #1
> You may want to try the attached patch. It determines FramesPerSec by
> having a look at the first picture of the recording and falls back to
> the FRAMESPERSEC macro otherwise.
>
> The current implementation requires that the recording was taken with
> cVideoRepacker enabled. Furthermore it must be MPEG2.
>
> The file marks.vdr still uses FRAMEPERSEC for compatibility. Plugins
> like dvd, burn, mp3, mplayer, etc. still use FRAMESPERSEC.
>
> Although this patch is against VDR-1.4.6, it applies with some offset
> also against VDR-1.5.1.



Thanks! Im using this patch with vdr-1.5.1 and it has fixed my problems with
the incorrect time of recordings for NTSC.  Before I had hardcoded it to 30,
but this is much more accurate.  Good job.  I think this should be
integrated into vdr along with a switch to set the default tv standard to
25/30.

Best Regards.
  

Patch

diff -Nurp ../vdr-1.4.6-orig/dvbplayer.c ./dvbplayer.c
--- ../vdr-1.4.6-orig/dvbplayer.c	2006-04-17 14:45:48.000000000 +0200
+++ ./dvbplayer.c	2007-03-13 23:19:51.000000000 +0100
@@ -182,8 +182,8 @@  bool cNonBlockingFileReader::WaitForData
 
 #define PLAYERBUFSIZE  MEGABYTE(1)
 
-// The number of frames to back up when resuming an interrupted replay session:
-#define RESUMEBACKUP (10 * FRAMESPERSEC)
+// The number of seconds to back up when resuming an interrupted replay session:
+#define RESUMEBACKUP 10
 
 class cDvbPlayer : public cPlayer, cThread {
 private:
@@ -196,6 +196,7 @@  private:
   cFileName *fileName;
   cIndexFile *index;
   cUnbufferedFile *replayFile;
+  int framesPerSec;
   bool eof;
   bool firstPacket;
   ePlayModes playMode;
@@ -225,6 +226,7 @@  public:
   void Goto(int Position, bool Still = false);
   virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
   virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
+  int GetFramesPerSec(void) { return framesPerSec; }
   };
 
 #define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
@@ -253,6 +255,7 @@  cDvbPlayer::cDvbPlayer(const char *FileN
   replayFile = fileName->Open();
   if (!replayFile)
      return;
+  framesPerSec = replayFile->GetFramesPerSec();
   ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
   // Create the index file:
   index = new cIndexFile(FileName, false);
@@ -341,7 +344,7 @@  bool cDvbPlayer::Save(void)
   if (index) {
      int Index = writeIndex;
      if (Index >= 0) {
-        Index -= RESUMEBACKUP;
+        Index -= RESUMEBACKUP * GetFramesPerSec();
         if (Index > 0)
            Index = index->GetNextIFrame(Index, false);
         else
@@ -371,7 +374,7 @@  void cDvbPlayer::Action(void)
 
   readIndex = Resume();
   if (readIndex >= 0)
-     isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true));
+     isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, GetFramesPerSec()));
 
   nonBlockingFileReader = new cNonBlockingFileReader;
   int Length = 0;
@@ -665,7 +668,7 @@  void cDvbPlayer::SkipSeconds(int Seconds
      Empty();
      int Index = writeIndex;
      if (Index >= 0) {
-        Index = max(Index + Seconds * FRAMESPERSEC, 0);
+        Index = max(Index + Seconds * GetFramesPerSec(), 0);
         if (Index > 0)
            Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
         if (Index >= 0)
@@ -832,3 +835,10 @@  void cDvbPlayerControl::Goto(int Positio
   if (player)
      player->Goto(Position, Still);
 }
+
+int cDvbPlayerControl::GetFramesPerSec()
+{
+  if (player)
+     return player->GetFramesPerSec();
+  return FRAMESPERSEC;
+}
diff -Nurp ../vdr-1.4.6-orig/dvbplayer.h ./dvbplayer.h
--- ../vdr-1.4.6-orig/dvbplayer.h	2002-06-23 12:13:51.000000000 +0200
+++ ./dvbplayer.h	2007-03-13 23:19:51.000000000 +0100
@@ -54,6 +54,8 @@  public:
   void Goto(int Index, bool Still = false);
        // Positions to the given index and displays that frame as a still picture
        // if Still is true.
+  int GetFramesPerSec();
+       // Returns the number of frames per second for the current recording.
   };
 
 #endif //__DVBPLAYER_H
diff -Nurp ../vdr-1.4.6-orig/menu.c ./menu.c
--- ../vdr-1.4.6-orig/menu.c	2006-12-02 12:12:02.000000000 +0100
+++ ./menu.c	2007-03-13 23:19:51.000000000 +0100
@@ -3931,7 +3931,7 @@  bool cReplayControl::ShowProgress(bool I
         lastCurrent = lastTotal = -1;
         }
      if (Total != lastTotal) {
-        displayReplay->SetTotal(IndexToHMSF(Total));
+        displayReplay->SetTotal(IndexToHMSF(Total, false, GetFramesPerSec()));
         if (!Initial)
            displayReplay->Flush();
         }
@@ -3939,7 +3939,7 @@  bool cReplayControl::ShowProgress(bool I
         displayReplay->SetProgress(Current, Total);
         if (!Initial)
            displayReplay->Flush();
-        displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames));
+        displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, GetFramesPerSec()));
         displayReplay->Flush();
         lastCurrent = Current;
         }
@@ -3971,8 +3971,8 @@  void cReplayControl::TimeSearchProcess(e
 {
 #define STAY_SECONDS_OFF_END 10
   int Seconds = (timeSearchTime >> 24) * 36000 + ((timeSearchTime & 0x00FF0000) >> 16) * 3600 + ((timeSearchTime & 0x0000FF00) >> 8) * 600 + (timeSearchTime & 0x000000FF) * 60;
-  int Current = (lastCurrent / FRAMESPERSEC);
-  int Total = (lastTotal / FRAMESPERSEC);
+  int Current = (lastCurrent / GetFramesPerSec());
+  int Total = (lastTotal / GetFramesPerSec());
   switch (Key) {
     case k0 ... k9:
          if (timeSearchPos < 4) {
@@ -3999,7 +3999,7 @@  void cReplayControl::TimeSearchProcess(e
     case kDown:
     case kOk:
          Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
-         Goto(Seconds * FRAMESPERSEC, Key == kDown || Key == kPause || Key == kOk);
+         Goto(Seconds * GetFramesPerSec(), Key == kDown || Key == kPause || Key == kOk);
          timeSearchActive = false;
          break;
     default:
@@ -4120,7 +4120,7 @@  void cReplayControl::EditTest(void)
         if ((m->Index() & 0x01) != 0)
            m = marks.Next(m);
         if (m) {
-           Goto(m->position - SecondsToFrames(3));
+           Goto(m->position - SecondsToFrames(3, GetFramesPerSec()));
            Play();
            }
         }
diff -Nurp ../vdr-1.4.6-orig/recording.c ./recording.c
--- ../vdr-1.4.6-orig/recording.c	2006-10-07 14:46:22.000000000 +0200
+++ ./recording.c	2007-03-13 23:19:51.000000000 +0100
@@ -1496,11 +1496,11 @@  cUnbufferedFile *cFileName::NextFile(voi
 
 // --- Index stuff -----------------------------------------------------------
 
-cString IndexToHMSF(int Index, bool WithFrame)
+cString IndexToHMSF(int Index, bool WithFrame, int FramesPerSec)
 {
   char buffer[16];
-  int f = (Index % FRAMESPERSEC) + 1;
-  int s = (Index / FRAMESPERSEC);
+  int f = (Index % FramesPerSec) + 1;
+  int s = (Index / FramesPerSec);
   int m = s / 60 % 60;
   int h = s / 3600;
   s %= 60;
@@ -1508,17 +1508,17 @@  cString IndexToHMSF(int Index, bool With
   return buffer;
 }
 
-int HMSFToIndex(const char *HMSF)
+int HMSFToIndex(const char *HMSF, int FramesPerSec)
 {
   int h, m, s, f = 0;
   if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
-     return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+     return (h * 3600 + m * 60 + s) * FramesPerSec + f - 1;
   return 0;
 }
 
-int SecondsToFrames(int Seconds)
+int SecondsToFrames(int Seconds, int FramesPerSec)
 {
-  return Seconds * FRAMESPERSEC;
+  return Seconds * FramesPerSec;
 }
 
 // --- ReadFrame -------------------------------------------------------------
diff -Nurp ../vdr-1.4.6-orig/recording.h ./recording.h
--- ../vdr-1.4.6-orig/recording.h	2006-12-01 16:06:07.000000000 +0100
+++ ./recording.h	2007-03-13 23:19:51.000000000 +0100
@@ -231,11 +231,11 @@  public:
   cUnbufferedFile *NextFile(void);
   };
 
-cString IndexToHMSF(int Index, bool WithFrame = false);
+cString IndexToHMSF(int Index, bool WithFrame = false, int FramesPerSec = FRAMESPERSEC);
       // Converts the given index to a string, optionally containing the frame number.
-int HMSFToIndex(const char *HMSF);
+int HMSFToIndex(const char *HMSF, int FramesPerSec = FRAMESPERSEC);
       // Converts the given string (format: "hh:mm:ss.ff") to an index.
-int SecondsToFrames(int Seconds); //XXX+ ->player???
+int SecondsToFrames(int Seconds, int FramesPerSec = FRAMESPERSEC); //XXX+ ->player???
       // Returns the number of frames corresponding to the given number of seconds.
 
 int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max);
diff -Nurp ../vdr-1.4.6-orig/svdrp.c ./svdrp.c
--- ../vdr-1.4.6-orig/svdrp.c	2006-08-12 11:09:55.000000000 +0200
+++ ./svdrp.c	2007-03-13 23:19:51.000000000 +0100
@@ -1301,8 +1301,10 @@  void cSVDRP::CmdPLAY(const char *Option)
                  int x = sscanf(option, "%d:%d:%d.%d", &h, &m, &s, &f);
                  if (x == 1)
                     pos = h;
-                 else if (x >= 3)
-                    pos = (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+                 else if (x >= 3) {
+                    int FramesPerSec = cUnbufferedFile::GetFramesPerSec(recording->FileName());
+                    pos = (h * 3600 + m * 60 + s) * FramesPerSec + f - 1;
+                    }
                  }
               cResumeFile resume(recording->FileName());
               if (pos <= 0)
diff -Nurp ../vdr-1.4.6-orig/tools.c ./tools.c
--- ../vdr-1.4.6-orig/tools.c	2006-12-02 12:12:59.000000000 +0100
+++ ./tools.c	2007-03-13 23:19:51.000000000 +0100
@@ -27,6 +27,8 @@  extern "C" {
 #include <utime.h>
 #include "i18n.h"
 #include "thread.h"
+#include "remux.h"
+#include "recording.h"
 
 int SysLogLevel = 3;
 
@@ -1244,6 +1246,77 @@  cUnbufferedFile *cUnbufferedFile::Create
   return File;
 }
 
+int cUnbufferedFile::GetFramesPerSec(const char *FileName)
+{
+  // use this constant as a fallback value
+  int FramesPerSec = FRAMESPERSEC;
+  // open the file an determine frames per second
+  cFileName fn(FileName, false);
+  cUnbufferedFile *f = fn.Open();
+  if (f) {
+     FramesPerSec = f->GetFramesPerSec();
+     fn.Close();
+     }
+  return FramesPerSec;
+}
+
+int cUnbufferedFile::GetFramesPerSec(void)
+{
+  // use this constant as a fallback value
+  int FramesPerSec = FRAMESPERSEC;
+  // rember current file position to restore later
+  off_t OrigPos = curpos;
+  // seek to the beginning and read a chunk of data
+  if (0 == Seek(0, SEEK_SET)) {
+     uchar Data[2048];
+     ssize_t Count = Read(Data, sizeof(Data));
+     if (Count > 0) {
+        // this chunk of data should actually be a PES packet
+        uchar *Limit = Data + Count;
+        int PesPayloadOffset = 0;
+        if (AnalyzePesHeader(Data, Count, PesPayloadOffset) == phMPEG2) {
+           // we need a video stream -- radio recordings use the default
+           if ((Data[3] & 0xF0) == 0xE0) {
+              // thanks to cVideoRepacker, the payload starts with a sequence header
+              uchar *p = Data + PesPayloadOffset;
+              if (p + 12 <= Limit) {
+                 if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xB3) {
+                    uint32_t frame_rate_code = p[7] & 0x0F;
+                    uint32_t frame_rate_extension_n = 0;
+                    uint32_t frame_rate_extension_d = 0;
+                    // now we need to have a look at the next startcode, 
+                    // as it might be a sequence extension
+                    p = (uchar *)memmem(p + 12, Limit - (p + 12), "\x00\x00\x01", 3);
+                    if (p && p + 4 < Limit && p[3] == 0xB5) { // extension start code
+                       if (p + 5 < Limit && (p[4] >> 4) == 0x1) { // sequence extension
+                          if (p + 10 < Limit) {
+                             frame_rate_extension_n = (p[9] & 0x60) >> 5;
+                             frame_rate_extension_d = (p[9] & 0x1F);
+                             }
+                          }
+                       }
+                    // calculate frame rate and round it for compatibility
+                    if (0x1 <= frame_rate_code && frame_rate_code <= 0x8) {
+                       static const int n[] = { -1, 24000, 24, 25, 30000, 30, 50, 60000, 60 };
+                       static const int d[] = { -1,  1001,  1,  1,  1001,  1,  1,  1001,  1 };
+                       double frame_rate = n[frame_rate_code] * (frame_rate_extension_n + 1) 
+                                / (double)(d[frame_rate_code] * (frame_rate_extension_d + 1));
+                       FramesPerSec = (int)frame_rate;
+                       if (frame_rate - FramesPerSec > 0.5)
+                          FramesPerSec++;
+fprintf(stderr, "FramesPerSec: %d\n", FramesPerSec);
+                       }
+                    }
+                 }
+              }
+           }
+        }
+     }
+  // restore original position
+  Seek(OrigPos, SEEK_SET);
+  return FramesPerSec;
+}
+
 // --- cLockFile -------------------------------------------------------------
 
 #define LOCKFILENAME      ".lock-vdr"
diff -Nurp ../vdr-1.4.6-orig/tools.h ./tools.h
--- ../vdr-1.4.6-orig/tools.h	2006-12-03 18:38:38.000000000 +0100
+++ ./tools.h	2007-03-13 23:19:51.000000000 +0100
@@ -263,6 +263,8 @@  public:
   ssize_t Read(void *Data, size_t Size);
   ssize_t Write(const void *Data, size_t Size);
   static cUnbufferedFile *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  static int GetFramesPerSec(const char *FileName);
+  int GetFramesPerSec(void);
   };
 
 class cLockFile {