Browse code

Add a -force_key_frames option to ffmpeg.

The option is useful to ensure that there is a seek point exactly at a
place the user will probably want to jump precisely sometime, the
major example would be the end of an opening and the beginning of a
chapter. The scene change detection system will often make it happen,
but not always for example if there is a fade-in.

See the thread:
Subject: [FFmpeg-devel] [PATCH] -force_key_frames option
Date: Tue, 12 Oct 2010 15:16:26 +0200

Patch by Nicolas George -mail nicolas,george,normalesup,org.

Originally committed as revision 25526 to svn://svn.ffmpeg.org/ffmpeg/trunk

Nicolas George authored on 2010/10/19 06:47:15
Showing 3 changed files
... ...
@@ -48,6 +48,7 @@ version <next>:
48 48
 - cropdetect filter
49 49
 - ffmpeg -crop* options removed
50 50
 - transpose filter added
51
+- ffmpeg -force_key_frames option added
51 52
 
52 53
 
53 54
 version 0.6:
... ...
@@ -495,6 +495,12 @@ Bitstream filters available are "dump_extra", "remove_extra", "noise", "h264_mp4
495 495
 @example
496 496
 ffmpeg -i h264.mp4 -vcodec copy -vbsf h264_mp4toannexb -an out.h264
497 497
 @end example
498
+@item -force_key_frames @var{time}[,@var{time}...]
499
+Force key frames at the specified timestamps, more precisely at the first
500
+frames after each specified time.
501
+This option can be useful to ensure that a seek point is present at a
502
+chapter mark or any other designated place in the output file.
503
+The timestamps must be specified in ascending order.
498 504
 @end table
499 505
 
500 506
 @section Audio Options
... ...
@@ -225,6 +225,7 @@ static int nb_frames_drop = 0;
225 225
 static int input_sync;
226 226
 static uint64_t limit_filesize = 0;
227 227
 static int force_fps = 0;
228
+static char *forced_key_frames = NULL;
228 229
 
229 230
 static int pgmyuv_compatibility_hack=0;
230 231
 static float dts_delta_threshold = 10;
... ...
@@ -272,6 +273,11 @@ typedef struct AVOutputStream {
272 272
     int original_height;
273 273
     int original_width;
274 274
 
275
+    /* forced key frames */
276
+    int64_t *forced_kf_pts;
277
+    int forced_kf_count;
278
+    int forced_kf_index;
279
+
275 280
     /* audio only */
276 281
     int audio_resample;
277 282
     ReSampleContext *resample; /* for audio resampling */
... ...
@@ -1189,6 +1195,11 @@ static void do_video_out(AVFormatContext *s,
1189 1189
             big_picture.pts= ost->sync_opts;
1190 1190
 //            big_picture.pts= av_rescale(ost->sync_opts, AV_TIME_BASE*(int64_t)enc->time_base.num, enc->time_base.den);
1191 1191
 //av_log(NULL, AV_LOG_DEBUG, "%"PRId64" -> encoder\n", ost->sync_opts);
1192
+            if (ost->forced_kf_index < ost->forced_kf_count &&
1193
+                big_picture.pts >= ost->forced_kf_pts[ost->forced_kf_index]) {
1194
+                big_picture.pict_type = FF_I_TYPE;
1195
+                ost->forced_kf_index++;
1196
+            }
1192 1197
             ret = avcodec_encode_video(enc,
1193 1198
                                        bit_buffer, bit_buffer_size,
1194 1199
                                        &big_picture);
... ...
@@ -1837,6 +1848,29 @@ static int copy_chapters(int infile, int outfile)
1837 1837
     return 0;
1838 1838
 }
1839 1839
 
1840
+static void parse_forced_key_frames(char *kf, AVOutputStream *ost,
1841
+                                    AVCodecContext *avctx)
1842
+{
1843
+    char *p;
1844
+    int n = 1, i;
1845
+    int64_t t;
1846
+
1847
+    for (p = kf; *p; p++)
1848
+        if (*p == ',')
1849
+            n++;
1850
+    ost->forced_kf_count = n;
1851
+    ost->forced_kf_pts = av_malloc(sizeof(*ost->forced_kf_pts) * n);
1852
+    if (!ost->forced_kf_pts) {
1853
+        av_log(NULL, AV_LOG_FATAL, "Could not allocate forced key frames array.\n");
1854
+        ffmpeg_exit(1);
1855
+    }
1856
+    for (i = 0; i < n; i++) {
1857
+        p = i ? strchr(p, ',') + 1 : kf;
1858
+        t = parse_time_or_die("force_key_frames", p, 1);
1859
+        ost->forced_kf_pts[i] = av_rescale_q(t, AV_TIME_BASE_Q, avctx->time_base);
1860
+    }
1861
+}
1862
+
1840 1863
 /*
1841 1864
  * The following code is the main loop of the file converter
1842 1865
  */
... ...
@@ -2578,6 +2612,7 @@ static int transcode(AVFormatContext **output_files,
2578 2578
                 av_fifo_free(ost->fifo); /* works even if fifo is not
2579 2579
                                              initialized but set to zero */
2580 2580
                 av_free(ost->pict_tmp.data[0]);
2581
+                av_free(ost->forced_kf_pts);
2581 2582
                 if (ost->video_resample)
2582 2583
                     sws_freeContext(ost->img_resample_ctx);
2583 2584
                 if (ost->resample)
... ...
@@ -3333,6 +3368,9 @@ static void new_video_stream(AVFormatContext *oc, int file_idx)
3333 3333
                 video_enc->flags |= CODEC_FLAG_PASS2;
3334 3334
             }
3335 3335
         }
