ffmpeg: implement -force_key_frames expression evalution

pull/9/head
Stefano Sabatini 12 years ago
parent 2b14344ab3
commit 43af18ef8b
  1. 38
      doc/ffmpeg.texi
  2. 65
      ffmpeg.c
  3. 14
      ffmpeg.h

@ -552,9 +552,16 @@ Force video tag/fourcc. This is an alias for @code{-tag:v}.
Show QP histogram Show QP histogram
@item -vbsf @var{bitstream_filter} @item -vbsf @var{bitstream_filter}
Deprecated see -bsf Deprecated see -bsf
@item -force_key_frames[:@var{stream_specifier}] @var{time}[,@var{time}...] (@emph{output,per-stream}) @item -force_key_frames[:@var{stream_specifier}] @var{time}[,@var{time}...] (@emph{output,per-stream})
@item -force_key_frames[:@var{stream_specifier}] expr:@var{expr} (@emph{output,per-stream})
Force key frames at the specified timestamps, more precisely at the first Force key frames at the specified timestamps, more precisely at the first
frames after each specified time. frames after each specified time.
If the argument is prefixed with @code{expr:}, the string @var{expr}
is interpreted like an expression and is evaluated for each frame. A
key frame is forced in case the evaluation is non-zero.
If one of the times is "@code{chapters}[@var{delta}]", it is expanded into If one of the times is "@code{chapters}[@var{delta}]", it is expanded into
the time of the beginning of all chapters in the file, shifted by the time of the beginning of all chapters in the file, shifted by
@var{delta}, expressed as a time in seconds. @var{delta}, expressed as a time in seconds.
@ -567,6 +574,37 @@ before the beginning of every chapter:
-force_key_frames 0:05:00,chapters-0.1 -force_key_frames 0:05:00,chapters-0.1
@end example @end example
The expression in @var{expr} can contain the following constants:
@table @option
@item n
the number of current processed frame, starting from 0
@item n_forced
the number of forced frames
@item prev_forced_n
the number of the previous forced frame, it is @code{NAN} when no
keyframe was forced yet
@item prev_forced_t
the time of the previous forced frame, it is @code{NAN} when no
keyframe was forced yet
@item t
the time of the current processed frame
@end table
For example to force a key frame every 5 seconds, you can specify:
@example
-force_key_frames expr:gte(t,n_forced*5)
@end example
To force a key frame 5 seconds after the time of the last forced one,
starting from second 13:
@example
-force_key_frames expr:if(isnan(prev_forced_t),gte(t,13),gte(t,prev_forced_t+5))
@end example
Note that forcing too many keyframes is very harmful for the lookahead
algorithms of certain encoders: using fixed-GOP options or similar
would be more efficient.
@item -copyinkf[:@var{stream_specifier}] (@emph{output,per-stream}) @item -copyinkf[:@var{stream_specifier}] (@emph{output,per-stream})
When doing stream copy, copy also non-key frames found at the When doing stream copy, copy also non-key frames found at the
beginning. beginning.

