aboutsummaryrefslogtreecommitdiffstats
path: root/avconv.c
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2016-05-27 12:14:33 +0200
committerAnton Khirnov <anton@khirnov.net>2016-06-25 12:04:32 +0200
commita3a0230a9870b9018dc7415ae5872784d524cfe5 (patch)
treee3b25576b71cdae38235c1cd480455329480d027 /avconv.c
parent3e265ca58f0505470186dce300ab66a6eac3978e (diff)
downloadffmpeg-a3a0230a9870b9018dc7415ae5872784d524cfe5.tar.gz
avconv: 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.
Diffstat (limited to 'avconv.c')
-rw-r--r--avconv.c245
1 files changed, 124 insertions, 121 deletions
diff --git a/avconv.c b/avconv.c
index 8913d8511f..65eabbdbc9 100644
--- a/avconv.c
+++ b/avconv.c
@@ -147,6 +147,13 @@ static void avconv_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]);
@@ -656,6 +663,8 @@ FF_ENABLE_DEPRECATION_WARNINGS
}
}
+static int init_output_stream(OutputStream *ost, char *error, int error_len);
+
/*
* Read one frame for lavfi output for ost and encode it.
*/
@@ -670,6 +679,16 @@ static int poll_filter(OutputStream *ost)
}
filtered_frame = ost->filtered_frame;
+ 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->enc->type == AVMEDIA_TYPE_AUDIO &&
!(ost->enc->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE))
ret = av_buffersink_get_samples(ost->filter->filter, filtered_frame,
@@ -745,7 +764,8 @@ static int poll_filters(void)
for (i = 0; i < nb_output_streams; i++) {
int64_t pts = output_streams[i]->sync_opts;
- if (!output_streams[i]->filter || output_streams[i]->finished)
+ if (!output_streams[i]->filter || output_streams[i]->finished ||
+ !output_streams[i]->filter->graph->graph)
continue;
pts = av_rescale_q(pts, output_streams[i]->enc_ctx->time_base,
@@ -1150,6 +1170,89 @@ static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *p
output_packet(of, &opkt, ost);
}
+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->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 (fg->inputs[i]->format < 0) {
+ 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 = poll_filters();
+ 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(ifilter->filter, frame);
+ 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
@@ -1199,7 +1302,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
{
AVFrame *decoded_frame, *f;
AVCodecContext *avctx = ist->dec_ctx;
- int i, ret, err = 0, resample_changed;
+ int i, ret, err = 0;
if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
return AVERROR(ENOMEM);
@@ -1222,57 +1325,6 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
decoded_frame->pts = pkt->pts;
}
- 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) &&
- configure_filtergraph(filtergraphs[i]) < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error reinitializing filters!\n");
- exit_program(1);
- }
- }
-
if (decoded_frame->pts != AV_NOPTS_VALUE)
decoded_frame->pts = av_rescale_q(decoded_frame->pts,
ist->st->time_base,
@@ -1287,12 +1339,11 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
} else
f = decoded_frame;
- err = av_buffersrc_add_frame(ist->filters[i]->filter, f);
+ err = ifilter_send_frame(ist->filters[i], f);
if (err < 0)
break;
}
-fail:
av_frame_unref(ist->filter_frame);
av_frame_unref(decoded_frame);
return err < 0 ? err : ret;
@@ -1301,7 +1352,7 @@ fail:
static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
{
AVFrame *decoded_frame, *f;
- int i, ret = 0, err = 0, resample_changed;
+ int i, ret = 0, err = 0;
if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
return AVERROR(ENOMEM);
@@ -1328,46 +1379,6 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
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));
-
- ret = poll_filters();
- 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);
- }
-
- 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) &&
- configure_filtergraph(filtergraphs[i]) < 0) {
- av_log(NULL, AV_LOG_FATAL, "Error reinitializing filters!\n");
- exit_program(1);
- }
- }
-
for (i = 0; i < ist->nb_filters; i++) {
if (i < ist->nb_filters - 1) {
f = ist->filter_frame;
@@ -1377,7 +1388,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
} else
f = decoded_frame;
- err = av_buffersrc_add_frame(ist->filters[i]->filter, f);
+ err = ifilter_send_frame(ist->filters[i], f);
if (err < 0)
break;
}
@@ -1415,11 +1426,18 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
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;
}
@@ -1618,17 +1636,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) {
@@ -1959,17 +1969,6 @@ static int init_output_stream_encode(OutputStream *ost)
enc_ctx->chroma_sample_location = dec_ctx->chroma_sample_location;
}
- 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);
- }
- }
-
switch (enc_ctx->codec_type) {
case AVMEDIA_TYPE_AUDIO:
enc_ctx->sample_fmt = ost->filter->filter->inputs[0]->format;
@@ -2142,6 +2141,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;