3336
+
3337
+        if (forced_key_frames)
3338
+            parse_forced_key_frames(forced_key_frames, ost, video_enc);
3336 3339
     }
3337 3340
     if (video_language) {
3338 3341
         av_metadata_set2(&st->metadata, "language", video_language, 0);
... ...
@@ -3342,6 +3380,7 @@ static void new_video_stream(AVFormatContext *oc, int file_idx)
3342 3342
     /* reset some key parameters */
3343 3343
     video_disable = 0;
3344 3344
     av_freep(&video_codec_name);
3345
+    av_freep(&forced_key_frames);
3345 3346
     video_stream_copy = 0;
3346 3347
     frame_pix_fmt = PIX_FMT_NONE;
3347 3348
 }
... ...
@@ -3644,6 +3683,7 @@ static void opt_output_file(const char *filename)
3644 3644
     set_context_opts(oc, avformat_opts, AV_OPT_FLAG_ENCODING_PARAM, NULL);
3645 3645
 
3646 3646
     nb_streamid_map = 0;
3647
+    av_freep(&forced_key_frames);
3647 3648
 }
3648 3649
 
3649 3650
 /* same option as mencoder */
... ...
@@ -4094,6 +4134,7 @@ static const OptionDef options[] = {
4094 4094
     { "qphist", OPT_BOOL | OPT_EXPERT | OPT_VIDEO, { (void *)&qp_hist }, "show QP histogram" },
4095 4095
     { "force_fps", OPT_BOOL | OPT_EXPERT | OPT_VIDEO, {(void*)&force_fps}, "force the selected framerate, disable the best supported framerate selection" },
4096 4096
     { "streamid", OPT_FUNC2 | HAS_ARG | OPT_EXPERT, {(void*)opt_streamid}, "set the value of an outfile streamid", "streamIndex:value" },
4097
+    { "force_key_frames", OPT_STRING | HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void *)&forced_key_frames}, "force key frames at specified timestamps", "timestamps" },
4097 4098
 
4098 4099
     /* audio options */
4099 4100
     { "ab", OPT_FUNC2 | HAS_ARG | OPT_AUDIO, {(void*)opt_bitrate}, "set bitrate (in bits/s)", "bitrate" },