On Mon, Apr 09, 2012 at 06:23:34AM +0200, Anton Khirnov wrote:

You accidentally verb in commit message

> ---
>  avconv.c         |  475 
> +++++++++++++++++++++++++++++++++++++++++++++++-------
>  doc/avconv.texi  |   47 ++++++-
>  doc/filters.texi |    1 +
>  3 files changed, 466 insertions(+), 57 deletions(-)
> 
> diff --git a/avconv.c b/avconv.c
> index d17b064..b864790 100644
> --- a/avconv.c
> +++ b/avconv.c
> @@ -89,6 +89,7 @@ typedef struct StreamMap {
>      int stream_index;
>      int sync_file_index;
>      int sync_stream_index;
> +    char *linklabel;       /** name of an output link, for mapping lavfi 
> outputs */
>  } StreamMap;
>  
>  /**
> @@ -155,9 +156,15 @@ typedef struct OutputFilter {
>      AVFilterContext     *filter;
>      struct OutputStream *ost;
>      struct FilterGraph  *graph;
> +
> +    /* temporary storage until stream maps are processed */
> +    AVFilterInOut       *out_tmp;
>  } OutputFilter;
>  
>  typedef struct FilterGraph {
> +    int            index;
> +    const char    *graph_desc;
> +
>      AVFilterGraph *graph;
>  
>      InputFilter   **inputs;
> @@ -410,6 +417,7 @@ typedef struct OptionsContext {
>  static void reset_options(OptionsContext *o)
>  {
>      const OptionDef *po = options;
> +    int i;
>  
>      /* all OPT_SPEC and OPT_STRING can be freed in generic way */
>      while (po->name) {
> @@ -430,6 +438,8 @@ static void reset_options(OptionsContext *o)
>          po++;
>      }
>  
> +    for (i = 0; i < o->nb_stream_maps; i++)
> +        av_freep(&o->stream_maps[i].linklabel);
>      av_freep(&o->stream_maps);
>      av_freep(&o->meta_data_maps);
>      av_freep(&o->streamid_map);
> @@ -659,14 +669,6 @@ static int configure_video_filters(FilterGraph *fg)
>      if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0)
>          return ret;
>  
> -    codec->width  = fg->outputs[0]->filter->inputs[0]->w;
> -    codec->height = fg->outputs[0]->filter->inputs[0]->h;
> -    codec->sample_aspect_ratio = ost->st->sample_aspect_ratio =
> -        ost->frame_aspect_ratio ? // overridden by the -aspect cli option
> -        av_d2q(ost->frame_aspect_ratio * codec->height/codec->width, 255) :
> -        fg->outputs[0]->filter->inputs[0]->sample_aspect_ratio;
> -    codec->pix_fmt = fg->outputs[0]->filter->inputs[0]->format;
> -
>      ost->filter = fg->outputs[0];
>  
>      return 0;
> @@ -678,6 +680,7 @@ static FilterGraph *init_simple_filtergraph(InputStream 
> *ist, OutputStream *ost)
>  
>      if (!fg)
>          exit_program(1);
> +    fg->index = nb_filtergraphs;
>  
>      fg->outputs = grow_array(fg->outputs, sizeof(*fg->outputs), 
> &fg->nb_outputs,
>                               fg->nb_outputs + 1);
> @@ -704,6 +707,197 @@ static FilterGraph *init_simple_filtergraph(InputStream 
> *ist, OutputStream *ost)
>      return fg;
>  }
>  
> +static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
> +{
> +    InputStream *ist;
> +    AVStream     *st = NULL;
> +    enum AVMediaType type = in->filter_ctx->input_pads[in->pad_idx].type;

hopefully this will never segfault

> +    int i;
> +
> +    // TODO: support other filter types
> +    if (type != AVMEDIA_TYPE_VIDEO) {
> +        av_log(NULL, AV_LOG_FATAL, "Only video filters supported 
> currently.\n");
> +        exit_program(1);
> +    }
> +
> +    if (in->name) {
> +        AVFormatContext *s;
> +        char *p;
> +        int file_idx = strtol(in->name, &p, 0);
> +
> +
> +        if (file_idx < 0 || file_idx > nb_input_files) {
> +            av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtegraph 
> description %s.\n",
> +                   file_idx, fg->graph_desc);
> +            exit_program(1);
> +        }
> +        s = input_files[file_idx].ctx;
> +
> +        for (i = 0; i < s->nb_streams; i++) {
> +            if (s->streams[i]->codec->codec_type != type)
> +                continue;
> +            if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : 
> p) == 1) {
> +                st = s->streams[i];
> +                break;
> +            }
> +        }
> +        if (!st) {
> +            av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph 
> description %s "
> +                   "matches no streams.\n", p, fg->graph_desc);
> +            exit_program(1);
> +        }
> +        ist = &input_streams[input_files[file_idx].ist_index + st->index];
> +    } else {
> +        /* find the first unused stream of corresponding type */
> +        for (i = 0; i < nb_input_streams; i++) {
> +            ist = &input_streams[i];
> +            if (ist->st->codec->codec_type == type && ist->discard)
> +                break;
> +        }
> +        if (i == nb_input_streams) {
> +            av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for "
> +                   "unlabeled input pad %d on filter %s", in->pad_idx,
> +                   in->filter_ctx->name);
> +            exit_program(1);
> +        }
> +    }
> +    ist->discard         = 0;
> +    ist->decoding_needed = 1;
> +    ist->st->discard = AVDISCARD_NONE;
> +
> +    fg->inputs = grow_array(fg->inputs, sizeof(*fg->inputs),
> +                            &fg->nb_inputs, fg->nb_inputs + 1);
> +    if (!(fg->inputs[fg->nb_inputs - 1] = 
> av_mallocz(sizeof(*fg->inputs[0]))))
> +        exit_program(1);
> +    fg->inputs[fg->nb_inputs - 1]->ist   = ist;
> +    fg->inputs[fg->nb_inputs - 1]->graph = fg;
> +
> +    ist->filters = grow_array(ist->filters, sizeof(*ist->filters),
> +                              &ist->nb_filters, ist->nb_filters + 1);
> +    ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1];
> +}
> +
> +static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, 
> AVFilterInOut *out)
> +{
> +    SinkContext  sink_ctx = { .pix_fmts = choose_pixel_fmts(ofilter->ost) };

Can I haz more traditional initialisation for it?

> +    AVCodecContext *codec = ofilter->ost->st->codec;
> +    AVFilterContext *last_filter = out->filter_ctx;
> +    int pad_idx = out->pad_idx;
> +    int ret;
> +
> +    ret = avfilter_graph_create_filter(&ofilter->filter, &sink,
> +                                       "out", NULL, &sink_ctx, fg->graph);
> +    if (ret < 0)
> +        return ret;
> +
> +    if (codec->width || codec->height) {
> +        char args[255];
> +        snprintf(args, sizeof(args), "%d:%d:flags=0x%X",
> +                 codec->width,
> +                 codec->height,
> +                 (unsigned)ofilter->ost->sws_flags);
> +        if ((ret = avfilter_graph_create_filter(&last_filter, 
> avfilter_get_by_name("scale"),
> +                                                NULL, args, NULL, 
> fg->graph)) < 0)
> +            return ret;
> +        if ((ret = avfilter_link(out->filter_ctx, out->pad_idx, last_filter, 
> 0)) < 0)
> +            return ret;
> +        pad_idx = 0;
> +    }
> +
> +    if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
> +        return ret;
> +
> +    return 0;
> +}
> +
> +static int configure_complex_filter(FilterGraph *fg)
> +{
> +    AVFilterInOut *inputs, *outputs, *cur;
> +    int ret, i, init = !fg->graph;
> +
> +    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)
> +        return ret;
> +
> +    for (cur = inputs; 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)
> +            return ret;
> +        if ((ret = avfilter_link(ifilter->filter, 0,
> +                                 cur->filter_ctx, cur->pad_idx)) < 0)
> +            return ret;
> +    }
> +    avfilter_inout_free(&inputs);
> +
> +    if (!init) {
> +        /* 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++)
> +            configure_output_filter(fg, fg->outputs[i], cur);
> +        avfilter_inout_free(&outputs);
> +
> +        if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0)
> +            return ret;
> +    } else {
> +        /* wait until output mappings are processed */
> +        for (cur = outputs; cur;) {
> +            fg->outputs = grow_array(fg->outputs, sizeof(*fg->outputs),
> +                                     &fg->nb_outputs, fg->nb_outputs + 1);
> +            if (!(fg->outputs[fg->nb_outputs - 1] = 
> av_mallocz(sizeof(*fg->outputs[0]))))
> +                exit_program(1);
> +            fg->outputs[fg->nb_outputs - 1]->graph   = fg;
> +            fg->outputs[fg->nb_outputs - 1]->out_tmp = cur;
> +            cur = cur->next;
> +            fg->outputs[fg->nb_outputs - 1]->out_tmp->next = NULL;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int configure_complex_filters(void)
> +{
> +    int i, ret = 0;
> +
> +    for (i = 0; i < nb_filtergraphs; i++)
> +        if (!filtergraphs[i]->graph &&
> +            (ret = configure_complex_filter(filtergraphs[i])) < 0)
> +            return ret;
> +    return 0;
> +}
> +
> +static int configure_filtergraph(FilterGraph *fg)
> +{
> +    return fg->graph_desc ? configure_complex_filter(fg) : 
> configure_video_filters(fg);
> +}
> +
> +static int ist_in_filtergraph(FilterGraph *fg, InputStream *ist)
> +{
> +    int i;
> +    for (i = 0; i < fg->nb_inputs; i++)
> +        if (fg->inputs[i]->ist == ist)
> +            return 1;
> +    return 0;
> +}
> +
>  static void term_exit(void)
>  {
>      av_log(NULL, AV_LOG_QUIET, "");
> @@ -2044,15 +2238,16 @@ static int transcode_video(InputStream *ist, AVPacket 
> *pkt, int *got_output, int
>          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++) {
> -        if (resample_changed &&
> -            configure_video_filters(ist->filters[i]->graph)) {
> +        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++) {
>          // XXX what an ugly hack
>          if (ist->filters[i]->graph->nb_outputs == 1)
>              ist->filters[i]->graph->outputs[0]->ost->last_quality = quality;
> @@ -2279,6 +2474,23 @@ static int init_input_stream(int ist_index, 
> OutputStream *output_streams, int nb
>      return 0;
>  }
>  
> +static InputStream *get_input_stream(OutputStream *ost)
> +{
> +    if (ost->source_index >= 0)
> +        return &input_streams[ost->source_index];
> +
> +    if (ost->filter) {
> +        FilterGraph *fg = ost->filter->graph;
> +        int i;
> +
> +        for (i = 0; i < fg->nb_inputs; i++)
> +            if (fg->inputs[i]->ist->st->codec->codec_type == 
> ost->st->codec->codec_type)
> +                return fg->inputs[i]->ist;
> +    }
> +
> +    return NULL;
> +}
> +
>  static int transcode_init(OutputFile *output_files,
>                            int nb_output_files,
>                            InputFile *input_files,
> @@ -2310,21 +2522,29 @@ static int transcode_init(OutputFile *output_files,
>          }
>      }
>  
> +    /* init complex filtergraphs */
> +    for (i = 0; i < nb_filtergraphs; i++)
> +        if ((ret = avfilter_graph_config(filtergraphs[i]->graph, NULL)) < 0)
> +            return ret;
> +
>      /* for each output stream, we compute the right encoding parameters */
>      for (i = 0; i < nb_output_streams; i++) {
>          ost = &output_streams[i];
>          oc  = output_files[ost->file_index].ctx;
> -        ist = &input_streams[ost->source_index];
> +        ist = get_input_stream(ost);
>  
>          if (ost->attachment_filename)
>              continue;
>  
>          codec  = ost->st->codec;
> -        icodec = ist->st->codec;
>  
> -        ost->st->disposition          = ist->st->disposition;
> -        codec->bits_per_raw_sample    = icodec->bits_per_raw_sample;
> -        codec->chroma_sample_location = icodec->chroma_sample_location;
> +        if (ist) {
> +            icodec = ist->st->codec;
> +
> +            ost->st->disposition          = ist->st->disposition;
> +            codec->bits_per_raw_sample    = icodec->bits_per_raw_sample;
> +            codec->chroma_sample_location = icodec->chroma_sample_location;
> +        }
>  
>          if (ost->stream_copy) {
>              uint64_t extra_size = (uint64_t)icodec->extradata_size + 
> FF_INPUT_BUFFER_PADDING_SIZE;
> @@ -2399,12 +2619,11 @@ static int transcode_init(OutputFile *output_files,
>                  abort();
>              }
>          } else {
> -            FilterGraph *fg;
> -
>              if (!ost->enc)
>                  ost->enc = avcodec_find_encoder(ost->st->codec->codec_id);
>  
> -            ist->decoding_needed = 1;
> +            if (ist)
> +                ist->decoding_needed = 1;
>              ost->encoding_needed = 1;
>  
>              switch (codec->codec_type) {
> @@ -2437,6 +2656,15 @@ static int transcode_init(OutputFile *output_files,
>                  ost->resample_channels    = icodec->channels;
>                  break;
>              case AVMEDIA_TYPE_VIDEO:
> +                if (!ost->filter) {
> +                    FilterGraph *fg;
> +                    fg = init_simple_filtergraph(ist, ost);
> +                    if (configure_video_filters(fg)) {
> +                        av_log(NULL, AV_LOG_FATAL, "Error opening 
> filters!\n");
> +                        exit(1);
> +                    }
> +                }
> +
>                  /*
>                   * We want CFR output if and only if one of those is true:
>                   * 1) user specified output framerate with -r
> @@ -2446,7 +2674,7 @@ static int transcode_init(OutputFile *output_files,
>                   *
>                   * in such a case, set ost->frame_rate
>                   */
> -                if (!ost->frame_rate.num &&
> +                if (!ost->frame_rate.num && ist &&
>                      (video_sync_method ==  VSYNC_CFR ||
>                       (video_sync_method ==  VSYNC_AUTO &&
>                        !(oc->oformat->flags & (AVFMT_NOTIMESTAMPS | 
> AVFMT_VARIABLE_FPS))))) {
> @@ -2459,14 +2687,18 @@ static int transcode_init(OutputFile *output_files,
>                  if (ost->frame_rate.num) {
>                      codec->time_base = (AVRational){ost->frame_rate.den, 
> ost->frame_rate.num};
>                      video_sync_method = VSYNC_CFR;
> -                } else
> +                } else if (ist)
>                      codec->time_base = ist->st->time_base;
> +                else
> +                    codec->time_base = 
> ost->filter->filter->inputs[0]->time_base;
>  
> -                fg = init_simple_filtergraph(ist, ost);
> -                if (configure_video_filters(fg)) {
> -                    av_log(NULL, AV_LOG_FATAL, "Error opening filters!\n");
> -                    exit(1);
> -                }
> +                codec->width  = ost->filter->filter->inputs[0]->w;
> +                codec->height = ost->filter->filter->inputs[0]->h;
> +                codec->sample_aspect_ratio = ost->st->sample_aspect_ratio =
> +                    ost->frame_aspect_ratio ? // overridden by the -aspect 
> cli option
> +                    av_d2q(ost->frame_aspect_ratio * 
> codec->height/codec->width, 255) :
> +                    ost->filter->filter->inputs[0]->sample_aspect_ratio;
> +                codec->pix_fmt = ost->filter->filter->inputs[0]->format;
>  
>                  if(codec->width   != icodec->width  ||
>                     codec->height  != icodec->height ||
> @@ -2521,14 +2753,17 @@ static int transcode_init(OutputFile *output_files,
>          ost = &output_streams[i];
>          if (ost->encoding_needed) {
>              AVCodec      *codec = ost->enc;
> -            AVCodecContext *dec = input_streams[ost->source_index].st->codec;
> +            AVCodecContext *dec = NULL;
>              if (!codec) {
>                  snprintf(error, sizeof(error), "Encoder (codec id %d) not 
> found for output stream #%d:%d",
>                           ost->st->codec->codec_id, ost->file_index, 
> ost->index);
>                  ret = AVERROR(EINVAL);
>                  goto dump_format;
>              }
> -            if (dec->subtitle_header) {
> +
> +            if ((ist = get_input_stream(ost)))
> +                dec = ist->st->codec;
> +            if (dec && dec->subtitle_header) {
>                  ost->st->codec->subtitle_header = 
> av_malloc(dec->subtitle_header_size);
>                  if (!ost->st->codec->subtitle_header) {
>                      ret = AVERROR(ENOMEM);
> @@ -2602,6 +2837,24 @@ static int transcode_init(OutputFile *output_files,
>  
>      /* dump the stream mapping */
>      av_log(NULL, AV_LOG_INFO, "Stream mapping:\n");
> +    for (i = 0; i < nb_input_streams; i++) {
> +        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);
> +                if (nb_filtergraphs > 1)
> +                    av_log(NULL, AV_LOG_INFO, " (graph %d)", 
> ist->filters[j]->graph->index);
> +                av_log(NULL, AV_LOG_INFO, "\n");
> +            }
> +        }
> +    }
> +
>      for (i = 0; i < nb_output_streams; i++) {
>          ost = &output_streams[i];
>  
> @@ -2611,6 +2864,21 @@ static int transcode_init(OutputFile *output_files,
>                     ost->attachment_filename, ost->file_index, ost->index);
>              continue;
>          }
> +
> +        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);
> +            if (nb_filtergraphs > 1)
> +                av_log(NULL, AV_LOG_INFO, " (graph %d)", 
> ost->filter->graph->index);
> +
> +            av_log(NULL, AV_LOG_INFO, " -> Stream #%d:%d (%s)\n", 
> ost->file_index,
> +                   ost->index, ost->enc ? ost->enc->name : "?");
> +            continue;
> +        }
> +
>          av_log(NULL, AV_LOG_INFO, "  Stream #%d:%d -> #%d:%d",
>                 input_streams[ost->source_index].file_index,
>                 input_streams[ost->source_index].st->index,
> @@ -2670,47 +2938,47 @@ static int transcode(OutputFile *output_files,
>      timer_start = av_gettime();
>  
>      for (; received_sigterm == 0;) {
> -        int file_index, ist_index;
> +        int file_index, ist_index, past_recording_time = 1;
>          AVPacket pkt;
>          int64_t ipts_min;
> -        double opts_min;
>  
>          ipts_min = INT64_MAX;
> -        opts_min = 1e100;
>  
> -        /* select the stream that we must read now by looking at the
> -           smallest output pts */
> -        file_index = -1;
> +        /* check if there's any stream where output is still needed */

diego-nit-contraction

>          for (i = 0; i < nb_output_streams; i++) {
>              OutputFile *of;
> -            int64_t ipts;
> -            double  opts;
>              ost = &output_streams[i];
> -            of = &output_files[ost->file_index];
> -            os = output_files[ost->file_index].ctx;
> -            ist = &input_streams[ost->source_index];
> -            if (ost->is_past_recording_time || no_packet[ist->file_index] ||
> +            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;
> -            opts = ost->st->pts.val * av_q2d(ost->st->time_base);
> +            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)
> +            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;
> -                    if (input_sync)
> -                        file_index = ist->file_index;
> -                }
> -                if (opts < opts_min) {
> -                    opts_min = opts;
> -                    if (!input_sync) file_index = ist->file_index;
> +                    file_index = ist->file_index;
>                  }
>              }
> -            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;
> -            }
>          }
>          /* if none, if is finished */
>          if (file_index < 0) {
> @@ -2954,6 +3222,18 @@ static int opt_map(OptionsContext *o, const char *opt, 
> const char *arg)
>      }
>  
>  
> +    if (map[0] == '[') {
> +        /* this mapping refers to lavfi output */
> +        const char *c = map + 1;
> +        o->stream_maps = grow_array(o->stream_maps, sizeof(*o->stream_maps),
> +                                    &o->nb_stream_maps, o->nb_stream_maps + 
> 1);
> +        m = &o->stream_maps[o->nb_stream_maps - 1];
> +        m->linklabel = av_get_token(&c, "]");
> +        if (!m->linklabel) {
> +            av_log(NULL, AV_LOG_ERROR, "Invalid output link label: %s.\n", 
> map);
> +            exit_program(1);
> +        }
> +    } else {
>      file_idx = strtol(map, &p, 0);
>      if (file_idx >= nb_input_files || file_idx < 0) {
>          av_log(NULL, AV_LOG_FATAL, "Invalid input file index: %d.\n", 
> file_idx);
> @@ -2989,6 +3269,7 @@ static int opt_map(OptionsContext *o, const char *opt, 
> const char *arg)
>                  m->sync_stream_index = i;
>              }
>          }
> +    }
>  
>      if (!m) {
>          av_log(NULL, AV_LOG_FATAL, "Stream map '%s' matches no streams.\n", 
> arg);
> @@ -3816,15 +4097,43 @@ static int copy_chapters(InputFile *ifile, OutputFile 
> *ofile, int copy_metadata)
>      return 0;
>  }
>  
> +static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
> +                               AVFormatContext *oc)
> +{
> +    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");
> +        exit_program(1);
> +    }
> +
> +    ost               = new_video_stream(o, oc);
> +    ost->source_index = -1;
> +    ost->filter       = ofilter;
> +
> +    ofilter->ost      = ost;
> +
> +    if (configure_output_filter(ofilter->graph, ofilter, ofilter->out_tmp) < 
> 0) {
> +        av_log(NULL, AV_LOG_FATAL, "Error configuring filter.\n");
> +        exit_program(1);
> +    }
> +    avfilter_inout_free(&ofilter->out_tmp);
> +}
> +
>  static void opt_output_file(void *optctx, const char *filename)
>  {
>      OptionsContext *o = optctx;
>      AVFormatContext *oc;
> -    int i, err;
> +    int i, j, err;
>      AVOutputFormat *file_oformat;
>      OutputStream *ost;
>      InputStream  *ist;
>  
> +    if (configure_complex_filters() < 0) {
> +        av_log(NULL, AV_LOG_FATAL, "Error configuring filters.\n");
> +        exit_program(1);
> +    }
> +
>      if (!strcmp(filename, "-"))
>          filename = "pipe:";
>  
> @@ -3853,6 +4162,24 @@ static void opt_output_file(void *optctx, const char 
> *filename)
>      oc->interrupt_callback = int_cb;
>      av_strlcpy(oc->filename, filename, sizeof(oc->filename));
>  
> +    /* create streams for all unlabeled output pads */
> +    for (i = 0; i < nb_filtergraphs; i++) {
> +        FilterGraph *fg = filtergraphs[i];
> +        for (j = 0; j < fg->nb_outputs; j++) {
> +            OutputFilter *ofilter = fg->outputs[j];
> +
> +            if (!ofilter->out_tmp || ofilter->out_tmp->name)
> +                continue;
> +
> +            switch 
> (ofilter->out_tmp->filter_ctx->output_pads[ofilter->out_tmp->pad_idx].type) {
> +            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;
> +            }
> +            init_output_filter(ofilter, o, oc);
> +        }
> +    }
> +
>      if (!o->nb_stream_maps) {
>          /* pick the "best" stream of each type */
>  #define NEW_STREAM(type, index)\
> @@ -3908,6 +4235,29 @@ static void opt_output_file(void *optctx, const char 
> *filename)
>              if (map->disabled)
>                  continue;
>  
> +            if (map->linklabel) {
> +                FilterGraph *fg;
> +                OutputFilter *ofilter = NULL;
> +                int j, k;
> +
> +                for (j = 0; j < nb_filtergraphs; j++) {
> +                    fg = filtergraphs[j];
> +                    for (k = 0; k < fg->nb_outputs; k++) {
> +                        AVFilterInOut *out = fg->outputs[k]->out_tmp;
> +                        if (out && !strcmp(out->name, map->linklabel)) {
> +                            ofilter = fg->outputs[k];
> +                            goto loop_end;
> +                        }
> +                    }
> +                }
> +loop_end:
> +                if (!ofilter) {
> +                    av_log(NULL, AV_LOG_FATAL, "Output with label '%s' does 
> not exist "
> +                           "in any defined filter graph.\n", map->linklabel);
> +                    exit_program(1);
> +                }
> +                init_output_filter(ofilter, o, oc);
> +            } else {
>              ist = &input_streams[input_files[map->file_index].ist_index + 
> map->stream_index];
>              switch (ist->st->codec->codec_type) {
>              case AVMEDIA_TYPE_VIDEO:    ost = new_video_stream(o, oc);    
> break;
> @@ -3926,6 +4276,7 @@ static void opt_output_file(void *optctx, const char 
> *filename)
>                                             map->sync_stream_index];
>              ist->discard = 0;
>              ist->st->discard = AVDISCARD_NONE;
> +            }
>          }
>      }
>  
> @@ -4456,6 +4807,17 @@ static void parse_cpuflags(int argc, char **argv, 
> const OptionDef *options)
>          opt_cpuflags("cpuflags", argv[idx + 1]);
>  }
>  
> +static int opt_filter_complex(const char *opt, const char *arg)
> +{
> +    filtergraphs = grow_array(filtergraphs, sizeof(*filtergraphs),
> +                              &nb_filtergraphs, nb_filtergraphs + 1);
> +    if (!(filtergraphs[nb_filtergraphs - 1] = 
> av_mallocz(sizeof(*filtergraphs[0]))))
> +        return AVERROR(ENOMEM);
> +    filtergraphs[nb_filtergraphs - 1]->index       = nb_filtergraphs - 1;
> +    filtergraphs[nb_filtergraphs - 1]->graph_desc = arg;
> +    return 0;
> +}
> +
>  #define OFFSET(x) offsetof(OptionsContext, x)
>  static const OptionDef options[] = {
>      /* main options */
> @@ -4500,6 +4862,7 @@ static const OptionDef options[] = {
>      { "q", HAS_ARG | OPT_EXPERT | OPT_DOUBLE | OPT_SPEC, {.off = 
> OFFSET(qscale)}, "use fixed quality scale (VBR)", "q" },
>      { "qscale", HAS_ARG | OPT_EXPERT | OPT_DOUBLE | OPT_SPEC, {.off = 
> OFFSET(qscale)}, "use fixed quality scale (VBR)", "q" },
>      { "filter", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(filters)}, 
> "set stream filterchain", "filter_list" },
> +    { "filter_complex", HAS_ARG | OPT_EXPERT, {(void*)opt_filter_complex}, 
> "create a complex filtergraph", "graph_description" },
>      { "stats", OPT_BOOL, {&print_stats}, "print progress report during 
> encoding", },
>      { "attach", HAS_ARG | OPT_FUNC2, {(void*)opt_attach}, "add an attachment 
> to the output file", "filename" },
>      { "dump_attachment", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = 
> OFFSET(dump_attachment)}, "extract an attachment into a file", "filename" },
> diff --git a/doc/avconv.texi b/doc/avconv.texi
> index 64892a4..32747c3 100644
> --- a/doc/avconv.texi
> +++ b/doc/avconv.texi
> @@ -207,6 +207,9 @@ codec-dependent.
>  @var{filter_graph} is a description of the filter graph to apply to
>  the stream. Use @code{-filters} to show all the available filters
>  (including also sources and sinks).
> +
> +See also the @option{-filter_complex} option if you want to create filter 
> graphs
> +with multiple inputs and/or outputs........

ETOOMANYTRAILINGDOTS

>  @item -pre[:@var{stream_specifier}] @var{preset_name} 
> (@emph{output,per-stream})
>  Specify the preset for matching stream(s).
>  
> @@ -460,7 +463,7 @@ Synchronize read on input.
>  @section Advanced options
>  
>  @table @option
> -@item -map 
> [-]@var{input_file_id}[:@var{stream_specifier}][,@var{sync_file_id}[:@var{stream_specifier}]]
>  (@emph{output})
> +@item -map 
> [-]@var{input_file_id}[:@var{stream_specifier}][,@var{sync_file_id}[:@var{stream_specifier}]]
>  | @var{[linklabel]} (@emph{output})
>  
>  Designate one or more input streams as a source for the output file. Each 
> input
>  stream is identified by the input file index @var{input_file_id} and
> @@ -476,6 +479,10 @@ the source for output stream 1, etc.
>  A @code{-} character before the stream identifier creates a "negative" 
> mapping.
>  It disables matching streams from already created mappings.
>  
> +An alternative @var{[linklabel]} form will map outputs from complex filter
> +graphs (see the @option{-filter_complex} option) to the output file.
> +@var{linklabel} must correspond to a defined output link label in the graph.
> +
>  For example, to map ALL streams from the first input file to output
>  @example
>  avconv -i INPUT -map 0 output
> @@ -639,6 +646,44 @@ Force a tag/fourcc for matching streams.
>  @item -cpuflags mask (@emph{global})
>  Set a mask that's applied to autodetected CPU flags.  This option is intended
>  for testing. Do not use it unless you know what you're doing.
> +
> +@item -filter_complex @var{filtergraph} (@emph{global})
> +Define a complex filter graph -- i.e. one with arbitrary number of inputs 
> and/or

nit: comma instead of dash would look better here

> +outputs. For simple graphs -- those with one input and one output of the same
> +type -- see the @option{-filter} options. @var{filtergraph} is a description 
> of
> +the filter graph, as described in @ref{Filtergraph syntax}.
> +
> +Input link labels must refer to input streams using the
> +@code{[file_index:stream_specifier]} syntax (i.e. the same as @option{-map}
> +uses). If @var{stream_specifier} matches multiple streams, the first one 
> will be
> +used. An unlabeled input will be connected to the first unused input stream 
> of
> +the matching type.
> +
> +Output link labels are referred to with @option{-map}. Unlabeled outputs are
> +added to the first output file.
> +
> +For example, to overlay an image over video
> +@example
> +avconv -i video.mkv -i image.png -filter_complex '[0:v][1:v]overlay[out]' 
> -map
> +'[out]' out.mkv
> +@end example
> +Here @code{[0:v]} refers to the first video stream in the first input file,
> +which is linked to the first (main) input of the overlay filter. Similarly 
> the
> +first video stream in the second input is linked to the second (overlay) 
> input
> +of overlay.
> +
> +Assuming there is only one video stream in each input file, we can omit input
> +labels, so the above is equivalent to
> +@example
> +avconv -i video.mkv -i image.png -filter_complex 'overlay[out]' -map
> +'[out]' out.mkv
> +@end example
> +
> +Furthermore we can omit the output label and the single output from the 
> filter
> +graph will be added to the output file automatically, so we can simply write
> +@example
> +avconv -i video.mkv -i image.png -filter_complex 'overlay' out.mkv
> +@end example
>  @end table
>  @c man end OPTIONS
>  
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 446e124..f0367bf 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -14,6 +14,7 @@ number of input and output pads of the filter.
>  A filter with no input pads is called a "source", a filter with no
>  output pads is called a "sink".
>  
> +@anchor{Filtergraph syntax}
>  @section Filtergraph syntax
>  
>  A filtergraph can be represented using a textual representation, which
> -- 

in general might be OK
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to