[FFmpeg-devel] [RFC] lavf/tee per-output stream selection

Nicolas George nicolas.george at normalesup.org
Sun Aug 4 12:07:14 CEST 2013


Le sextidi 16 thermidor, an CCXXI, Stefano Sabatini a écrit :
> >From 7b08890d825abc7df66bd49e8cbb1f4ae5c888b1 Mon Sep 17 00:00:00 2001
> From: Stefano Sabatini <stefasab at gmail.com>
> Date: Sat, 3 Aug 2013 11:23:59 +0200
> Subject: [PATCH] lavf/tee: add special select option
> 
> TODO: bump micro
> ---
>  doc/muxers.texi   |  5 +++++
>  libavformat/tee.c | 48 ++++++++++++++++++++++++++++++++++++++++--------
>  2 files changed, 45 insertions(+), 8 deletions(-)
> 
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 7c5a1c8..37a1734 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -844,6 +844,11 @@ The BNF description of the bitstream filters specification is given by:
>  @var{BSF} ::= @var{BSF_NAME}[+ at var{STREAM_SPECIFIER}]
>  @var{BSFS} ::= @var{BSF}[, at var{BSFS}]
>  @end example
> +
> + at item select
> +Select the streams that should be mapped to the slave output,
> +specified by a stream specifier. If not specified, this defaults to
> +all the input streams.

Examples may be welcome. Is it possible to have several stream specifiers,
i.e. map "video + English audio + French audio" to "video + English audio"
and "video + French audio"?

>  @end table
>  
>  Example: encode something and both archive it in a WebM file and stream it
> diff --git a/libavformat/tee.c b/libavformat/tee.c
> index 90947c7..4604ab4 100644
> --- a/libavformat/tee.c
> +++ b/libavformat/tee.c
> @@ -30,6 +30,7 @@
>  typedef struct {
>      AVFormatContext *fmt_ctx;
>      AVBitStreamFilterContext **bsf_ctxs; ///< bitstream filters per stream

> +    int *stream_map; ///< map between input and output streams, disabled streams are set to -1

Nit: please try to keep lines below 80 chars.

Also, the comment is unclear about the direction of the map, it could be
either:

	stream_num_in_slave  = stream_map[stream_num_in_global]
	stream_num_in_global = stream_map[stream_num_in_slave]

>  } TeeSlave;
>  
>  typedef struct TeeContext {
> @@ -179,9 +180,10 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
>      AVDictionary *options = NULL;
>      AVDictionaryEntry *entry;
>      char *filename;
> -    char *format = NULL, *bsfs = NULL;
> +    char *format = NULL, *bsfs = NULL, *select = NULL;
>      AVFormatContext *avf2 = NULL;
>      AVStream *st, *st2;
> +    int stream_count;
>  
>      if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0)
>          return ret;
> @@ -195,14 +197,42 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
>          entry->value = NULL; /* prevent it from being freed */
>          av_dict_set(&options, "bsfs", NULL, 0);
>      }
> +    if ((entry = av_dict_get(options, "select", NULL, 0))) {
> +        select = entry->value;
> +        entry->value = NULL; /* prevent it from being freed */
> +        av_dict_set(&options, "select", NULL, 0);
> +    }

This hunk really needs factoring.

>  
>      ret = avformat_alloc_output_context2(&avf2, NULL, format, filename);
>      if (ret < 0)
>          goto fail;
>      av_free(format);
>  
> +    tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(int));

sizeof(*tee_slave->stream_map)

