News:

--

Main Menu

Patch for incorrect time and FPS

Started by jfx, January 02, 2013, 05:38:15 PM

Previous topic - Next topic

jfx

The following patches fix the incorrect time and FPS problems described in http://avidemux.org/smuf/index.php/topic,11384.0.html


--- ADM_videoCopy.cpp.orig Wed Jan 02 03:02:52 2013
+++ ADM_videoCopy.cpp Thu Jan 03 21:33:12 2013
@@ -52,6 +52,22 @@
     {
         uint64_t delta=ptsStart;
         video_body->getPtsDtsDelta(&delta);
+
+        // The next DTS must be used if the first one is 0 and the second one is more than a frame later (DTS can be e.g. 0,800,820...)
+        _SEGMENT *seg0=video_body->getSegment(0);
+        if (startTime<seg0->_startTimeUs+seg0->_durationUs)
+        {
+            uint64_t seg0Pts=seg0->_refStartTimeUs,pts,dts,dts2;
+            uint32_t flags;
+
+            if (video_body->getVideoPtsDts(1,&flags,&pts,&dts2) && dts2>frameIncrement && dts2!=ADM_NO_PTS
+                && video_body->getVideoPtsDts(0,&flags,&pts,&dts) && dts==0
+                && (seg0Pts==0 || pts==seg0Pts) && (startTime==0 || startTime==pts))
+            {
+                delta=pts-(dts2-frameIncrement);
+            }
+        }
+
         ADM_info("PTS/DTS delta=%"PRIu64" us\n",delta);
         //videoDelay
         if(delta>ptsStart)



--- muxerMp4v2.cpp.orig Wed Jan 02 03:02:54 2013
+++ muxerMp4v2.cpp Wed Jan 02 18:15:58 2013
@@ -284,7 +284,7 @@
     // Write last frame
     nextWrite=!nextWrite;
     MP4WriteSample(handle,videoTrackId,in[nextWrite].data,in[nextWrite].len,
-                        90000/100, // duration=10ms
+                        (MP4Duration)(90000/(1000000.0/videoIncrement)), // duration
                         0, // pts/dts offset
                         0 // Sync Sample
                         );

mean


jfx


jfx

Yet another update of the first patch. FFmpeg probably performs a similar correction to determine the correct delay. The file produced by Avidemux with this patch is the same as the file produced by FFmpeg. This patch could be further improved to skip all frames having DTS set to -1.

videofan

#4
I'm having the same issue when saving to MP4. If I provided a MP4 file sample here would that help with developing another patch fix?
I'm attempting to append the sample to itself, and I end up with variable frame rate when the source is constant frame rate. I believe you can reproduce this with any mp4 file, I've tested other encodes of mine which I split. Outputting to AVI preserves the original frame rate which is constant, however I need to output to the original container (MP4).

I'm using the latest nightly on linux, I also attempted this on Windows with latest nightly -- same result. It does not matter if I use output-format MP4 or MP4V2.

avidemux3_cli --load test1.mp4 --append test2.mp4 --audio-codec copy --video-codec copy --output-format MP4 --save out.mp4

jfx

#5
I have uploaded a new test file. If the fpstest2.ts file is saved as MP4v2, it has incorrect FPS and DTS frame values. This is caused by incorrect frame increment. The test file stores two fields per frame and FPS is set to vertical refresh rate of the display. FPS is often set to fields/s instead of data frames/s.

http://rapidshare.com/files/2219034376/fpstest2.ts
If the link does not work, copy the URL to a new window.

The following patches fix the problem and also improve the first patch. The DTS is adjusted directly which should be the best solution.


Index: avidemux_plugins/ADM_muxers/muxerMp4v2/muxerMp4v2Video.cpp
===================================================================
--- avidemux_plugins/ADM_muxers/muxerMp4v2/muxerMp4v2Video.cpp (revision 8403)
+++ avidemux_plugins/ADM_muxers/muxerMp4v2/muxerMp4v2Video.cpp (working copy)
@@ -231,10 +231,8 @@
                 return false;
             }
         }
-        double inc=vStream->getAvgFps1000();
-        inc=inc/1000;
-        if(inc>0.005) inc=1/inc;
-                else inc=0.005;
+        double inc=vStream->getFrameIncrement();
+        inc=inc/1000000;
         ADM_info("Frame increment =%d ms\n",(int)(inc*1000));
         inc*=90000;
         setMaxDurationPerChunk(videoTrackId, inc);
Index: avidemux/common/ADM_editor/src/ADM_segment.cpp
===================================================================
--- avidemux/common/ADM_editor/src/ADM_segment.cpp (revision 8403)
+++ avidemux/common/ADM_editor/src/ADM_segment.cpp (working copy)
@@ -89,6 +89,47 @@
   frameD=1/frameD;
   frameD*=1000000;
   ref->timeIncrementInUs=(uint64_t)frameD;
