VDR 1.7.17: fix frame detection

Message ID 4D85D50F.5090608@tvdr.de
State New
Headers

Commit Message

Klaus Schmidinger March 20, 2011, 10:21 a.m. UTC
  The attached patch reactivates some of the frame detecting code that was
already in VDR 1.7.16, and adds a method of determining whether the
current video stream consists of separate "fields" instead of complete
frames. If this is the case, it puts two subsequent fields together to
one frame in the index file.

I know that the way this is detected (by counting the number of "frames"
between two I-frames) is "guesswork", but until I see a case where this
fails I'd say it still beats having a complete H.264 parser in there ;-)

Please test the patch (which is against VDR 1.7.17) by recording all kinds of
streams (SD and HD) available to you and verify that, when replaying such
a recording

- the current and total time in the replay progress display is correct.
- fast forward/rewind works properly
- setting an editing mark, jumping to it and moving it around updates
  the picture accordingly

The patch activates 'DebugFrames', so whenever a recording starts
VDR prints something like this to stderr:

/50 /50 /50 /50 /30 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /10 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /30 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /10
Delta = 1800  FPS = 50.00  FPPU = 1 NF = 32
/50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /30 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /50 /10 *

If you encounter a channel where one of the above tests fails, please send
me the data VDR has printed to stderr when the recording started. Maybe it
can help to further fine tune this.

Please do all testing with newly created recordings that have been done
with a VDR that has this patch applied.

Klaus
  

Patch

===================================================================
RCS file: ./RCS/remux.c
retrieving revision 2.52
diff -u -b -r2.52 ./remux.c
--- ./remux.c	2011/03/13 13:57:09	2.52
+++ ./remux.c	2011/03/20 10:05:34
@@ -17,7 +17,7 @@ 
 
 // Set these to 'true' for debug output:
 static bool DebugPatPmt = false;
-static bool DebugFrames = false;
+static bool DebugFrames = true;//XXX false;
 
 #define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
 #define dbgframes(a...) if (DebugFrames) fprintf(stderr, a)
@@ -783,9 +783,11 @@ 
   synced = false;
   newFrame = independentFrame = false;
   numPtsValues = 0;
+  numFrames = 0;
   numIFrames = 0;
   framesPerSecond = 0;
   framesInPayloadUnit = framesPerPayloadUnit = 0;
+  payloadUnitOfFrame = 0;
   scanning = false;
   scanner = EMPTY_SCANNER;
 }
@@ -807,6 +809,7 @@ 
 void cFrameDetector::Reset(void)
 {
   newFrame = independentFrame = false;
+  payloadUnitOfFrame = 0;
   scanning = false;
   scanner = EMPTY_SCANNER;
 }
@@ -844,6 +847,7 @@ 
                              dbgframes("#");
                              numPtsValues = 0;
                              numIFrames = 0;
+                             numFrames = 0;
                              }
                           else
                              numPtsValues++;
@@ -863,9 +867,22 @@ 
                              framesPerSecond = 25.0;
                           else if (Delta % 3003 == 0)
                              framesPerSecond = 30.0 / 1.001;
-                          else if (abs(Delta - 1800) <= 1)
+                          else if (abs(Delta - 1800) <= 1) {
+                             if (numFrames > 50) {
+                                // this is a "best guess": if there are more than 50 frames between two I-frames, we assume each "frame" actually contains a "field", so two "fields" make one "frame"
+                                framesPerSecond = 25.0;
+                                framesPerPayloadUnit = -2;
+                                }
+                             else
                              framesPerSecond = 50.0;
+                             }
                           else if (Delta == 1501)
+                             if (numFrames > 50) {
+                                // this is a "best guess": if there are more than 50 frames between two I-frames, we assume each "frame" actually contains a "field", so two "fields" make one "frame"
+                                framesPerSecond = 30.0 / 1.001;
+                                framesPerPayloadUnit = -2;
+                                }
+                             else
                              framesPerSecond = 60.0 / 1.001;
                           else {
                              framesPerSecond = 25.0;
@@ -874,7 +891,7 @@ 
                           }
                        else // audio
                           framesPerSecond = 90000.0 / Delta; // PTS of audio frames is always increasing
-                       dbgframes("\nDelta = %d  FPS = %5.2f  FPPU = %d\n", Delta, framesPerSecond, framesPerPayloadUnit);
+                       dbgframes("\nDelta = %d  FPS = %5.2f  FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numFrames);
                        }
                     }
                  scanner = EMPTY_SCANNER;
@@ -909,6 +926,8 @@ 
                                   framesInPayloadUnit++;
                                   if (independentFrame)
                                      numIFrames++;
+                                  if (numIFrames == 1)
+                                     numFrames++;
                                   dbgframes("%d ", (Data[i + 2] >> 3) & 0x07);
                                   }
                                if (synced)
@@ -923,6 +942,13 @@ 
                                newFrame = true;
                                independentFrame = Data[i + 1] == 0x10;
                                if (synced) {
+                                  if (framesPerPayloadUnit < 0) {
+                                     payloadUnitOfFrame = (payloadUnitOfFrame + 1) % -framesPerPayloadUnit;
+                                     if (payloadUnitOfFrame != 0 && independentFrame)
+                                        payloadUnitOfFrame = 0;
+                                     if (payloadUnitOfFrame)
+                                        newFrame = false;
+                                     }
                                   if (framesPerPayloadUnit <= 1)
                                      scanning = false;
                                   }
@@ -930,6 +956,8 @@ 
                                   framesInPayloadUnit++;
                                   if (independentFrame)
                                      numIFrames++;
+                                  if (numIFrames == 1)
+                                     numFrames++;
                                   dbgframes("%02X ", Data[i + 1]);
                                   }
                                if (synced)
@@ -955,7 +983,7 @@ 
                      }
                  if (!synced && framesPerSecond > 0.0 && independentFrame) {
                     synced = true;
-                    dbgframes("*");
+                    dbgframes("*\n");
                     Reset();
                     return Processed + TS_SIZE;
                     }
===================================================================
RCS file: ./RCS/remux.h
retrieving revision 2.27
diff -u -b -r2.27 ./remux.h
--- ./remux.h	2010/11/01 11:24:20	2.27
+++ ./remux.h	2011/03/19 16:52:46
@@ -345,12 +345,15 @@ 
   bool independentFrame;
   uint32_t ptsValues[MaxPtsValues]; // 32 bit is enough - we only need the delta
   int numPtsValues;
+  int numFrames;
   int numIFrames;
   bool isVideo;
   double framesPerSecond;
   int framesInPayloadUnit;
   int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
-                            // while others put an entire GOP into one payload unit (> 1).
+                            // some put an entire GOP into one payload unit (> 1), and
+                            // some spread a single frame over several payload units (< 0).
+  int payloadUnitOfFrame;
   bool scanning;
   uint32_t scanner;
 public: