X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=avconv.c;h=7abf10d7b0eb42e1e0caada41e1a02771daf61c3;hb=1d01fee980edf60215acd94daf2533a9fefb9342;hp=07bb86d4d1e2fd782aa4c7d369754ea53d81143b;hpb=01e98b1b006c67ab0948f3c1de65a94609fe79cf;p=ffmpeg diff --git a/avconv.c b/avconv.c index 07bb86d4d1e..7abf10d7b0e 100644 --- a/avconv.c +++ b/avconv.c @@ -55,7 +55,6 @@ #if HAVE_SYS_RESOURCE_H #include -#include #include #elif HAVE_GETPROCESSTIMES #include @@ -69,6 +68,10 @@ #include #endif +#if HAVE_PTHREADS +#include +#endif + #include #include "cmdutils.h" @@ -140,18 +143,25 @@ static float dts_delta_threshold = 10; static int print_stats = 1; +#if HAVE_PTHREADS +/* signal to input threads that they should exit; set by the main thread */ +static int transcoding_finished; +#endif + #define DEFAULT_PASS_LOGFILENAME_PREFIX "av2pass" typedef struct InputFilter { AVFilterContext *filter; struct InputStream *ist; struct FilterGraph *graph; + uint8_t *name; } InputFilter; typedef struct OutputFilter { AVFilterContext *filter; struct OutputStream *ost; struct FilterGraph *graph; + uint8_t *name; /* temporary storage until stream maps are processed */ AVFilterInOut *out_tmp; @@ -169,19 +179,6 @@ typedef struct FilterGraph { int nb_outputs; } FilterGraph; -typedef struct FrameBuffer { - uint8_t *base[4]; - uint8_t *data[4]; - int linesize[4]; - - int h, w; - enum PixelFormat pix_fmt; - - int refcount; - struct InputStream *ist; - struct FrameBuffer *next; -} FrameBuffer; - typedef struct InputStream { int file_index; AVStream *st; @@ -201,6 +198,7 @@ typedef struct InputStream { int is_start; /* is 1 at the start and after a discontinuity */ int showed_multi_packet_warning; AVDictionary *opts; + AVRational framerate; /* framerate forced with -r */ int resample_height; int resample_width; @@ -215,7 +213,7 @@ typedef struct InputStream { FrameBuffer *buffer_pool; /* decoded data from this stream goes into all those filters - * currently video only */ + * currently video and audio only */ InputFilter **filters; int nb_filters; } InputStream; @@ -229,6 +227,15 @@ typedef struct InputFile { int nb_streams; /* number of stream that avconv is aware of; may be different from ctx.nb_streams if new streams appear during av_read_frame() */ int rate_emu; + +#if HAVE_PTHREADS + pthread_t thread; /* thread reading from this file */ + int finished; /* the thread has exited */ + int joined; /* the thread has been joined */ + pthread_mutex_t fifo_lock; /* lock for access to fifo */ + pthread_cond_t fifo_cond; /* the main thread will signal on this cond after reading from fifo */ + AVFifoBuffer *fifo; /* demuxed packets are stored here; freed by the main thread */ +#endif } InputFile; typedef struct OutputStream { @@ -446,134 +453,6 @@ static void reset_options(OptionsContext *o) init_opts(); } -static int alloc_buffer(InputStream *ist, AVCodecContext *s, FrameBuffer **pbuf) -{ - FrameBuffer *buf = av_mallocz(sizeof(*buf)); - int i, ret; - const int pixel_size = av_pix_fmt_descriptors[s->pix_fmt].comp[0].step_minus1+1; - int h_chroma_shift, v_chroma_shift; - int edge = 32; // XXX should be avcodec_get_edge_width(), but that fails on svq1 - int w = s->width, h = s->height; - - if (!buf) - return AVERROR(ENOMEM); - - if (!(s->flags & CODEC_FLAG_EMU_EDGE)) { - w += 2*edge; - h += 2*edge; - } - - avcodec_align_dimensions(s, &w, &h); - if ((ret = av_image_alloc(buf->base, buf->linesize, w, h, - s->pix_fmt, 32)) < 0) { - av_freep(&buf); - return ret; - } - /* XXX this shouldn't be needed, but some tests break without this line - * those decoders are buggy and need to be fixed. - * the following tests fail: - * cdgraphics, ansi, aasc, fraps-v1, qtrle-1bit - */ - memset(buf->base[0], 128, ret); - - avcodec_get_chroma_sub_sample(s->pix_fmt, &h_chroma_shift, &v_chroma_shift); - for (i = 0; i < FF_ARRAY_ELEMS(buf->data); i++) { - const int h_shift = i==0 ? 0 : h_chroma_shift; - const int v_shift = i==0 ? 0 : v_chroma_shift; - if (s->flags & CODEC_FLAG_EMU_EDGE) - buf->data[i] = buf->base[i]; - else - buf->data[i] = buf->base[i] + - FFALIGN((buf->linesize[i]*edge >> v_shift) + - (pixel_size*edge >> h_shift), 32); - } - buf->w = s->width; - buf->h = s->height; - buf->pix_fmt = s->pix_fmt; - buf->ist = ist; - - *pbuf = buf; - return 0; -} - -static void free_buffer_pool(InputStream *ist) -{ - FrameBuffer *buf = ist->buffer_pool; - while (buf) { - ist->buffer_pool = buf->next; - av_freep(&buf->base[0]); - av_free(buf); - buf = ist->buffer_pool; - } -} - -static void unref_buffer(InputStream *ist, FrameBuffer *buf) -{ - av_assert0(buf->refcount); - buf->refcount--; - if (!buf->refcount) { - buf->next = ist->buffer_pool; - ist->buffer_pool = buf; - } -} - -static int codec_get_buffer(AVCodecContext *s, AVFrame *frame) -{ - InputStream *ist = s->opaque; - FrameBuffer *buf; - int ret, i; - - if (!ist->buffer_pool && (ret = alloc_buffer(ist, s, &ist->buffer_pool)) < 0) - return ret; - - buf = ist->buffer_pool; - ist->buffer_pool = buf->next; - buf->next = NULL; - if (buf->w != s->width || buf->h != s->height || buf->pix_fmt != s->pix_fmt) { - av_freep(&buf->base[0]); - av_free(buf); - if ((ret = alloc_buffer(ist, s, &buf)) < 0) - return ret; - } - buf->refcount++; - - frame->opaque = buf; - frame->type = FF_BUFFER_TYPE_USER; - frame->extended_data = frame->data; - frame->pkt_pts = s->pkt ? s->pkt->pts : AV_NOPTS_VALUE; - frame->width = buf->w; - frame->height = buf->h; - frame->format = buf->pix_fmt; - frame->sample_aspect_ratio = s->sample_aspect_ratio; - - for (i = 0; i < FF_ARRAY_ELEMS(buf->data); i++) { - frame->base[i] = buf->base[i]; // XXX h264.c uses base though it shouldn't - frame->data[i] = buf->data[i]; - frame->linesize[i] = buf->linesize[i]; - } - - return 0; -} - -static void codec_release_buffer(AVCodecContext *s, AVFrame *frame) -{ - InputStream *ist = s->opaque; - FrameBuffer *buf = frame->opaque; - int i; - - for (i = 0; i < FF_ARRAY_ELEMS(frame->data); i++) - frame->data[i] = NULL; - - unref_buffer(ist, buf); -} - -static void filter_release_buffer(AVFilterBuffer *fb) -{ - FrameBuffer *buf = fb->priv; - av_free(fb); - unref_buffer(buf->ist, buf); -} - /** * Define a function for building a string containing a list of * allowed formats, @@ -630,225 +509,6 @@ DEF_CHOOSE_FORMAT(int, sample_rate, supported_samplerates, 0, DEF_CHOOSE_FORMAT(uint64_t, channel_layout, channel_layouts, 0, GET_CH_LAYOUT_NAME, ",") -static int configure_audio_filters(FilterGraph *fg, AVFilterContext **in_filter, - AVFilterContext **out_filter) -{ - InputStream *ist = fg->inputs[0]->ist; - OutputStream *ost = fg->outputs[0]->ost; - AVCodecContext *codec = ost->st->codec; - AVCodecContext *icodec = ist->st->codec; - char *sample_fmts, *sample_rates, *channel_layouts; - char args[256]; - int ret; - - avfilter_graph_free(&fg->graph); - if (!(fg->graph = avfilter_graph_alloc())) - return AVERROR(ENOMEM); - - snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:" - "channel_layout=0x%"PRIx64, ist->st->time_base.num, - ist->st->time_base.den, icodec->sample_rate, - av_get_sample_fmt_name(icodec->sample_fmt), icodec->channel_layout); - ret = avfilter_graph_create_filter(&fg->inputs[0]->filter, - avfilter_get_by_name("abuffer"), - "src", args, NULL, fg->graph); - if (ret < 0) - return ret; - - ret = avfilter_graph_create_filter(&fg->outputs[0]->filter, - avfilter_get_by_name("abuffersink"), - "out", NULL, NULL, fg->graph); - if (ret < 0) - return ret; - - *in_filter = fg->inputs[0]->filter; - *out_filter = fg->outputs[0]->filter; - - if (codec->channels && !codec->channel_layout) - codec->channel_layout = av_get_default_channel_layout(codec->channels); - - sample_fmts = choose_sample_fmts(ost); - sample_rates = choose_sample_rates(ost); - channel_layouts = choose_channel_layouts(ost); - if (sample_fmts || sample_rates || channel_layouts) { - AVFilterContext *format; - char args[256]; - int len = 0; - - if (sample_fmts) - len += snprintf(args + len, sizeof(args) - len, "sample_fmts=%s:", - sample_fmts); - if (sample_rates) - len += snprintf(args + len, sizeof(args) - len, "sample_rates=%s:", - sample_rates); - if (channel_layouts) - len += snprintf(args + len, sizeof(args) - len, "channel_layouts=%s:", - channel_layouts); - args[len - 1] = 0; - - av_freep(&sample_fmts); - av_freep(&sample_rates); - av_freep(&channel_layouts); - - ret = avfilter_graph_create_filter(&format, - avfilter_get_by_name("aformat"), - "aformat", args, NULL, fg->graph); - if (ret < 0) - return ret; - - ret = avfilter_link(format, 0, fg->outputs[0]->filter, 0); - if (ret < 0) - return ret; - - *out_filter = format; - } - - if (audio_sync_method > 0) { - AVFilterContext *async; - char args[256]; - int len = 0; - - av_log(NULL, AV_LOG_WARNING, "-async has been deprecated. Used the " - "asyncts audio filter instead.\n"); - - if (audio_sync_method > 1) - len += snprintf(args + len, sizeof(args) - len, "compensate=1:" - "max_comp=%d:", audio_sync_method); - snprintf(args + len, sizeof(args) - len, "min_delta=%f", - audio_drift_threshold); - - ret = avfilter_graph_create_filter(&async, - avfilter_get_by_name("asyncts"), - "async", args, NULL, fg->graph); - if (ret < 0) - return ret; - - ret = avfilter_link(*in_filter, 0, async, 0); - if (ret < 0) - return ret; - - *in_filter = async; - } - - return 0; -} - -static int configure_video_filters(FilterGraph *fg, AVFilterContext **in_filter, - AVFilterContext **out_filter) -{ - InputStream *ist = fg->inputs[0]->ist; - OutputStream *ost = fg->outputs[0]->ost; - AVFilterContext *filter; - AVCodecContext *codec = ost->st->codec; - char *pix_fmts; - AVRational sample_aspect_ratio; - char args[255]; - int ret; - - if (ist->st->sample_aspect_ratio.num) { - sample_aspect_ratio = ist->st->sample_aspect_ratio; - } else - sample_aspect_ratio = ist->st->codec->sample_aspect_ratio; - - snprintf(args, 255, "%d:%d:%d:%d:%d:%d:%d", ist->st->codec->width, - ist->st->codec->height, ist->st->codec->pix_fmt, 1, AV_TIME_BASE, - sample_aspect_ratio.num, sample_aspect_ratio.den); - - ret = avfilter_graph_create_filter(&fg->inputs[0]->filter, - avfilter_get_by_name("buffer"), - "src", args, NULL, fg->graph); - if (ret < 0) - return ret; - ret = avfilter_graph_create_filter(&fg->outputs[0]->filter, - avfilter_get_by_name("buffersink"), - "out", NULL, NULL, fg->graph); - if (ret < 0) - return ret; - *in_filter = fg->inputs[0]->filter; - *out_filter = fg->outputs[0]->filter; - - if (codec->width || codec->height) { - snprintf(args, 255, "%d:%d:flags=0x%X", - codec->width, - codec->height, - (unsigned)ost->sws_flags); - if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), - NULL, args, NULL, fg->graph)) < 0) - return ret; - if ((ret = avfilter_link(*in_filter, 0, filter, 0)) < 0) - return ret; - *in_filter = filter; - } - - if ((pix_fmts = choose_pix_fmts(ost))) { - if ((ret = avfilter_graph_create_filter(&filter, - avfilter_get_by_name("format"), - "format", pix_fmts, NULL, - fg->graph)) < 0) - return ret; - if ((ret = avfilter_link(filter, 0, *out_filter, 0)) < 0) - return ret; - - *out_filter = filter; - av_freep(&pix_fmts); - } - - snprintf(args, sizeof(args), "flags=0x%X", (unsigned)ost->sws_flags); - fg->graph->scale_sws_opts = av_strdup(args); - - return 0; -} - -static int configure_simple_filtergraph(FilterGraph *fg) -{ - OutputStream *ost = fg->outputs[0]->ost; - AVFilterContext *in_filter, *out_filter; - int ret; - - avfilter_graph_free(&fg->graph); - fg->graph = avfilter_graph_alloc(); - - switch (ost->st->codec->codec_type) { - case AVMEDIA_TYPE_VIDEO: - ret = configure_video_filters(fg, &in_filter, &out_filter); - break; - case AVMEDIA_TYPE_AUDIO: - ret = configure_audio_filters(fg, &in_filter, &out_filter); - break; - default: av_assert0(0); - } - if (ret < 0) - return ret; - - if (ost->avfilter) { - AVFilterInOut *outputs = avfilter_inout_alloc(); - AVFilterInOut *inputs = avfilter_inout_alloc(); - - outputs->name = av_strdup("in"); - outputs->filter_ctx = in_filter; - outputs->pad_idx = 0; - outputs->next = NULL; - - inputs->name = av_strdup("out"); - inputs->filter_ctx = out_filter; - inputs->pad_idx = 0; - inputs->next = NULL; - - if ((ret = avfilter_graph_parse(fg->graph, ost->avfilter, inputs, outputs, NULL)) < 0) - return ret; - } else { - if ((ret = avfilter_link(in_filter, 0, out_filter, 0)) < 0) - return ret; - } - - if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0) - return ret; - - ost->filter = fg->outputs[0]; - - return 0; -} - static FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost) { FilterGraph *fg = av_mallocz(sizeof(*fg)); @@ -864,6 +524,8 @@ static FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost) fg->outputs[0]->ost = ost; fg->outputs[0]->graph = fg; + ost->filter = fg->outputs[0]; + fg->inputs = grow_array(fg->inputs, sizeof(*fg->inputs), &fg->nb_inputs, fg->nb_inputs + 1); if (!(fg->inputs[0] = av_mallocz(sizeof(*fg->inputs[0])))) @@ -885,12 +547,13 @@ static FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost) static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) { InputStream *ist; - enum AVMediaType type = in->filter_ctx->input_pads[in->pad_idx].type; + enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); int i; // TODO: support other filter types - if (type != AVMEDIA_TYPE_VIDEO) { - av_log(NULL, AV_LOG_FATAL, "Only video filters supported currently.\n"); + if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) { + av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported " + "currently.\n"); exit_program(1); } @@ -951,98 +614,347 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1]; } -static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +{ + char *pix_fmts; + OutputStream *ost = ofilter->ost; + AVCodecContext *codec = ost->st->codec; + AVFilterContext *last_filter = out->filter_ctx; + int pad_idx = out->pad_idx; + int ret; + + + ret = avfilter_graph_create_filter(&ofilter->filter, + avfilter_get_by_name("buffersink"), + "out", NULL, pix_fmts, fg->graph); + if (ret < 0) + return ret; + + if (codec->width || codec->height) { + char args[255]; + AVFilterContext *filter; + + snprintf(args, sizeof(args), "%d:%d:flags=0x%X", + codec->width, + codec->height, + (unsigned)ost->sws_flags); + if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), + NULL, args, NULL, fg->graph)) < 0) + return ret; + if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) + return ret; + + last_filter = filter; + pad_idx = 0; + } + + if ((pix_fmts = choose_pix_fmts(ost))) { + AVFilterContext *filter; + if ((ret = avfilter_graph_create_filter(&filter, + avfilter_get_by_name("format"), + "format", pix_fmts, NULL, + fg->graph)) < 0) + return ret; + if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) + return ret; + + last_filter = filter; + pad_idx = 0; + av_freep(&pix_fmts); + } + + if (ost->frame_rate.num) { + AVFilterContext *fps; + char args[255]; + + snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num, + ost->frame_rate.den); + ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"), + "fps", args, NULL, fg->graph); + if (ret < 0) + return ret; + + ret = avfilter_link(last_filter, pad_idx, fps, 0); + if (ret < 0) + return ret; + last_filter = fps; + pad_idx = 0; + } + + if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) + return ret; + + return 0; +} + +static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +{ + OutputStream *ost = ofilter->ost; + AVCodecContext *codec = ost->st->codec; + AVFilterContext *last_filter = out->filter_ctx; + int pad_idx = out->pad_idx; + char *sample_fmts, *sample_rates, *channel_layouts; + int ret; + + ret = avfilter_graph_create_filter(&ofilter->filter, + avfilter_get_by_name("abuffersink"), + "out", NULL, NULL, fg->graph); + if (ret < 0) + return ret; + + if (codec->channels && !codec->channel_layout) + codec->channel_layout = av_get_default_channel_layout(codec->channels); + + sample_fmts = choose_sample_fmts(ost); + sample_rates = choose_sample_rates(ost); + channel_layouts = choose_channel_layouts(ost); + if (sample_fmts || sample_rates || channel_layouts) { + AVFilterContext *format; + char args[256]; + int len = 0; + + if (sample_fmts) + len += snprintf(args + len, sizeof(args) - len, "sample_fmts=%s:", + sample_fmts); + if (sample_rates) + len += snprintf(args + len, sizeof(args) - len, "sample_rates=%s:", + sample_rates); + if (channel_layouts) + len += snprintf(args + len, sizeof(args) - len, "channel_layouts=%s:", + channel_layouts); + args[len - 1] = 0; + + av_freep(&sample_fmts); + av_freep(&sample_rates); + av_freep(&channel_layouts); + + ret = avfilter_graph_create_filter(&format, + avfilter_get_by_name("aformat"), + "aformat", args, NULL, fg->graph); + if (ret < 0) + return ret; + + ret = avfilter_link(last_filter, pad_idx, format, 0); + if (ret < 0) + return ret; + + last_filter = format; + pad_idx = 0; + } + + if (audio_sync_method > 0) { + AVFilterContext *async; + char args[256]; + int len = 0; + + av_log(NULL, AV_LOG_WARNING, "-async has been deprecated. Used the " + "asyncts audio filter instead.\n"); + + if (audio_sync_method > 1) + len += snprintf(args + len, sizeof(args) - len, "compensate=1:" + "max_comp=%d:", audio_sync_method); + snprintf(args + len, sizeof(args) - len, "min_delta=%f", + audio_drift_threshold); + + ret = avfilter_graph_create_filter(&async, + avfilter_get_by_name("asyncts"), + "async", args, NULL, fg->graph); + if (ret < 0) + return ret; + + ret = avfilter_link(last_filter, pad_idx, async, 0); + if (ret < 0) + return ret; + + last_filter = async; + pad_idx = 0; + } + + if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) + return ret; + + return 0; +} + +#define DESCRIBE_FILTER_LINK(f, inout, in) \ +{ \ + AVFilterContext *ctx = inout->filter_ctx; \ + AVFilterPad *pads = in ? ctx->input_pads : ctx->output_pads; \ + int nb_pads = in ? ctx->input_count : ctx->output_count; \ + AVIOContext *pb; \ + \ + if (avio_open_dyn_buf(&pb) < 0) \ + exit_program(1); \ + \ + avio_printf(pb, "%s", ctx->filter->name); \ + if (nb_pads > 1) \ + avio_printf(pb, ":%s", avfilter_pad_get_name(pads, inout->pad_idx));\ + avio_w8(pb, 0); \ + avio_close_dyn_buf(pb, &f->name); \ +} + +static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +{ + av_freep(&ofilter->name); + DESCRIBE_FILTER_LINK(ofilter, out, 0); + + switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { + case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out); + case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out); + default: av_assert0(0); + } +} + +static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, + AVFilterInOut *in) +{ + AVFilterContext *first_filter = in->filter_ctx; + AVFilter *filter = avfilter_get_by_name("buffer"); + InputStream *ist = ifilter->ist; + AVRational tb = ist->framerate.num ? (AVRational){ist->framerate.den, + ist->framerate.num} : + ist->st->time_base; + AVRational sar; + char args[255]; + int pad_idx = in->pad_idx; + int ret; + + sar = ist->st->sample_aspect_ratio.num ? + ist->st->sample_aspect_ratio : + ist->st->codec->sample_aspect_ratio; + snprintf(args, sizeof(args), "%d:%d:%d:%d:%d:%d:%d", ist->st->codec->width, + ist->st->codec->height, ist->st->codec->pix_fmt, + tb.num, tb.den, sar.num, sar.den); + + if ((ret = avfilter_graph_create_filter(&ifilter->filter, filter, in->name, + args, NULL, fg->graph)) < 0) + return ret; + + if (ist->framerate.num) { + AVFilterContext *setpts; + + if ((ret = avfilter_graph_create_filter(&setpts, + avfilter_get_by_name("setpts"), + "setpts", "N", NULL, + fg->graph)) < 0) + return ret; + + if ((ret = avfilter_link(setpts, 0, first_filter, pad_idx)) < 0) + return ret; + + first_filter = setpts; + pad_idx = 0; + } + + if ((ret = avfilter_link(ifilter->filter, 0, first_filter, pad_idx)) < 0) + return ret; + return 0; +} + +static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, + AVFilterInOut *in) { - char *pix_fmts; - AVCodecContext *codec = ofilter->ost->st->codec; - AVFilterContext *last_filter = out->filter_ctx; - int pad_idx = out->pad_idx; + AVFilterContext *first_filter = in->filter_ctx; + AVFilter *filter = avfilter_get_by_name("abuffer"); + InputStream *ist = ifilter->ist; + int pad_idx = in->pad_idx; + char args[255]; int ret; + snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s" + ":channel_layout=0x%"PRIx64, + ist->st->time_base.num, ist->st->time_base.den, + ist->st->codec->sample_rate, + av_get_sample_fmt_name(ist->st->codec->sample_fmt), + ist->st->codec->channel_layout); - ret = avfilter_graph_create_filter(&ofilter->filter, - avfilter_get_by_name("buffersink"), - "out", NULL, pix_fmts, fg->graph); - if (ret < 0) + if ((ret = avfilter_graph_create_filter(&ifilter->filter, filter, + in->name, args, NULL, + fg->graph)) < 0) return ret; - if (codec->width || codec->height) { - char args[255]; - AVFilterContext *filter; + if (audio_sync_method > 0) { + AVFilterContext *async; + char args[256]; + int len = 0; - snprintf(args, sizeof(args), "%d:%d:flags=0x%X", - codec->width, - codec->height, - (unsigned)ofilter->ost->sws_flags); - if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), - NULL, args, NULL, fg->graph)) < 0) - return ret; - if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) - return ret; + av_log(NULL, AV_LOG_WARNING, "-async has been deprecated. Used the " + "asyncts audio filter instead.\n"); - last_filter = filter; - pad_idx = 0; - } + if (audio_sync_method > 1) + len += snprintf(args + len, sizeof(args) - len, "compensate=1:" + "max_comp=%d:", audio_sync_method); + snprintf(args + len, sizeof(args) - len, "min_delta=%f", + audio_drift_threshold); - if ((pix_fmts = choose_pix_fmts(ofilter->ost))) { - AVFilterContext *filter; - if ((ret = avfilter_graph_create_filter(&filter, - avfilter_get_by_name("format"), - "format", pix_fmts, NULL, - fg->graph)) < 0) + ret = avfilter_graph_create_filter(&async, + avfilter_get_by_name("asyncts"), + "async", args, NULL, fg->graph); + if (ret < 0) return ret; - if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) + + ret = avfilter_link(async, 0, first_filter, pad_idx); + if (ret < 0) return ret; - last_filter = filter; - pad_idx = 0; - av_freep(&pix_fmts); + first_filter = async; + pad_idx = 0; } - - if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) + if ((ret = avfilter_link(ifilter->filter, 0, first_filter, pad_idx)) < 0) return ret; return 0; } -static int configure_complex_filter(FilterGraph *fg) +static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, + AVFilterInOut *in) +{ + av_freep(&ifilter->name); + DESCRIBE_FILTER_LINK(ifilter, in, 1); + + switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) { + case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in); + case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in); + default: av_assert0(0); + } +} + +static int configure_filtergraph(FilterGraph *fg) { AVFilterInOut *inputs, *outputs, *cur; - int ret, i, init = !fg->graph; + int ret, i, init = !fg->graph, simple = !fg->graph_desc; + const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter : + fg->graph_desc; avfilter_graph_free(&fg->graph); if (!(fg->graph = avfilter_graph_alloc())) return AVERROR(ENOMEM); - if ((ret = avfilter_graph_parse2(fg->graph, fg->graph_desc, &inputs, &outputs)) < 0) + if (simple) { + OutputStream *ost = fg->outputs[0]->ost; + char args[255]; + snprintf(args, sizeof(args), "flags=0x%X", (unsigned)ost->sws_flags); + fg->graph->scale_sws_opts = av_strdup(args); + } + + if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0) return ret; - for (cur = inputs; init && cur; cur = cur->next) + if (simple && (!inputs || inputs->next || !outputs || outputs->next)) { + av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' does not have " + "exactly one input and output.\n", graph_desc); + return AVERROR(EINVAL); + } + + for (cur = inputs; !simple && init && cur; cur = cur->next) init_input_filter(fg, cur); - for (cur = inputs, i = 0; cur; cur = cur->next, i++) { - InputFilter *ifilter = fg->inputs[i]; - InputStream *ist = ifilter->ist; - AVRational sar; - char args[255]; - - sar = ist->st->sample_aspect_ratio.num ? ist->st->sample_aspect_ratio : - ist->st->codec->sample_aspect_ratio; - snprintf(args, sizeof(args), "%d:%d:%d:%d:%d:%d:%d", ist->st->codec->width, - ist->st->codec->height, ist->st->codec->pix_fmt, 1, AV_TIME_BASE, - sar.num, sar.den); - - if ((ret = avfilter_graph_create_filter(&ifilter->filter, - avfilter_get_by_name("buffer"), cur->name, - args, NULL, fg->graph)) < 0) + for (cur = inputs, i = 0; cur; cur = cur->next, i++) + if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) return ret; - if ((ret = avfilter_link(ifilter->filter, 0, - cur->filter_ctx, cur->pad_idx)) < 0) - return ret; - } avfilter_inout_free(&inputs); - if (!init) { + if (!init || simple) { /* we already know the mappings between lavfi outputs and output streams, * so we can finish the setup */ for (cur = outputs, i = 0; cur; cur = cur->next, i++) @@ -1074,17 +986,11 @@ static int configure_complex_filters(void) for (i = 0; i < nb_filtergraphs; i++) if (!filtergraphs[i]->graph && - (ret = configure_complex_filter(filtergraphs[i])) < 0) + (ret = configure_filtergraph(filtergraphs[i])) < 0) return ret; return 0; } -static int configure_filtergraph(FilterGraph *fg) -{ - return fg->graph_desc ? configure_complex_filter(fg) : - configure_simple_filtergraph(fg); -} - static int ist_in_filtergraph(FilterGraph *fg, InputStream *ist) { int i; @@ -1132,11 +1038,15 @@ void exit_program(int ret) for (i = 0; i < nb_filtergraphs; i++) { avfilter_graph_free(&filtergraphs[i]->graph); - for (j = 0; j < filtergraphs[i]->nb_inputs; j++) + for (j = 0; j < filtergraphs[i]->nb_inputs; j++) { + av_freep(&filtergraphs[i]->inputs[j]->name); av_freep(&filtergraphs[i]->inputs[j]); + } av_freep(&filtergraphs[i]->inputs); - for (j = 0; j < filtergraphs[i]->nb_outputs; j++) + for (j = 0; j < filtergraphs[i]->nb_outputs; j++) { + av_freep(&filtergraphs[i]->outputs[j]->name); av_freep(&filtergraphs[i]->outputs[j]); + } av_freep(&filtergraphs[i]->outputs); av_freep(&filtergraphs[i]); } @@ -1171,7 +1081,7 @@ void exit_program(int ret) for (i = 0; i < nb_input_streams; i++) { av_freep(&input_streams[i]->decoded_frame); av_dict_free(&input_streams[i]->opts); - free_buffer_pool(input_streams[i]); + free_buffer_pool(&input_streams[i]->buffer_pool); av_freep(&input_streams[i]->filters); av_freep(&input_streams[i]); } @@ -1265,13 +1175,6 @@ static void update_sample_fmt(AVCodecContext *dec, AVCodec *dec_codec, } } -static double -get_sync_ipts(const OutputStream *ost, int64_t pts) -{ - OutputFile *of = output_files[ost->file_index]; - return (double)(pts - of->start_time) / AV_TIME_BASE; -} - static void write_frame(AVFormatContext *s, AVPacket *pkt, OutputStream *ost) { AVBitStreamFilterContext *bsfc = ost->bitstream_filters; @@ -1483,17 +1386,9 @@ static void do_video_out(AVFormatContext *s, AVFrame *in_picture, int *frame_size, float quality) { - int nb_frames, i, ret, format_video_sync; - AVCodecContext *enc; - double sync_ipts, delta; - - enc = ost->st->codec; - - sync_ipts = get_sync_ipts(ost, in_picture->pts) / av_q2d(enc->time_base); - delta = sync_ipts - ost->sync_opts; - - /* by default, we output a single frame */ - nb_frames = 1; + int ret, format_video_sync; + AVPacket pkt; + AVCodecContext *enc = ost->st->codec; *frame_size = 0; @@ -1501,120 +1396,98 @@ static void do_video_out(AVFormatContext *s, if (format_video_sync == VSYNC_AUTO) format_video_sync = (s->oformat->flags & AVFMT_NOTIMESTAMPS) ? VSYNC_PASSTHROUGH : (s->oformat->flags & AVFMT_VARIABLE_FPS) ? VSYNC_VFR : VSYNC_CFR; - - switch (format_video_sync) { - case VSYNC_CFR: - // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c - if (delta < -1.1) - nb_frames = 0; - else if (delta > 1.1) - nb_frames = lrintf(delta); - break; - case VSYNC_VFR: - if (delta <= -0.6) - nb_frames = 0; - else if (delta > 0.6) - ost->sync_opts = lrint(sync_ipts); - break; - case VSYNC_PASSTHROUGH: - ost->sync_opts = lrint(sync_ipts); - break; - default: - av_assert0(0); - } - - nb_frames = FFMIN(nb_frames, ost->max_frames - ost->frame_number); - if (nb_frames == 0) { + if (format_video_sync != VSYNC_PASSTHROUGH && + ost->frame_number && + in_picture->pts != AV_NOPTS_VALUE && + in_picture->pts < ost->sync_opts) { nb_frames_drop++; av_log(NULL, AV_LOG_VERBOSE, "*** drop!\n"); return; - } else if (nb_frames > 1) { - nb_frames_dup += nb_frames - 1; - av_log(NULL, AV_LOG_VERBOSE, "*** %d dup!\n", nb_frames - 1); } + if (in_picture->pts == AV_NOPTS_VALUE) + in_picture->pts = ost->sync_opts; + ost->sync_opts = in_picture->pts; + + if (!ost->frame_number) - ost->first_pts = ost->sync_opts; + ost->first_pts = in_picture->pts; - /* duplicates frame if needed */ - for (i = 0; i < nb_frames; i++) { - AVPacket pkt; - av_init_packet(&pkt); - pkt.data = NULL; - pkt.size = 0; + av_init_packet(&pkt); + pkt.data = NULL; + pkt.size = 0; - if (!check_recording_time(ost)) - return; + if (!check_recording_time(ost) || + ost->frame_number >= ost->max_frames) + return; - if (s->oformat->flags & AVFMT_RAWPICTURE && - enc->codec->id == CODEC_ID_RAWVIDEO) { - /* raw pictures are written as AVPicture structure to - avoid any copies. We support temporarily the older - method. */ - enc->coded_frame->interlaced_frame = in_picture->interlaced_frame; - enc->coded_frame->top_field_first = in_picture->top_field_first; - pkt.data = (uint8_t *)in_picture; - pkt.size = sizeof(AVPicture); - pkt.pts = av_rescale_q(ost->sync_opts, enc->time_base, ost->st->time_base); - pkt.flags |= AV_PKT_FLAG_KEY; + if (s->oformat->flags & AVFMT_RAWPICTURE && + enc->codec->id == CODEC_ID_RAWVIDEO) { + /* raw pictures are written as AVPicture structure to + avoid any copies. We support temporarily the older + method. */ + enc->coded_frame->interlaced_frame = in_picture->interlaced_frame; + enc->coded_frame->top_field_first = in_picture->top_field_first; + pkt.data = (uint8_t *)in_picture; + pkt.size = sizeof(AVPicture); + pkt.pts = av_rescale_q(in_picture->pts, enc->time_base, ost->st->time_base); + pkt.flags |= AV_PKT_FLAG_KEY; - write_frame(s, &pkt, ost); - } else { - int got_packet; - AVFrame big_picture; - - big_picture = *in_picture; - /* better than nothing: use input picture interlaced - settings */ - big_picture.interlaced_frame = in_picture->interlaced_frame; - if (ost->st->codec->flags & (CODEC_FLAG_INTERLACED_DCT|CODEC_FLAG_INTERLACED_ME)) { - if (ost->top_field_first == -1) - big_picture.top_field_first = in_picture->top_field_first; - else - big_picture.top_field_first = !!ost->top_field_first; - } + write_frame(s, &pkt, ost); + } else { + int got_packet; + AVFrame big_picture; + + big_picture = *in_picture; + /* better than nothing: use input picture interlaced + settings */ + big_picture.interlaced_frame = in_picture->interlaced_frame; + if (ost->st->codec->flags & (CODEC_FLAG_INTERLACED_DCT|CODEC_FLAG_INTERLACED_ME)) { + if (ost->top_field_first == -1) + big_picture.top_field_first = in_picture->top_field_first; + else + big_picture.top_field_first = !!ost->top_field_first; + } - /* handles same_quant here. This is not correct because it may - not be a global option */ - big_picture.quality = quality; - if (!enc->me_threshold) - big_picture.pict_type = 0; - big_picture.pts = ost->sync_opts; - 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++; - } - ret = avcodec_encode_video2(enc, &pkt, &big_picture, &got_packet); - if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Video encoding failed\n"); - exit_program(1); - } + /* handles same_quant here. This is not correct because it may + not be a global option */ + big_picture.quality = quality; + if (!enc->me_threshold) + big_picture.pict_type = 0; + 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++; + } + ret = avcodec_encode_video2(enc, &pkt, &big_picture, &got_packet); + if (ret < 0) { + av_log(NULL, AV_LOG_FATAL, "Video encoding failed\n"); + exit_program(1); + } - if (got_packet) { - if (pkt.pts != AV_NOPTS_VALUE) - pkt.pts = av_rescale_q(pkt.pts, enc->time_base, ost->st->time_base); - if (pkt.dts != AV_NOPTS_VALUE) - pkt.dts = av_rescale_q(pkt.dts, enc->time_base, ost->st->time_base); + if (got_packet) { + if (pkt.pts != AV_NOPTS_VALUE) + pkt.pts = av_rescale_q(pkt.pts, enc->time_base, ost->st->time_base); + if (pkt.dts != AV_NOPTS_VALUE) + pkt.dts = av_rescale_q(pkt.dts, enc->time_base, ost->st->time_base); - write_frame(s, &pkt, ost); - *frame_size = pkt.size; - video_size += pkt.size; + write_frame(s, &pkt, ost); + *frame_size = pkt.size; + video_size += pkt.size; - /* if two pass, output log */ - if (ost->logfile && enc->stats_out) { - fprintf(ost->logfile, "%s", enc->stats_out); - } + /* if two pass, output log */ + if (ost->logfile && enc->stats_out) { + fprintf(ost->logfile, "%s", enc->stats_out); } } - ost->sync_opts++; - /* - * For video, number of frames in == number of packets out. - * But there may be reordering, so we can't throw away frames on encoder - * flush, we need to limit them here, before they go into encoder. - */ - ost->frame_number++; } + ost->sync_opts++; + /* + * For video, number of frames in == number of packets out. + * But there may be reordering, so we can't throw away frames on encoder + * flush, we need to limit them here, before they go into encoder. + */ + ost->frame_number++; } static double psnr(double d) @@ -1671,7 +1544,7 @@ static int poll_filters(void) OutputFile *of = output_files[ost->file_index]; int ret = 0; - if (!ost->filter || ost->is_past_recording_time) + if (!ost->filter) continue; if (!ost->filtered_frame && !(ost->filtered_frame = avcodec_alloc_frame())) { @@ -1680,7 +1553,7 @@ static int poll_filters(void) avcodec_get_frame_defaults(ost->filtered_frame); filtered_frame = ost->filtered_frame; - while (ret >= 0) { + while (ret >= 0 && !ost->is_past_recording_time) { if (ost->enc->type == AVMEDIA_TYPE_AUDIO && !(ost->enc->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)) ret = av_buffersink_read_samples(ost->filter->filter, &picref, @@ -1692,11 +1565,7 @@ static int poll_filters(void) break; avfilter_copy_buf_props(filtered_frame, picref); - if (ost->enc->type == AVMEDIA_TYPE_VIDEO) - filtered_frame->pts = av_rescale_q(picref->pts, - ost->filter->filter->inputs[0]->time_base, - AV_TIME_BASE_Q); - else if (picref->pts != AV_NOPTS_VALUE) + if (picref->pts != AV_NOPTS_VALUE) filtered_frame->pts = av_rescale_q(picref->pts, ost->filter->filter->inputs[0]->time_base, ost->st->codec->time_base) - @@ -2029,7 +1898,7 @@ static int guess_input_channel_layout(InputStream *ist) return 1; } -static int transcode_audio(InputStream *ist, AVPacket *pkt, int *got_output) +static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output) { AVFrame *decoded_frame; AVCodecContext *avctx = ist->st->codec; @@ -2171,7 +2040,7 @@ static int transcode_audio(InputStream *ist, AVPacket *pkt, int *got_output) return ret; } -static int transcode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_t *pkt_pts) +static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output) { AVFrame *decoded_frame; void *buffer_to_free = NULL; @@ -2183,9 +2052,6 @@ static int transcode_video(InputStream *ist, AVPacket *pkt, int *got_output, int else avcodec_get_frame_defaults(ist->decoded_frame); decoded_frame = ist->decoded_frame; - pkt->pts = *pkt_pts; - pkt->dts = ist->last_dts; - *pkt_pts = AV_NOPTS_VALUE; ret = avcodec_decode_video2(ist->st->codec, decoded_frame, got_output, pkt); @@ -2289,7 +2155,6 @@ static int output_packet(InputStream *ist, const AVPacket *pkt) { int i; int got_output; - int64_t pkt_pts = AV_NOPTS_VALUE; AVPacket avpkt; if (ist->next_dts == AV_NOPTS_VALUE) @@ -2307,8 +2172,6 @@ static int output_packet(InputStream *ist, const AVPacket *pkt) if (pkt->dts != AV_NOPTS_VALUE) ist->next_dts = ist->last_dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q); - if (pkt->pts != AV_NOPTS_VALUE) - pkt_pts = av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q); // while we have more to decode or while the decoder did output something on EOF while (ist->decoding_needed && (avpkt.size > 0 || (!pkt && got_output))) { @@ -2325,10 +2188,10 @@ static int output_packet(InputStream *ist, const AVPacket *pkt) switch (ist->st->codec->codec_type) { case AVMEDIA_TYPE_AUDIO: - ret = transcode_audio (ist, &avpkt, &got_output); + ret = decode_audio (ist, &avpkt, &got_output); break; case AVMEDIA_TYPE_VIDEO: - ret = transcode_video (ist, &avpkt, &got_output, &pkt_pts); + ret = decode_video (ist, &avpkt, &got_output); if (avpkt.duration) ist->next_dts += av_rescale_q(avpkt.duration, ist->st->time_base, AV_TIME_BASE_Q); else if (ist->st->r_frame_rate.num) @@ -2433,7 +2296,7 @@ static int init_input_stream(int ist_index, char *error, int error_len) if (codec->type == AVMEDIA_TYPE_VIDEO && codec->capabilities & CODEC_CAP_DR1) { ist->st->codec->get_buffer = codec_get_buffer; ist->st->codec->release_buffer = codec_release_buffer; - ist->st->codec->opaque = ist; + ist->st->codec->opaque = &ist->buffer_pool; } if (!av_dict_get(ist->opts, "threads", NULL, 0)) @@ -2616,12 +2479,33 @@ static int transcode_init(void) ist->decoding_needed = 1; ost->encoding_needed = 1; + /* + * We want CFR output if and only if one of those is true: + * 1) user specified output framerate with -r + * 2) user specified -vsync cfr + * 3) output format is CFR and the user didn't force vsync to + * something else than CFR + * + * in such a case, set ost->frame_rate + */ + if (codec->codec_type == AVMEDIA_TYPE_VIDEO && + !ost->frame_rate.num && ist && + (video_sync_method == VSYNC_CFR || + (video_sync_method == VSYNC_AUTO && + !(oc->oformat->flags & (AVFMT_NOTIMESTAMPS | AVFMT_VARIABLE_FPS))))) { + ost->frame_rate = ist->st->r_frame_rate.num ? ist->st->r_frame_rate : (AVRational){25, 1}; + if (ost->enc && ost->enc->supported_framerates && !ost->force_fps) { + int idx = av_find_nearest_q_idx(ost->frame_rate, ost->enc->supported_framerates); + ost->frame_rate = ost->enc->supported_framerates[idx]; + } + } + if (!ost->filter && (codec->codec_type == AVMEDIA_TYPE_VIDEO || codec->codec_type == AVMEDIA_TYPE_AUDIO)) { FilterGraph *fg; fg = init_simple_filtergraph(ist, ost); - if (configure_simple_filtergraph(fg)) { + if (configure_filtergraph(fg)) { av_log(NULL, AV_LOG_FATAL, "Error opening filters!\n"); exit(1); } @@ -2636,32 +2520,7 @@ static int transcode_init(void) codec->time_base = (AVRational){ 1, codec->sample_rate }; break; case AVMEDIA_TYPE_VIDEO: - /* - * We want CFR output if and only if one of those is true: - * 1) user specified output framerate with -r - * 2) user specified -vsync cfr - * 3) output format is CFR and the user didn't force vsync to - * something else than CFR - * - * in such a case, set ost->frame_rate - */ - if (!ost->frame_rate.num && ist && - (video_sync_method == VSYNC_CFR || - (video_sync_method == VSYNC_AUTO && - !(oc->oformat->flags & (AVFMT_NOTIMESTAMPS | AVFMT_VARIABLE_FPS))))) { - ost->frame_rate = ist->st->r_frame_rate.num ? ist->st->r_frame_rate : (AVRational){25, 1}; - if (ost->enc && ost->enc->supported_framerates && !ost->force_fps) { - int idx = av_find_nearest_q_idx(ost->frame_rate, ost->enc->supported_framerates); - ost->frame_rate = ost->enc->supported_framerates[idx]; - } - } - if (ost->frame_rate.num) { - codec->time_base = (AVRational){ost->frame_rate.den, ost->frame_rate.num}; - video_sync_method = VSYNC_CFR; - } else if (ist) - codec->time_base = ist->st->time_base; - else - codec->time_base = ost->filter->filter->inputs[0]->time_base; + codec->time_base = ost->filter->filter->inputs[0]->time_base; codec->width = ost->filter->filter->inputs[0]->w; codec->height = ost->filter->filter->inputs[0]->h; @@ -2782,8 +2641,12 @@ static int transcode_init(void) for (i = 0; i < nb_output_files; i++) { oc = output_files[i]->ctx; oc->interrupt_callback = int_cb; - if (avformat_write_header(oc, &output_files[i]->opts) < 0) { - snprintf(error, sizeof(error), "Could not write header for output file #%d (incorrect codec parameters ?)", i); + if ((ret = avformat_write_header(oc, &output_files[i]->opts)) < 0) { + char errbuf[128]; + const char *errbuf_ptr = errbuf; + if (av_strerror(ret, errbuf, sizeof(errbuf)) < 0) + errbuf_ptr = strerror(AVUNERROR(ret)); + snprintf(error, sizeof(error), "Could not write header for output file #%d (incorrect codec parameters ?): %s", i, errbuf_ptr); ret = AVERROR(EINVAL); goto dump_format; } @@ -2806,13 +2669,10 @@ static int transcode_init(void) ist = input_streams[i]; for (j = 0; j < ist->nb_filters; j++) { - AVFilterLink *link = ist->filters[j]->filter->outputs[0]; if (ist->filters[j]->graph->graph_desc) { av_log(NULL, AV_LOG_INFO, " Stream #%d:%d (%s) -> %s", ist->file_index, ist->st->index, ist->dec ? ist->dec->name : "?", - link->dst->filter->name); - if (link->dst->input_count > 1) - av_log(NULL, AV_LOG_INFO, ":%s", link->dstpad->name); + ist->filters[j]->name); if (nb_filtergraphs > 1) av_log(NULL, AV_LOG_INFO, " (graph %d)", ist->filters[j]->graph->index); av_log(NULL, AV_LOG_INFO, "\n"); @@ -2832,10 +2692,7 @@ static int transcode_init(void) if (ost->filter && ost->filter->graph->graph_desc) { /* output from a complex graph */ - AVFilterLink *link = ost->filter->filter->inputs[0]; - av_log(NULL, AV_LOG_INFO, " %s", link->src->filter->name); - if (link->src->output_count > 1) - av_log(NULL, AV_LOG_INFO, ":%s", link->srcpad->name); + av_log(NULL, AV_LOG_INFO, " %s", ost->filter->name); if (nb_filtergraphs > 1) av_log(NULL, AV_LOG_INFO, " (graph %d)", ost->filter->graph->index); @@ -2874,6 +2731,176 @@ static int transcode_init(void) return 0; } +/** + * @return 1 if there are still streams where more output is wanted, + * 0 otherwise + */ +static int need_output(void) +{ + int i; + + for (i = 0; i < nb_output_streams; i++) { + OutputStream *ost = output_streams[i]; + OutputFile *of = output_files[ost->file_index]; + AVFormatContext *os = output_files[ost->file_index]->ctx; + + if (ost->is_past_recording_time || + (os->pb && avio_tell(os->pb) >= of->limit_filesize)) + continue; + if (ost->frame_number >= ost->max_frames) { + int j; + for (j = 0; j < of->ctx->nb_streams; j++) + output_streams[of->ost_index + j]->is_past_recording_time = 1; + continue; + } + + return 1; + } + + return 0; +} + +static int select_input_file(uint8_t *no_packet) +{ + int64_t ipts_min = INT64_MAX; + int i, file_index = -1; + + for (i = 0; i < nb_input_streams; i++) { + InputStream *ist = input_streams[i]; + int64_t ipts = ist->last_dts; + + if (ist->discard || no_packet[ist->file_index]) + continue; + if (!input_files[ist->file_index]->eof_reached) { + if (ipts < ipts_min) { + ipts_min = ipts; + file_index = ist->file_index; + } + } + } + + return file_index; +} + +#if HAVE_PTHREADS +static void *input_thread(void *arg) +{ + InputFile *f = arg; + int ret = 0; + + while (!transcoding_finished && ret >= 0) { + AVPacket pkt; + ret = av_read_frame(f->ctx, &pkt); + + if (ret == AVERROR(EAGAIN)) { + usleep(10000); + ret = 0; + continue; + } else if (ret < 0) + break; + + pthread_mutex_lock(&f->fifo_lock); + while (!av_fifo_space(f->fifo)) + pthread_cond_wait(&f->fifo_cond, &f->fifo_lock); + + av_dup_packet(&pkt); + av_fifo_generic_write(f->fifo, &pkt, sizeof(pkt), NULL); + + pthread_mutex_unlock(&f->fifo_lock); + } + + f->finished = 1; + return NULL; +} + +static void free_input_threads(void) +{ + int i; + + if (nb_input_files == 1) + return; + + transcoding_finished = 1; + + for (i = 0; i < nb_input_files; i++) { + InputFile *f = input_files[i]; + AVPacket pkt; + + if (!f->fifo || f->joined) + continue; + + pthread_mutex_lock(&f->fifo_lock); + while (av_fifo_size(f->fifo)) { + av_fifo_generic_read(f->fifo, &pkt, sizeof(pkt), NULL); + av_free_packet(&pkt); + } + pthread_cond_signal(&f->fifo_cond); + pthread_mutex_unlock(&f->fifo_lock); + + pthread_join(f->thread, NULL); + f->joined = 1; + + while (av_fifo_size(f->fifo)) { + av_fifo_generic_read(f->fifo, &pkt, sizeof(pkt), NULL); + av_free_packet(&pkt); + } + av_fifo_free(f->fifo); + } +} + +static int init_input_threads(void) +{ + int i, ret; + + if (nb_input_files == 1) + return 0; + + for (i = 0; i < nb_input_files; i++) { + InputFile *f = input_files[i]; + + if (!(f->fifo = av_fifo_alloc(8*sizeof(AVPacket)))) + return AVERROR(ENOMEM); + + pthread_mutex_init(&f->fifo_lock, NULL); + pthread_cond_init (&f->fifo_cond, NULL); + + if ((ret = pthread_create(&f->thread, NULL, input_thread, f))) + return AVERROR(ret); + } + return 0; +} + +static int get_input_packet_mt(InputFile *f, AVPacket *pkt) +{ + int ret = 0; + + pthread_mutex_lock(&f->fifo_lock); + + if (av_fifo_size(f->fifo)) { + av_fifo_generic_read(f->fifo, pkt, sizeof(*pkt), NULL); + pthread_cond_signal(&f->fifo_cond); + } else { + if (f->finished) + ret = AVERROR_EOF; + else + ret = AVERROR(EAGAIN); + } + + pthread_mutex_unlock(&f->fifo_lock); + + return ret; +} +#endif + +static int get_input_packet(InputFile *f, AVPacket *pkt) +{ +#if HAVE_PTHREADS + if (nb_input_files > 1) + return get_input_packet_mt(f, pkt); +#endif + return av_read_frame(f->ctx, pkt); +} + /* * The following code is the main loop of the file converter */ @@ -2899,49 +2926,23 @@ static int transcode(void) timer_start = av_gettime(); +#if HAVE_PTHREADS + if ((ret = init_input_threads()) < 0) + goto fail; +#endif + for (; received_sigterm == 0;) { - int file_index, ist_index, past_recording_time = 1; + int file_index, ist_index; AVPacket pkt; - int64_t ipts_min; - - ipts_min = INT64_MAX; /* check if there's any stream where output is still needed */ - for (i = 0; i < nb_output_streams; i++) { - OutputFile *of; - ost = output_streams[i]; - of = output_files[ost->file_index]; - os = output_files[ost->file_index]->ctx; - if (ost->is_past_recording_time || - (os->pb && avio_tell(os->pb) >= of->limit_filesize)) - continue; - if (ost->frame_number > ost->max_frames) { - int j; - for (j = 0; j < of->ctx->nb_streams; j++) - output_streams[of->ost_index + j]->is_past_recording_time = 1; - continue; - } - past_recording_time = 0; - } - if (past_recording_time) + if (!need_output()) { + av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n"); break; - - /* select the stream that we must read now by looking at the - smallest output pts */ - file_index = -1; - for (i = 0; i < nb_input_streams; i++) { - int64_t ipts; - ist = input_streams[i]; - ipts = ist->last_dts; - if (ist->discard || no_packet[ist->file_index]) - continue; - if (!input_files[ist->file_index]->eof_reached) { - if (ipts < ipts_min) { - ipts_min = ipts; - file_index = ist->file_index; - } - } } + + /* select the stream that we must read now */ + file_index = select_input_file(no_packet); /* if none, if is finished */ if (file_index < 0) { if (no_packet_count) { @@ -2950,12 +2951,13 @@ static int transcode(void) usleep(10000); continue; } + av_log(NULL, AV_LOG_VERBOSE, "No more inputs to read from, finishing.\n"); break; } - /* read a frame from it and output it in the fifo */ is = input_files[file_index]->ctx; - ret = av_read_frame(is, &pkt); + ret = get_input_packet(input_files[file_index], &pkt); + if (ret == AVERROR(EAGAIN)) { no_packet[file_index] = 1; no_packet_count++; @@ -3037,6 +3039,9 @@ static int transcode(void) /* dump report by using the output first video and audio streams */ print_report(0, timer_start); } +#if HAVE_PTHREADS + free_input_threads(); +#endif /* at the end of stream, we must flush the decoder buffers */ for (i = 0; i < nb_input_streams; i++) { @@ -3081,6 +3086,9 @@ static int transcode(void) fail: av_freep(&no_packet); +#if HAVE_PTHREADS + free_input_threads(); +#endif if (output_streams) { for (i = 0; i < nb_output_streams; i++) { @@ -3399,6 +3407,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) AVStream *st = ic->streams[i]; AVCodecContext *dec = st->codec; InputStream *ist = av_mallocz(sizeof(*ist)); + char *framerate = NULL; if (!ist) exit_program(1); @@ -3423,6 +3432,14 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) ist->resample_width = dec->width; ist->resample_pix_fmt = dec->pix_fmt; + MATCH_PER_STREAM_OPT(frame_rates, str, framerate, ic, st); + if (framerate && av_parse_video_rate(&ist->framerate, + framerate) < 0) { + av_log(NULL, AV_LOG_ERROR, "Error parsing framerate %s.\n", + framerate); + exit_program(1); + } + break; case AVMEDIA_TYPE_AUDIO: guess_input_channel_layout(ist); @@ -3544,7 +3561,14 @@ static int opt_input_file(OptionsContext *o, const char *opt, const char *filena } } if (o->nb_frame_rates) { - av_dict_set(&format_opts, "framerate", o->frame_rates[o->nb_frame_rates - 1].u.str, 0); + /* set the format-level framerate option; + * this is important for video grabbers, e.g. x11 */ + if (file_iformat && file_iformat->priv_class && + av_opt_find(&file_iformat->priv_class, "framerate", NULL, 0, + AV_OPT_SEARCH_FAKE_OBJ)) { + av_dict_set(&format_opts, "framerate", + o->frame_rates[o->nb_frame_rates - 1].u.str, 0); + } } if (o->nb_frame_sizes) { av_dict_set(&format_opts, "video_size", o->frame_sizes[o->nb_frame_sizes - 1].u.str, 0); @@ -3851,7 +3875,8 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc) const char *p = NULL; char *forced_key_frames = NULL, *frame_rate = NULL, *frame_size = NULL; char *frame_aspect_ratio = NULL, *frame_pix_fmt = NULL; - char *intra_matrix = NULL, *inter_matrix = NULL, *filters = NULL; + char *intra_matrix = NULL, *inter_matrix = NULL; + const char *filters = "null"; int i; MATCH_PER_STREAM_OPT(frame_rates, str, frame_rate, oc, st); @@ -3942,8 +3967,7 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc) MATCH_PER_STREAM_OPT(top_field_first, i, ost->top_field_first, oc, st); MATCH_PER_STREAM_OPT(filters, str, filters, oc, st); - if (filters) - ost->avfilter = av_strdup(filters); + ost->avfilter = av_strdup(filters); } else { MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, ost->copy_initial_nonkeyframes, oc ,st); } @@ -3964,7 +3988,8 @@ static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc) audio_enc->codec_type = AVMEDIA_TYPE_AUDIO; if (!ost->stream_copy) { - char *sample_fmt = NULL, *filters = NULL;; + char *sample_fmt = NULL; + const char *filters = "anull"; MATCH_PER_STREAM_OPT(audio_channels, i, audio_enc->channels, oc, st); @@ -3978,8 +4003,7 @@ static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc) MATCH_PER_STREAM_OPT(audio_sample_rate, i, audio_enc->sample_rate, oc, st); MATCH_PER_STREAM_OPT(filters, str, filters, oc, st); - if (filters) - ost->avfilter = av_strdup(filters); + ost->avfilter = av_strdup(filters); } return ost; @@ -4087,12 +4111,16 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o, { OutputStream *ost; - if (ofilter->out_tmp->filter_ctx->output_pads[ofilter->out_tmp->pad_idx].type != AVMEDIA_TYPE_VIDEO) { - av_log(NULL, AV_LOG_FATAL, "Only video filters are supported currently.\n"); + switch (avfilter_pad_get_type(ofilter->out_tmp->filter_ctx->output_pads, + ofilter->out_tmp->pad_idx)) { + case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc); break; + case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc); break; + default: + av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported " + "currently.\n"); exit_program(1); } - ost = new_video_stream(o, oc); ost->source_index = -1; ost->filter = ofilter; @@ -4163,7 +4191,8 @@ static void opt_output_file(void *optctx, const char *filename) if (!ofilter->out_tmp || ofilter->out_tmp->name) continue; - switch (ofilter->out_tmp->filter_ctx->output_pads[ofilter->out_tmp->pad_idx].type) { + switch (avfilter_pad_get_type(ofilter->out_tmp->filter_ctx->output_pads, + ofilter->out_tmp->pad_idx)) { case AVMEDIA_TYPE_VIDEO: o->video_disable = 1; break; case AVMEDIA_TYPE_AUDIO: o->audio_disable = 1; break; case AVMEDIA_TYPE_SUBTITLE: o->subtitle_disable = 1; break;