FFmpeg: Fix integer overflow when writing custom FPS with high denominator
authorSybren A. Stüvel <sybren@stuvel.eu>
Tue, 11 Jun 2019 13:51:39 +0000 (15:51 +0200)
committerSybren A. Stüvel <sybren@stuvel.eu>
Tue, 11 Jun 2019 13:51:39 +0000 (15:51 +0200)
FFmpeg uses a fraction of integers to indicate the frame rate, whereas
Blender uses `int / float`. When a custom frame rate is used with
non-integer base, the FPS and Base settings were multiplied with 100000
before passing to FFmpeg as `int`. This could overflow when a high
enough FPS setting was used, which is the case when importing a video of
almost-but-not-quite-integer frame rate into the VSE. The overflow
caused FFmpeg to return an error "The encoder timebase is not set",
which is rather cryptic for users.

The new solution is to take the max int and divide that by the frame
rate, and use that ratio to pass to FFmpeg. This won't overflow, and
thus allows exporting arbitrary frame rates.

source/blender/blenkernel/intern/writeffmpeg.c

index ae41b8f..fb8bfa1 100644 (file)
@@ -589,8 +589,13 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
     c->time_base.num = (int)rd->frs_sec_base;
   }
   else {
     c->time_base.num = (int)rd->frs_sec_base;
   }
   else {
-    c->time_base.den = rd->frs_sec * 100000;
-    c->time_base.num = ((double)rd->frs_sec_base) * 100000;
+    // This calculates a fraction (DENUM_MAX / num) which approximates the scene
+    // frame rate (frs_sec / frs_sec_base).
+    const double DENUM_MAX = 2147483647;
+    const double num = (DENUM_MAX / (double)rd->frs_sec) * rd->frs_sec_base;
+
+    c->time_base.den = (int)DENUM_MAX;
+    c->time_base.num = (int)num;
   }
 
   c->gop_size = context->ffmpeg_gop_size;
   }
 
   c->gop_size = context->ffmpeg_gop_size;