[FFmpeg-devel] [PATCH] ffmpeg: implement -force_key_frames_expr option

Stefano Sabatini stefasab at gmail.com
Thu Dec 13 14:30:42 CET 2012


---
 doc/ffmpeg.texi |   20 ++++++++++++++++++++
 ffmpeg.c        |   44 ++++++++++++++++++++++++++++++++++++++++++--
 ffmpeg.h        |   15 +++++++++++++++
 ffmpeg_opt.c    |    7 +++++++
 4 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index b8f6930..ff14e04 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -551,12 +551,32 @@ Force video tag/fourcc. This is an alias for @code{-tag:v}.
 Show QP histogram
 @item -vbsf @var{bitstream_filter}
 Deprecated see -bsf
+
 @item -force_key_frames[:@var{stream_specifier}] @var{time}[, at var{time}...] (@emph{output,per-stream})
 Force key frames at the specified timestamps, more precisely at the first
 frames after each specified time.
 This option can be useful to ensure that a seek point is present at a
 chapter mark or any other designated place in the output file.
 The timestamps must be specified in ascending order.
+See also the @option{force_key_frames_expr} option.
+
+ at item -force_key_frames_expr[:@var{stream_specifier}] @var{expr} (@emph{output,per-stream})
+Force key frames at the time specified by the expression in
+ at var{expr}, more precisely at the first frames after each specified
+time. The expression can contain the following constants:
+
+ at table @option
+ at item n
+The count of the next match, starting from 0.
+ at item prev_t
+The time of the last forced key frame, it is @code{NAN} when no
+keyframe was still forced.
+ at end table
+
+For example you can specify the expression @code{n*5} to force a key
+frame every 5 seconds, or @code{prev_t+5} to specify a key frame after
+the last one forced.
+See also the @option{force_key_frames} option.
 
 @item -copyinkf[:@var{stream_specifier}] (@emph{output,per-stream})
 When doing stream copy, copy also non-key frames found at the
diff --git a/ffmpeg.c b/ffmpeg.c
index 956f5b6..7985051 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -109,6 +109,13 @@ const int program_birth_year = 2000;
 
 static FILE *vstats_file;
 
+const char *const forced_keyframes_const_names[] = {
+    "n",
+    "prev_t",
+    "next_t",
+    NULL
+};
+
 static void do_video_stats(OutputStream *ost, int frame_size);
 static int64_t getutime(void);
 
@@ -435,6 +442,8 @@ static void exit_program(void)
         avcodec_free_frame(&output_streams[i]->filtered_frame);
 
         av_freep(&output_streams[i]->forced_keyframes);
+        av_freep(&output_streams[i]->forced_keyframes_expr);
+        av_expr_free(output_streams[i]->forced_keyframes_pexpr);
         av_freep(&output_streams[i]->avfilter);
         av_freep(&output_streams[i]->logfile_prefix);
         av_freep(&output_streams[i]);