> +    if (!tee_slave->stream_map) {
> +        ret = AVERROR(ENOMEM);
> +        goto fail;
> +    }
> +
> +    stream_count = 0;
>      for (i = 0; i < avf->nb_streams; i++) {
>          st = avf->streams[i];

> +        if (select && select[0]) {

Why select[0]?

> +            ret = avformat_match_stream_specifier(avf, avf->streams[i], select);
> +            if (ret < 0) {
> +                av_log(avf, AV_LOG_ERROR,
> +                       "Invalid stream specifier '%s' for output '%s'\n",
> +                       select, slave);
> +                goto fail;
> +            }
> +
> +            if (ret == 0) { /* no match */
> +                tee_slave->stream_map[i] = -1;
> +                continue;
> +            }
> +        }
> +        tee_slave->stream_map[i] = stream_count++;
> +
>          if (!(st2 = avformat_new_stream(avf2, NULL))) {
>              ret = AVERROR(ENOMEM);
>              goto fail;
> @@ -277,6 +307,7 @@ static void close_slaves(AVFormatContext *avf)
>                  av_bitstream_filter_close(bsf_ctx);
>              }
>          }
> +        av_freep(&tee->slaves[i].stream_map);
>  
>          avio_close(avf2->pb);
>          avf2->pb = NULL;
> @@ -419,29 +450,30 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
>      AVPacket pkt2;
>      int ret_all = 0, ret;
>      unsigned i, s;
> +    int s2;
>      AVRational tb, tb2;
>  
>      for (i = 0; i < tee->nb_slaves; i++) {
>          avf2 = tee->slaves[i].fmt_ctx;
>          s = pkt->stream_index;
> -        if (s >= avf2->nb_streams) {
> -            if (!ret_all)
> -                ret_all = AVERROR(EINVAL);
> +        s2 = tee->slaves[i].stream_map[s];
> +        if (s2 < 0)
>              continue;
> -        }
> +
>          if ((ret = av_copy_packet(&pkt2, pkt)) < 0 ||
>              (ret = av_dup_packet(&pkt2))< 0)
>              if (!ret_all) {
>                  ret = ret_all;
>                  continue;
>              }
> -        tb  = avf ->streams[s]->time_base;
> -        tb2 = avf2->streams[s]->time_base;
> +        tb  = avf ->streams[s ]->time_base;
> +        tb2 = avf2->streams[s2]->time_base;
>          pkt2.pts      = av_rescale_q(pkt->pts,      tb, tb2);
>          pkt2.dts      = av_rescale_q(pkt->dts,      tb, tb2);
>          pkt2.duration = av_rescale_q(pkt->duration, tb, tb2);
> +        pkt2.stream_index = s2;
>  
> -        filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsf_ctxs[s]);
> +        filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsf_ctxs[s2]);
>          if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0)
>              if (!ret_all)
>                  ret_all = ret;

> >From 371a649c329f57b023fdfe6d9eedbd46b2d7991c Mon Sep 17 00:00:00 2001
> From: Stefano Sabatini <stefasab at gmail.com>
> Date: Sat, 3 Aug 2013 14:05:13 +0200
> Subject: [PATCH] doc/muxers: add elaborate example for the tee muxer
> 
> ---
>  doc/muxers.texi | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 37a1734..beceeab 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -859,6 +859,16 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a
>    "archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/"
>  @end example
>  
> +Example: use @command{ffmpeg} to encode the input, and send the output
> +to three different destinations. The @code{dump_extra} bitstream
> +filter is used to add extradata information to all the video keyframes
> +in output, as requested by the MPEG-TS format. The @filename{out.aac}
> +output is filtered in order to contain only audio packets.
> + at example
> +ffmpeg -i ... -map 0 -flags +global_header -c:v libx264 -c:a aac -strict experimental
> +       -f tee "[bsfs=dump_extra+v]out.ts|[movflags=+faststart]out.mp4|[select=a]out.aac"
> + at end example
> +
>  Note: some codecs may need different options depending on the output format;
>  the auto-detection of this can not work with the tee muxer. The main example
>  is the @option{global_header} flag.

This one seems ok.

Regards,

-- 
  Nicolas George
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20130804/28426b66/attachment.asc>


More information about the ffmpeg-devel mailing list