+
+  // Probe the real time increment as the value determined from FPS may be incorrect due to interlace
+  uint64_t firstNonZeroDts=ADM_NO_PTS,pts,dts;
+  int firstNonZeroDtsFrame;
+  for (int frame=0; frame<info.nb_frames && frame<100; frame++)
+  {
+      if (ref->_aviheader->getPtsDts(frame,&pts,&dts) && dts!=ADM_NO_PTS && dts!=0)
+      {
+          if (firstNonZeroDts==ADM_NO_PTS)
+          {
+              firstNonZeroDts=dts;
+              firstNonZeroDtsFrame=frame;
+              continue;
+          }
+
+          uint64_t probedTimeIncrement=(dts-firstNonZeroDts)/(frame-firstNonZeroDtsFrame);
+          if (probedTimeIncrement==ref->timeIncrementInUs*2 || probedTimeIncrement==ref->timeIncrementInUs/2)
+              ref->timeIncrementInUs=probedTimeIncrement;
+          break;
+      }
+  }
+
+  // Adjust the first DTS if it is 0 and the next valid DTS is higher than expected (DTS can be e.g. 0,800,820...)
+  uint64_t pts0;
+  if (ref->_aviheader->getPtsDts(0,&pts0,&dts) && dts==0)
+  {
+      for (int frame=1; frame<info.nb_frames && frame<100; frame++)
+      {
+          if (ref->_aviheader->getPtsDts(frame,&pts,&dts) && dts!=ADM_NO_PTS && dts!=0)
+          {
+              if (dts>(frame+1)*ref->timeIncrementInUs)
+              {
+                  uint64_t adjustedDts0=dts-frame*ref->timeIncrementInUs;
+                  if (adjustedDts0<pts0)
+                      ref->_aviheader->setPtsDts(0,pts0,adjustedDts0);
+              }
+              break;
+          }
+      }
+  }
+
   ADM_info("[Editor] About %"PRIu64" microseconds per frame\n",ref->timeIncrementInUs);
   ref->_nb_video_frames = info.nb_frames;
   //
@@ -107,7 +148,6 @@
     // Set the default startTime to the pts of first Pic
     vidHeader *demuxer= ref->_aviheader;
     uint32_t flags;
-    uint64_t pts,dts;
         demuxer->getFlags(0,&flags);
         demuxer->getPtsDts(0,&pts,&dts);
         ref->firstFramePts=0;
Index: avidemux/common/ADM_muxerGate/src/ADM_videoCopy.cpp
===================================================================
--- avidemux/common/ADM_muxerGate/src/ADM_videoCopy.cpp (revision 8403)
+++ avidemux/common/ADM_muxerGate/src/ADM_videoCopy.cpp (working copy)
@@ -53,21 +53,6 @@
         uint64_t delta=ptsStart;
         video_body->getPtsDtsDelta(&delta);

-// The next DTS must be used if the first one is 0 and the second one is more than a frame later (DTS can be e.g. 0,800,820...)
-        _SEGMENT *seg0=video_body->getSegment(0);
-        if (startTime<seg0->_startTimeUs+seg0->_durationUs)
-        {
-        uint64_t seg0Pts=seg0->_refStartTimeUs,pts,dts,dts2;
-        uint32_t flags;
-
-        if (video_body->getVideoPtsDts(1,&flags,&pts,&dts2) && dts2>frameIncrement && dts2!=ADM_NO_PTS
-        && video_body->getVideoPtsDts(0,&flags,&pts,&dts) && dts==0
-        && (seg0Pts==0 || pts==seg0Pts) && (startTime==0 || startTime==pts))
-         {
-             delta=pts-(dts2-frameIncrement);
-         }
-        }
-
         ADM_info("PTS/DTS delta=%"PRIu64" us\n",delta);
         //videoDelay
         if(delta>ptsStart)
Index: avidemux_core/ADM_coreMuxer/src/ADM_muxerUtils.cpp
===================================================================
--- avidemux_core/ADM_coreMuxer/src/ADM_muxerUtils.cpp (revision 8403)
+++ avidemux_core/ADM_coreMuxer/src/ADM_muxerUtils.cpp (working copy)
@@ -75,15 +75,12 @@
*/
bool     ADM_muxer::initUI(const char *title)
{
-        float f=(float)vStream->getAvgFps1000();
-        f=1000./f;
-        f*=1000000;
         bool useTray = false;

         if (!prefs->get(FEATURES_USE_SYSTRAY, &useTray))
             useTray = false;

-        videoIncrement=(uint64_t)f;  // Video increment in AVI-Tick
+        videoIncrement=vStream->getFrameIncrement();  // Video increment in AVI-Tick
         videoDuration=vStream->getVideoDuration();
         ADM_info("Muxer, creating UI, video duration is %s\n",ADM_us2plain(videoDuration));
         encoding=createEncoding(videoDuration,useTray);



jfx

I have updated the last patch with a consistency check for adjusted DTS0 < PTS0.

videofan

I applied your latest patches to latest build but still get variable frame rate with MP4.

Frame rate mode                          : Variable
Frame rate                               : 25.000 fps
Minimum frame rate                       : 5.000 fps
Maximum frame rate                       : 25.000 fps

jfx

If the minimum frame rate is 5 FPS, the average frame rate should also be affected. MP4 format seems to support only the average FPS which is useless. The value cannot be used to set the display refresh rate if the file is missing one or more frames.

videofan

The problem is that the source is encoded to a constant frame rate of 25 fps. So where does it get 5 fps from if both files have 25 unique frames per second?

jfx

The problem is most likely the starting PTS of the next file or some frames missing at the end of the first file.

jfx

I have improved the last patch. The first DTS will be adjusted only if the next one is at least two frames ahead in time. It seems DTS is sometimes incremented by frame increment + several microseconds.

mean

Could you attach it please
It's way easier to merge it that way

jfx

I have removed the DTS0 workaround from the patch as it would work bad for variable frame rate files.