summaryrefslogtreecommitdiff
path: root/ffmpeg.c
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2016-05-27 12:14:33 +0200
committerwm4 <nfxjfg@googlemail.com>2017-03-03 08:45:43 +0100
commitaf1761f7b5b1b72197dc40934953b775c2d951cc (patch)
tree46941e97c792c8331d288639e4f39231bcb0f017 /ffmpeg.c
parent4ee5aed122ba7d289c1686eca6eba161d5d62304 (diff)
ffmpeg: init filtergraphs only after we have a frame on each input
This makes sure the actual stream parameters are used, which is important mainly for hardware decoding+filtering cases, which would previously require various weird workarounds to handle the fact that a fake software graph has to be constructed, but never used. This should also improve behaviour in rare cases where avformat_find_stream_info() does not provide accurate information. This merges Libav commit a3a0230. It was previously skipped. The code in flush_encoders() which sets up a "fake" format wasn't in Libav. I'm not sure if it's a good idea, but it tends to give behavior closer to the old one in certain corner cases. The vp8-size-change gives different result, because now the size of the first frame is used. libavformat reported the size of the largest frame for some reason. The exr tests now use the sample aspect ratio of the first frame. For some reason libavformat determines 0/1 as aspect ratio, while the decoder returns the correct one. The ffm and mxf tests change the field_order values. I'm assuming another libavformat/decoding mismatch. Signed-off-by: wm4 <nfxjfg@googlemail.com>
Diffstat (limited to 'ffmpeg.c')
-rw-r--r--ffmpeg.c338
1 files changed, 215 insertions, 123 deletions
diff --git a/ffmpeg.c b/ffmpeg.c
index 983e2fb486..88f68342ed 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -123,6 +123,7 @@ const char *const forced_keyframes_const_names[] = {
static void do_video_stats(OutputStream *ost, int frame_size);
static int64_t getutime(void);
static int64_t getmaxrss(void);
+static int ifilter_has_all_input_formats(FilterGraph *fg);
static int run_as_daemon = 0;
static int nb_frames_dup = 0;
@@ -472,6 +473,13 @@ static void ffmpeg_cleanup(int ret)
FilterGraph *fg = filtergraphs[i];
avfilter_graph_free(&fg->graph);
for (j = 0; j < fg->nb_inputs; j++) {
+ while (av_fifo_size(fg->inputs[j]->frame_queue)) {
+ AVFrame *frame;
+ av_fifo_generic_read(fg->inputs[j]->frame_queue, &frame,
+ sizeof(frame), NULL);
+ av_frame_free(&frame);
+ }
+ av_fifo_free(fg->inputs[j]->frame_queue);
av_buffer_unref(&fg->inputs[j]->hw_frames_ctx);
av_freep(&fg->inputs[j]->name);
av_freep(&fg->inputs[j]);
@@ -1377,6 +1385,8 @@ static void do_video_stats(OutputStream *ost, int frame_size)
}
}
+static int init_output_stream(OutputStream *ost, char *error, int error_len);
+
static void finish_output_stream(OutputStream *ost)
{
OutputFile *of = output_files[ost->file_index];
@@ -1409,10 +1419,20 @@ static int reap_filters(int flush)
AVCodecContext *enc = ost->enc_ctx;
int ret = 0;
- if (!ost->filter)
+ if (!ost->filter || !ost->filter->graph->graph)
continue;
filter = ost->filter->filter;
+ if (!ost->initialized) {
+ char error[1024];
+ ret = init_output_stream(ost, error, sizeof(error));
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Error initializing output stream %d:%d -- %s\n",
+ ost->file_index, ost->index, error);
+ exit_program(1);
+ }
+ }
+
if (!ost->filtered_frame && !(ost->filtered_frame = av_frame_alloc())) {
return AVERROR(ENOMEM);
}
@@ -1813,6 +1833,54 @@ static void flush_encoders(void)
if (!ost->encoding_needed)
continue;
+ // Try to enable encoding with no input frames.
+ // Maybe we should just let encoding fail instead.
+ if (!ost->initialized) {
+ FilterGraph *fg = ost->filter->graph;
+ char error[1024];
+
+ av_log(NULL, AV_LOG_WARNING,
+ "Finishing stream %d:%d without any data written to it.\n",
+ ost->file_index, ost->st->index);
+
+ if (ost->filter && !fg->graph) {
+ int x;
+ for (x = 0; x < fg->nb_inputs; x++) {
+ InputFilter *ifilter = fg->inputs[x];
+ if (ifilter->format < 0) {
+ AVCodecParameters *par = ifilter->ist->st->codecpar;
+ // We never got any input. Set a fake format, which will
+ // come from libavformat.
+ ifilter->format = par->format;
+ ifilter->sample_rate = par->sample_rate;
+ ifilter->channels = par->channels;
+ ifilter->channel_layout = par->channel_layout;
+ ifilter->width = par->width;
+ ifilter->height = par->height;
+ ifilter->sample_aspect_ratio = par->sample_aspect_ratio;
+ }
+ }
+
+ if (!ifilter_has_all_input_formats(fg))
+ continue;
+
+ ret = configure_filtergraph(fg);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Error configuring filter graph\n");
+ exit_program(1);
+ }
+
+ finish_output_stream(ost);
+ }
+
+ ret = init_output_stream(ost, error, sizeof(error));
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Error initializing output stream %d:%d -- %s\n",
+ ost->file_index, ost->index, error);
+ exit_program(1);
+ }
+ }
+
if (enc->codec_type == AVMEDIA_TYPE_AUDIO && enc->frame_size <= 1)
continue;
#if FF_API_LAVF_FMT_RAWPICTURE
@@ -2044,6 +2112,102 @@ static void check_decode_result(InputStream *ist, int *got_output, int ret)
}
}
+// Filters can be configured only if the formats of all inputs are known.
+static int ifilter_has_all_input_formats(FilterGraph *fg)
+{
+ int i;
+ for (i = 0; i < fg->nb_inputs; i++) {
+ if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO ||
+ fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO))
+ return 0;
+ }
+ return 1;
+}
+
+static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame)
+{
+ FilterGraph *fg = ifilter->graph;
+ int need_reinit, ret, i;
+
+ /* determine if the parameters for this input changed */
+ need_reinit = ifilter->format != frame->format;
+ if (!!ifilter->hw_frames_ctx != !!frame->hw_frames_ctx ||
+ (ifilter->hw_frames_ctx && ifilter->hw_frames_ctx->data != frame->hw_frames_ctx->data))
+ need_reinit = 1;
+
+ switch (ifilter->ist->st->codecpar->codec_type) {
+ case AVMEDIA_TYPE_AUDIO:
+ need_reinit |= ifilter->sample_rate != frame->sample_rate ||
+ ifilter->channels != frame->channels ||
+ ifilter->channel_layout != frame->channel_layout;
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ need_reinit |= ifilter->width != frame->width ||
+ ifilter->height != frame->height;
+ break;
+ }
+
+ if (need_reinit) {
+ ret = ifilter_parameters_from_frame(ifilter, frame);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* (re)init the graph if possible, otherwise buffer the frame and return */
+ if (need_reinit || !fg->graph) {
+ for (i = 0; i < fg->nb_inputs; i++) {
+ if (!ifilter_has_all_input_formats(fg)) {
+ AVFrame *tmp = av_frame_clone(frame);
+ if (!tmp)
+ return AVERROR(ENOMEM);
+ av_frame_unref(frame);
+
+ if (!av_fifo_space(ifilter->frame_queue)) {
+ ret = av_fifo_realloc2(ifilter->frame_queue, 2 * av_fifo_size(ifilter->frame_queue));
+ if (ret < 0)
+ return ret;
+ }
+ av_fifo_generic_write(ifilter->frame_queue, &tmp, sizeof(tmp), NULL);
+ return 0;
+ }
+ }
+
+ ret = reap_filters(1);
+ if (ret < 0 && ret != AVERROR_EOF) {
+ char errbuf[128];
+ av_strerror(ret, errbuf, sizeof(errbuf));
+
+ av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", errbuf);
+ return ret;
+ }
+
+ ret = configure_filtergraph(fg);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n");
+ return ret;
+ }
+
+ for (i = 0; i < fg->nb_inputs; i++) {
+ while (av_fifo_size(fg->inputs[i]->frame_queue)) {
+ AVFrame *tmp;
+ av_fifo_generic_read(fg->inputs[i]->frame_queue, &tmp, sizeof(tmp), NULL);
+ ret = av_buffersrc_add_frame(fg->inputs[i]->filter, tmp);
+ av_frame_free(&tmp);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ ret = av_buffersrc_add_frame_flags(ifilter->filter, frame, AV_BUFFERSRC_FLAG_PUSH);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Error while filtering\n");
+ return ret;
+ }
+
+ return 0;
+}
+
// This does not quite work like avcodec_decode_audio4/avcodec_decode_video2.
// There is the following difference: if you got a frame, you must call
// it again with pkt=NULL. pkt==NULL is treated differently from pkt.size==0
@@ -2085,8 +2249,7 @@ static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame)
break;
} else
f = decoded_frame;
- ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, f,
- AV_BUFFERSRC_FLAG_PUSH);
+ ret = ifilter_send_frame(ist->filters[i], f);
if (ret == AVERROR_EOF)
ret = 0; /* ignore */
if (ret < 0) {
@@ -2102,7 +2265,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
{
AVFrame *decoded_frame;
AVCodecContext *avctx = ist->dec_ctx;
- int i, ret, err = 0, resample_changed;
+ int ret, err = 0;
AVRational decoded_frame_tb;
if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
@@ -2138,59 +2301,6 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
avctx->sample_rate;
#endif
- resample_changed = ist->resample_sample_fmt != decoded_frame->format ||
- ist->resample_channels != avctx->channels ||
- ist->resample_channel_layout != decoded_frame->channel_layout ||
- ist->resample_sample_rate != decoded_frame->sample_rate;
- if (resample_changed) {
- char layout1[64], layout2[64];
-
- if (!guess_input_channel_layout(ist)) {
- av_log(NULL, AV_LOG_FATAL, "Unable to find default channel "
- "layout for Input Stream #%d.%d\n", ist->file_index,
- ist->st->index);
- exit_program(1);
- }
- decoded_frame->channel_layout = avctx->channel_layout;
-
- av_get_channel_layout_string(layout1, sizeof(layout1), ist->resample_channels,
- ist->resample_channel_layout);
- av_get_channel_layout_string(layout2, sizeof(layout2), avctx->channels,
- decoded_frame->channel_layout);
-
- av_log(NULL, AV_LOG_INFO,
- "Input stream #%d:%d frame changed from rate:%d fmt:%s ch:%d chl:%s to rate:%d fmt:%s ch:%d chl:%s\n",
- ist->file_index, ist->st->index,
- ist->resample_sample_rate, av_get_sample_fmt_name(ist->resample_sample_fmt),
- ist->resample_channels, layout1,
- decoded_frame->sample_rate, av_get_sample_fmt_name(decoded_frame->format),
- avctx->channels, layout2);
-
- ist->resample_sample_fmt = decoded_frame->format;
- ist->resample_sample_rate = decoded_frame->sample_rate;
- ist->resample_channel_layout = decoded_frame->channel_layout;
- ist->resample_channels = avctx->channels;
-
- for (i = 0; i < ist->nb_filters; i++) {
- err = ifilter_parameters_from_frame(ist->filters[i], decoded_frame);
- if (err < 0) {
- av_log(NULL, AV_LOG_ERROR,
- "Error reconfiguring input stream %d:%d filter %d\n",
- ist->file_index, ist->st->index, i);
- goto fail;
- }
- }
-
- for (i = 0; i < nb_filtergraphs; i++)
- if (ist_in_filtergraph(filtergraphs[i], ist)) {
- FilterGraph *fg = filtergraphs[i];
- if (configure_filtergraph(fg) < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error reinitializing filters!\n");
- exit_program(1);
- }
- }
- }
-
if (decoded_frame->pts != AV_NOPTS_VALUE) {
decoded_frame_tb = ist->st->time_base;
} else if (pkt && pkt->pts != AV_NOPTS_VALUE) {
@@ -2206,9 +2316,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
(AVRational){1, avctx->sample_rate});
ist->nb_samples = decoded_frame->nb_samples;
err = send_frame_to_filters(ist, decoded_frame);
- decoded_frame->pts = AV_NOPTS_VALUE;
-fail:
av_frame_unref(ist->filter_frame);
av_frame_unref(decoded_frame);
return err < 0 ? err : ret;
@@ -2217,7 +2325,7 @@ fail:
static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int eof)
{
AVFrame *decoded_frame;
- int i, ret = 0, err = 0, resample_changed;
+ int i, ret = 0, err = 0;
int64_t best_effort_timestamp;
int64_t dts = AV_NOPTS_VALUE;
AVPacket avpkt;
@@ -2332,39 +2440,6 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int eo
if (ist->st->sample_aspect_ratio.num)
decoded_frame->sample_aspect_ratio = ist->st->sample_aspect_ratio;
- resample_changed = ist->resample_width != decoded_frame->width ||
- ist->resample_height != decoded_frame->height ||
- ist->resample_pix_fmt != decoded_frame->format;
- if (resample_changed) {
- av_log(NULL, AV_LOG_INFO,
- "Input stream #%d:%d frame changed from size:%dx%d fmt:%s to size:%dx%d fmt:%s\n",
- ist->file_index, ist->st->index,
- ist->resample_width, ist->resample_height, av_get_pix_fmt_name(ist->resample_pix_fmt),
- decoded_frame->width, decoded_frame->height, av_get_pix_fmt_name(decoded_frame->format));
-
- ist->resample_width = decoded_frame->width;
- ist->resample_height = decoded_frame->height;
- ist->resample_pix_fmt = decoded_frame->format;
-
- for (i = 0; i < ist->nb_filters; i++) {
- err = ifilter_parameters_from_frame(ist->filters[i], decoded_frame);
- if (err < 0) {
- av_log(NULL, AV_LOG_ERROR,
- "Error reconfiguring input stream %d:%d filter %d\n",
- ist->file_index, ist->st->index, i);
- goto fail;
- }
- }
-
- for (i = 0; i < nb_filtergraphs; i++) {
- if (ist_in_filtergraph(filtergraphs[i], ist) && ist->reinit_filters &&
- configure_filtergraph(filtergraphs[i]) < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error reinitializing filters!\n");
- exit_program(1);
- }
- }
- }
-
err = send_frame_to_filters(ist, decoded_frame);
fail:
@@ -2434,11 +2509,18 @@ out:
static int send_filter_eof(InputStream *ist)
{
- int i, ret;
+ int i, j, ret;
for (i = 0; i < ist->nb_filters; i++) {
- ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
- if (ret < 0)
- return ret;
+ if (ist->filters[i]->filter) {
+ ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
+ if (ret < 0)
+ return ret;
+ } else {
+ // the filtergraph was never configured
+ FilterGraph *fg = ist->filters[i]->graph;
+ for (j = 0; j < fg->nb_outputs; j++)
+ finish_output_stream(fg->outputs[j]->ost);
+ }
}
return 0;
}
@@ -2544,6 +2626,9 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo
break;
}
+ if (got_output)
+ ist->got_output = 1;
+
if (!got_output)
break;
@@ -2721,17 +2806,9 @@ static int get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
static int init_input_stream(int ist_index, char *error, int error_len)
{
- int i, ret;
+ int ret;
InputStream *ist = input_streams[ist_index];
- for (i = 0; i < ist->nb_filters; i++) {
- ret = ifilter_parameters_from_decoder(ist->filters[i], ist->dec_ctx);
- if (ret < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error initializing filter input\n");
- return ret;
- }
- }
-
if (ist->decoding_needed) {
AVCodec *codec = ist->dec;
if (!codec) {
@@ -3125,17 +3202,6 @@ static int init_output_stream_encode(OutputStream *ost)
ost->st->disposition = AV_DISPOSITION_DEFAULT;
}
- if ((enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
- enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
- filtergraph_is_simple(ost->filter->graph)) {
- FilterGraph *fg = ost->filter->graph;
-
- if (configure_filtergraph(fg)) {
- av_log(NULL, AV_LOG_FATAL, "Error opening filters!\n");
- exit_program(1);
- }
- }
-
if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
if (!ost->frame_rate.num)
ost->frame_rate = av_buffersink_get_frame_rate(ost->filter->filter);
@@ -3475,10 +3541,6 @@ static int transcode_init(void)
ost = output_streams[i];
if (!ost->stream_copy) {
-#if CONFIG_LIBMFX
- if (qsv_transcode_init(ost))
- exit_program(1);
-#endif
#if CONFIG_CUVID
if (cuvid_transcode_init(ost))
@@ -3499,6 +3561,10 @@ static int transcode_init(void)
/* open each encoder */
for (i = 0; i < nb_output_streams; i++) {
+ // skip streams fed from filtergraphs until we have a frame for them
+ if (output_streams[i]->filter)
+ continue;
+
ret = init_output_stream(output_streams[i], error, sizeof(error));
if (ret < 0)
goto dump_format;
@@ -3669,6 +3735,9 @@ static OutputStream *choose_output(void)
if (ost->st->cur_dts == AV_NOPTS_VALUE)
av_log(NULL, AV_LOG_DEBUG, "cur_dts is invalid (this is harmless if it occurs once at the start per stream)\n");
+ if (!ost->initialized && !ost->inputs_done)
+ return ost;
+
if (!ost->finished && opts < opts_min) {
opts_min = opts;
ost_min = ost->unavailable ? NULL : ost;
@@ -4326,7 +4395,7 @@ static int transcode_from_filter(FilterGraph *graph, InputStream **best_ist)
static int transcode_step(void)
{
OutputStream *ost;
- InputStream *ist;
+ InputStream *ist = NULL;
int ret;
ost = choose_output();
@@ -4340,11 +4409,34 @@ static int transcode_step(void)
return AVERROR_EOF;
}
- if (ost->filter) {
+ if (ost->filter && !ost->filter->graph->graph) {
+ if (ifilter_has_all_input_formats(ost->filter->graph)) {
+ ret = configure_filtergraph(ost->filter->graph);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n");
+ return ret;
+ }
+ }
+ }
+
+ if (ost->filter && ost->filter->graph->graph) {
if ((ret = transcode_from_filter(ost->filter->graph, &ist)) < 0)
return ret;
if (!ist)
return 0;
+ } else if (ost->filter) {
+ int i;
+ for (i = 0; i < ost->filter->graph->nb_inputs; i++) {
+ InputFilter *ifilter = ost->filter->graph->inputs[i];
+ if (!ifilter->ist->got_output && !input_files[ifilter->ist->file_index]->eof_reached) {
+ ist = ifilter->ist;
+ break;
+ }
+ }
+ if (!ist) {
+ ost->inputs_done = 1;
+ return 0;
+ }
} else {
av_assert0(ost->source_index >= 0);
ist = input_streams[ost->source_index];