[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