@ -109,6 +109,15 @@ const int program_birth_year = 2000;
static FILE *vstats_file; static FILE *vstats_file;
const char *const forced_keyframes_const_names[] = {
"n",
"n_forced",
"prev_forced_n",
"prev_forced_t",
"t",
NULL
};
static void do_video_stats(OutputStream *ost, int frame_size); static void do_video_stats(OutputStream *ost, int frame_size);
static int64_t getutime(void); static int64_t getutime(void);
@ -437,6 +446,7 @@ static void exit_program(void)
avcodec_free_frame(&output_streams[i]->filtered_frame); avcodec_free_frame(&output_streams[i]->filtered_frame);
av_freep(&output_streams[i]->forced_keyframes); av_freep(&output_streams[i]->forced_keyframes);
av_expr_free(output_streams[i]->forced_keyframes_pexpr);
av_freep(&output_streams[i]->avfilter); av_freep(&output_streams[i]->avfilter);
av_freep(&output_streams[i]->logfile_prefix); av_freep(&output_streams[i]->logfile_prefix);
av_freep(&output_streams[i]); av_freep(&output_streams[i]);
@ -873,8 +883,9 @@ static void do_video_out(AVFormatContext *s,
video_size += pkt.size; video_size += pkt.size;
write_frame(s, &pkt, ost); write_frame(s, &pkt, ost);
} else { } else {
int got_packet; int got_packet, forced_keyframe = 0;
AVFrame big_picture; AVFrame big_picture;
double pts_time;
big_picture = *in_picture; big_picture = *in_picture;
/* better than nothing: use input picture interlaced /* better than nothing: use input picture interlaced
@ -898,11 +909,41 @@ static void do_video_out(AVFormatContext *s,
big_picture.quality = ost->st->codec->global_quality; big_picture.quality = ost->st->codec->global_quality;
if (!enc->me_threshold) if (!enc->me_threshold)
big_picture.pict_type = 0; 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 && if (ost->forced_kf_index < ost->forced_kf_count &&
big_picture.pts >= ost->forced_kf_pts[ost->forced_kf_index]) { big_picture.pts >= ost->forced_kf_pts[ost->forced_kf_index]) {
big_picture.pict_type = AV_PICTURE_TYPE_I;
ost->forced_kf_index++; ost->forced_kf_index++;
forced_keyframe = 1;
} else if (ost->forced_keyframes_pexpr) {
double res;
ost->forced_keyframes_expr_const_values[FKF_T] = pts_time;
res = av_expr_eval(ost->forced_keyframes_pexpr,
ost->forced_keyframes_expr_const_values, NULL);
av_dlog(NULL, "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n",
ost->forced_keyframes_expr_const_values[FKF_N],
ost->forced_keyframes_expr_const_values[FKF_N_FORCED],
ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N],
ost->forced_keyframes_expr_const_values[FKF_T],
ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T],
res);
if (res) {
forced_keyframe = 1;
ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N] =
ost->forced_keyframes_expr_const_values[FKF_N];
ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T] =
ost->forced_keyframes_expr_const_values[FKF_T];
ost->forced_keyframes_expr_const_values[FKF_N_FORCED] += 1;
}
ost->forced_keyframes_expr_const_values[FKF_N] += 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); update_benchmark(NULL);
ret = avcodec_encode_video2(enc, &pkt, &big_picture, &got_packet); ret = avcodec_encode_video2(enc, &pkt, &big_picture, &got_packet);
update_benchmark("encode_video %d.%d", ost->file_index, ost->index); update_benchmark("encode_video %d.%d", ost->file_index, ost->index);
@ -2272,9 +2313,23 @@ static int transcode_init(void)
codec->bits_per_raw_sample = frame_bits_per_raw_sample; codec->bits_per_raw_sample = frame_bits_per_raw_sample;
} }
if (ost->forced_keyframes) if (ost->forced_keyframes) {
parse_forced_key_frames(ost->forced_keyframes, ost, if (!strncmp(ost->forced_keyframes, "expr:", 5)) {
ost->st->codec); ret = av_expr_parse(&ost->forced_keyframes_pexpr, ost->forced_keyframes+5,
forced_keyframes_const_names, NULL, NULL, NULL, NULL, 0, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR,
"Invalid force_key_frames expression '%s'\n", ost->forced_keyframes+5);
return ret;
}
ost->forced_keyframes_expr_const_values[FKF_N] = 0;
ost->forced_keyframes_expr_const_values[FKF_N_FORCED] = 0;
ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N] = NAN;
ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T] = NAN;
} else {
parse_forced_key_frames(ost->forced_keyframes, ost, ost->st->codec);
}
}
break; break;
case AVMEDIA_TYPE_SUBTITLE: case AVMEDIA_TYPE_SUBTITLE:
codec->time_base = (AVRational){1, 1000}; codec->time_base = (AVRational){1, 1000};

@ -41,6 +41,7 @@
#include "libavutil/avutil.h" #include "libavutil/avutil.h"
#include "libavutil/dict.h" #include "libavutil/dict.h"
#include "libavutil/eval.h"
#include "libavutil/fifo.h" #include "libavutil/fifo.h"
#include "libavutil/pixfmt.h" #include "libavutil/pixfmt.h"
#include "libavutil/rational.h" #include "libavutil/rational.h"
@ -289,6 +290,17 @@ typedef struct InputFile {
#endif #endif
} InputFile; } InputFile;
enum forced_keyframes_const {
FKF_N,
FKF_N_FORCED,
FKF_PREV_FORCED_N,
FKF_PREV_FORCED_T,
FKF_T,
FKF_NB
};
extern const char *const forced_keyframes_const_names[];
typedef struct OutputStream { typedef struct OutputStream {
int file_index; /* file index */ int file_index; /* file index */
int index; /* stream index in the output file */ int index; /* stream index in the output file */
@ -320,6 +332,8 @@ typedef struct OutputStream {
int forced_kf_count; int forced_kf_count;
int forced_kf_index; int forced_kf_index;
char *forced_keyframes; char *forced_keyframes;
AVExpr *forced_keyframes_pexpr;
double forced_keyframes_expr_const_values[FKF_NB];
/* audio only */ /* audio only */
int audio_channels_map[SWR_CH_MAX]; /* list of the channels id to pick from the source stream */ int audio_channels_map[SWR_CH_MAX]; /* list of the channels id to pick from the source stream */

Loading…
Cancel
Save