@@ -872,8 +881,9 @@ static void do_video_out(AVFormatContext *s,
         video_size += pkt.size;
         write_frame(s, &pkt, ost);
     } else {
-        int got_packet;
+        int got_packet, forced_keyframe = 0;
         AVFrame big_picture;
+        double pts_time;
 
         big_picture = *in_picture;
         /* better than nothing: use input picture interlaced
@@ -897,11 +907,26 @@ static void do_video_out(AVFormatContext *s,
         big_picture.quality = ost->st->codec->global_quality;
         if (!enc->me_threshold)
             big_picture.pict_type = 0;
+
+        pts_time = big_picture.pts != AV_NOPTS_VALUE ?
+            big_picture.pts * av_q2d(enc->time_base) : NAN;
         if (ost->forced_kf_index < ost->forced_kf_count &&
             big_picture.pts >= ost->forced_kf_pts[ost->forced_kf_index]) {
-            big_picture.pict_type = AV_PICTURE_TYPE_I;
             ost->forced_kf_index++;
+            forced_keyframe = 1;
+        } else if (pts_time != AV_NOPTS_VALUE &&
+                   pts_time >= ost->forced_keyframes_expr_const_values[FKF_NEXT_T]) {
+            ost->forced_keyframes_expr_const_values[FKF_N] += 1;
+            ost->forced_keyframes_expr_const_values[FKF_PREV_T] = pts_time;
+            ost->forced_keyframes_expr_const_values[FKF_NEXT_T] =
+                av_expr_eval(ost->forced_keyframes_pexpr, ost->forced_keyframes_expr_const_values, NULL);
+            forced_keyframe = 1;
+        }
+        if (forced_keyframe) {
+            big_picture.pict_type = AV_PICTURE_TYPE_I;
+            av_log(NULL, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time);
         }
+
         update_benchmark(NULL);
         ret = avcodec_encode_video2(enc, &pkt, &big_picture, &got_packet);
         update_benchmark("encode_video %d.%d", ost->file_index, ost->index);
@@ -2226,9 +2251,24 @@ static int transcode_init(void)
                     codec->bits_per_raw_sample = frame_bits_per_raw_sample;
                 }
 
+                if (ost->forced_keyframes && ost->forced_keyframes_expr) {
+                    av_log(NULL, AV_LOG_ERROR,
+                           "forced_keyframes and forced_keyframes_expr are incompatible, select just one\n");
+                    return AVERROR(EINVAL);
+                }
                 if (ost->forced_keyframes)
                     parse_forced_key_frames(ost->forced_keyframes, ost,
                                             ost->st->codec);
+                if (ost->forced_keyframes_expr) {
+                    ret = av_expr_parse(&ost->forced_keyframes_pexpr, ost->forced_keyframes_expr,
+                                        forced_keyframes_const_names, NULL, NULL, NULL, NULL, 0, NULL);
+                    if (ret < 0)
+                        return ret;
+                    ost->forced_keyframes_expr_const_values[FKF_N] = 0;
+                    ost->forced_keyframes_expr_const_values[FKF_PREV_T] = NAN;
+                    ost->forced_keyframes_expr_const_values[FKF_NEXT_T] =
+                        av_expr_eval(ost->forced_keyframes_pexpr, ost->forced_keyframes_expr_const_values, NULL);
+                }
                 break;
             case AVMEDIA_TYPE_SUBTITLE:
                 codec->time_base = (AVRational){1, 1000};
diff --git a/ffmpeg.h b/ffmpeg.h
index afef6fb..12c563d 100644
--- a/ffmpeg.h
+++ b/ffmpeg.h
@@ -41,6 +41,7 @@
 
 #include "libavutil/avutil.h"
 #include "libavutil/dict.h"
+#include "libavutil/eval.h"
 #include "libavutil/fifo.h"
 #include "libavutil/pixfmt.h"
 #include "libavutil/rational.h"
@@ -139,6 +140,8 @@ typedef struct OptionsContext {
     int        nb_qscale;
     SpecifierOpt *forced_key_frames;
     int        nb_forced_key_frames;
+    SpecifierOpt *forced_key_frames_expr;
+    int        nb_forced_key_frames_expr;
     SpecifierOpt *force_fps;
     int        nb_force_fps;
     SpecifierOpt *frame_aspect_ratios;
@@ -284,6 +287,15 @@ typedef struct InputFile {
 #endif
 } InputFile;
 
+enum forced_keyframes_const {
+    FKF_N,
+    FKF_PREV_T,
+    FKF_NEXT_T,
+    FKF_NB
+};
+
+extern const char *const forced_keyframes_const_names[];
+
 typedef struct OutputStream {
     int file_index;          /* file index */
     int index;               /* stream index in the output file */
@@ -315,6 +327,9 @@ typedef struct OutputStream {
     int forced_kf_count;
     int forced_kf_index;
     char *forced_keyframes;
+    char *forced_keyframes_expr;
+    AVExpr *forced_keyframes_pexpr;
+    double forced_keyframes_expr_const_values[FKF_NB];
 
     /* audio only */
     int audio_channels_map[SWR_CH_MAX];  /* list of the channels id to pick from the source stream */
diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c
index c846a10..4b6eda0 100644
--- a/ffmpeg_opt.c
+++ b/ffmpeg_opt.c
@@ -1174,6 +1174,10 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in
         if (ost->forced_keyframes)
             ost->forced_keyframes = av_strdup(ost->forced_keyframes);
 
+        MATCH_PER_STREAM_OPT(forced_key_frames_expr, str, ost->forced_keyframes_expr, oc, st);
+        if (ost->forced_keyframes_expr)
+            ost->forced_keyframes_expr = av_strdup(ost->forced_keyframes_expr);
+
         MATCH_PER_STREAM_OPT(force_fps, i, ost->force_fps, oc, st);
 
         ost->top_field_first = -1;
@@ -2451,6 +2455,9 @@ const OptionDef options[] = {
     { "force_key_frames", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT  | OPT_SPEC,
         { .off = OFFSET(forced_key_frames) },
         "force key frames at specified timestamps", "timestamps" },
+    { "force_key_frames_expr", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT  | OPT_SPEC,
+        { .off = OFFSET(forced_key_frames_expr) },
+        "force key frames when the given expression is true", "expression" },
     { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE,                         { .func_arg = opt_bitrate },
         "video bitrate (please use -b:v)", "bitrate" },
 
-- 
1.7.9.5



More information about the ffmpeg-devel mailing list