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

Nicolas George nicolas.george at normalesup.org
Wed Aug 21 15:01:44 CEST 2013


Le duodi 2 fructidor, an CCXXI, Stefano Sabatini a écrit :
> Updated and rebased.
> -- 
> FFmpeg = Faithless Fast Majestic Perennial Extravagant Ghost

> >From b49874d5420cc2c0d8c6b3933ae74b8bebb9fb8d Mon Sep 17 00:00:00 2001
> From: Stefano Sabatini <stefasab at gmail.com>
> Date: Thu, 8 Aug 2013 12:11:59 +0200
> Subject: [PATCH] lavf/tee: add special select option
> 
> TODO: bump micro
> ---
>  doc/muxers.texi   |  5 +++++
>  libavformat/tee.c | 57 +++++++++++++++++++++++++++++++++++++++++++------------
>  2 files changed, 50 insertions(+), 12 deletions(-)
> 
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 52da73e..c9c4416 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -871,6 +871,11 @@ separated by @code{/}. If the stream specifier is not specified, the
>  bistream filters will be applied to all streams in the output.
>  
>  Several bitstream filters can be specified, separated by ",".
> +
> + 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.
>  @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 f7a8f41..a04c7ef 100644
> --- a/libavformat/tee.c
> +++ b/libavformat/tee.c
> @@ -30,6 +30,9 @@
>  typedef struct {
>      AVFormatContext *avf;
>      AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream
> +

> +    /** map from input to output streams indexes, disabled output streams are set to -1 */

Minor nit: please split this line.

> +    int *stream_map;
>  } TeeSlave;
>  
>  typedef struct TeeContext {
> @@ -134,24 +137,52 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
>      AVDictionary *options = NULL;
>      AVDictionaryEntry *entry;
>      char *filename;
> -    char *format = NULL;
> +    char *format = NULL, *select = NULL;
>      AVFormatContext *avf2 = NULL;
>      AVStream *st, *st2;
> +    int stream_count;
>  
>      if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0)
>          return ret;
> -    if ((entry = av_dict_get(options, "f", NULL, 0))) {
> -        format = entry->value;
> -        entry->value = NULL; /* prevent it from being freed */
> -        av_dict_set(&options, "f", NULL, 0);
> +
> +#define STEAL_OPTION(option, field)                             \
> +    if ((entry = av_dict_get(options, option, NULL, 0))) {      \
> +        field = entry->value;                                   \
> +        entry->value = NULL; /* prevent it from being freed */  \
> +        av_dict_set(&options, option, NULL, 0);                 \
>      }
> +    STEAL_OPTION("f", format);
> +    STEAL_OPTION("select", select);
>  
>      ret = avformat_alloc_output_context2(&avf2, NULL, format, filename);
>      if (ret < 0)
>          goto end;
>  
> +    tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map));
> +    if (!tee_slave->stream_map) {
> +        ret = AVERROR(ENOMEM);
> +        goto end;
> +    }
> +
> +    stream_count = 0;
>      for (i = 0; i < avf->nb_streams; i++) {
>          st = avf->streams[i];
> +        if (select) {
> +            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 end;
> +            }
> +
> +            if (ret == 0) { /* no match */
> +                tee_slave->stream_map[i] = -1;
> +                continue;
> +            }
> +        }
> +        tee_slave->stream_map[i] = stream_count++;

Nit: add a warning if some input streams are not mapped at all.

    for (i = 0; i < avf->nb_streams; i++)
	int mapped = 0;
	for (j = 0; i < tee->nb_slaves; j++)
	    mapped += tee->slaves[j].stream_map[i] >= 0;
	if (!mapped)
	    av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped "
		   "to any slave.\n", i);
    }

> +
>          if (!(st2 = avformat_new_stream(avf2, NULL))) {
>              ret = AVERROR(ENOMEM);
>              goto end;
> @@ -266,6 +297,7 @@ static void close_slaves(AVFormatContext *avf)
>                  bsf = bsf_next;
>              }
>          }
> +        av_freep(&tee->slaves[i].stream_map);
>  
>          avio_close(avf2->pb);
>          avf2->pb = NULL;
> @@ -408,29 +440,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].avf;
>          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].bsfs[s]);
> +        filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsfs[s2]);
>          if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0)
>              if (!ret_all)
>                  ret_all = ret;

Apart from that, LGTM. Sorry for the delay.

> >From 3cf0413b8b660b46a4b21eac2992dd68ceced51d 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 elaborated example for the tee muxer
> 
> The example shows how to combine bsfs and select options.

LGTM too.

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/20130821/322c0485/attachment.asc>


More information about the ffmpeg-devel mailing list