[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