From patchwork Mon Oct 17 11:13:34 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: sebechlebskyjan@gmail.com X-Patchwork-Id: 1033 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.133 with SMTP id o127csp271362vsd; Mon, 17 Oct 2016 04:30:58 -0700 (PDT) X-Received: by 10.28.66.149 with SMTP id k21mr8358175wmi.106.1476703858135; Mon, 17 Oct 2016 04:30:58 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id fb7si41023987wjb.76.2016.10.17.04.30.57; Mon, 17 Oct 2016 04:30:58 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id A4F11689A7B; Mon, 17 Oct 2016 14:30:52 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qk0-f196.google.com (mail-qk0-f196.google.com [209.85.220.196]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4A9F5689200 for ; Mon, 17 Oct 2016 14:30:46 +0300 (EEST) Received: by mail-qk0-f196.google.com with SMTP id f128so13036914qkb.0 for ; Mon, 17 Oct 2016 04:30:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=UOtVi15FnJ+sYQmA3+tfV5eS6PNXYfwZyr8mKpji2mg=; b=Ms6pakmVsdk9llWD2kODioqFgjDYBn16U90BYmshDVIIkJmgH2Uq8fIT55lOUBmP81 onbGTBak5UuCTZO/eUPJrSLHqWr0E/9A5JZTAdPQvXK8C9CBrMl6H0Pytkk7KFKxNK8D pI3QL9DLsh3cbkwg/rc+fLhdzC4SiAfTkQrriQCr8c70NbMoLBOobkOmzbfUuM819As+ E5ZaJXPHf0PFhsazpDXospC4XtHIj2fOn4rUdeJrNYe/bKKw0sOFpMWJFxp0Uy5vqEel 6BLtL97fjxvrqw/tY2GTtLWGIZAKfJ6j4zDOAcS57uDGElIKQO4l3gleqpxeVjDFMFQ8 2X/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=UOtVi15FnJ+sYQmA3+tfV5eS6PNXYfwZyr8mKpji2mg=; b=iCzKt3qpNyEGVmLowZSJzeKZiCJDZooTKv8abUUkcPDQJc4i3NVQ1T+q/CUF6D9Qxz 2KCdvAfeGzib65NvxaEeilQOfw6ORa/rE9JFZqRSbUlukQSGiKxEQolsdKiBDbYRihEW AEmE+k9wcofvQtE9QYKllV//Xt/mm9KA+B/ZgLic07cvaZ1sBE/OIf5/xRd3X70DHW47 c4MgWWKuKfV+Kc9BDct9GU6DYdq6uN9/sxTV3tzgs3+/jI0fs5dWCk94eOWqrP1dRnCP caKr/f8X5OE+XsB+WGuRI77b3Mq7OKHF0EN4qSA6DreCv6toBEmzqBEgFCq5Ys/Wl7yA f2Nw== X-Gm-Message-State: AA6/9RlO7mVyFpoo1b2sWNbW0WWzFUxGdIF1uynoNTY5dSaMdbtdBsCEByhQmhFO7Vyndg== X-Received: by 10.194.17.71 with SMTP id m7mr12917755wjd.179.1476702826301; Mon, 17 Oct 2016 04:13:46 -0700 (PDT) Received: from localhost.localdomain (157.174.broadband3.iol.cz. [85.70.174.157]) by smtp.gmail.com with ESMTPSA id a1sm52272871wju.41.2016.10.17.04.13.45 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 17 Oct 2016 04:13:45 -0700 (PDT) From: sebechlebskyjan@gmail.com To: ffmpeg-devel@ffmpeg.org Date: Mon, 17 Oct 2016 13:13:34 +0200 Message-Id: <1476702814-28527-1-git-send-email-sebechlebskyjan@gmail.com> X-Mailer: git-send-email 1.9.1 Subject: [FFmpeg-devel] [PATCH v2] libavformat/tee: Add fifo support for tee X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Jan Sebechlebsky MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Jan Sebechlebsky Signed-off-by: Jan Sebechlebsky --- Thanks for noticing, I've fixed the patch (also some minor formatting issues I've noticed). doc/muxers.texi | 20 +++++++++++++ libavformat/tee.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index dbe53f5..7b4e165 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1473,6 +1473,7 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@anchor{fifo} @section fifo The fifo pseudo-muxer allows the separation of encoding and muxing by using @@ -1580,6 +1581,18 @@ with the tee muxer; encoding can be a very expensive process. It is not useful when using the libavformat API directly because it is then possible to feed the same packets to several muxers directly. +@table @option + +@item use_fifo @var{bool} +If set to 1, slave outputs will be processed in separate thread using @ref{fifo} +muxer. This allows to compensate for different speed/latency/reliability of +outputs and setup transparent recovery. By default this feature is turned off. + +@item fifo_options +Options to pass to fifo pseudo-muxer instances. See @ref{fifo}. + +@end table + The slave outputs are specified in the file name given to the muxer, separated by '|'. If any of the slave name contains the '|' separator, leading or trailing spaces or any special character, it must be @@ -1601,6 +1614,13 @@ output name suffix. Specify a list of bitstream filters to apply to the specified output. +@item use_fifo @var{bool} +This allows to override tee muxer use_fifo option for individual slave muxer. + +@item fifo_options +This allows to override tee muxer fifo_options for individual slave muxer. +See @ref{fifo}. + It is possible to specify to which streams a given bitstream filter applies, by appending a stream specifier to the option separated by @code{/}. @var{spec} must be a stream specifier (see @ref{Format diff --git a/libavformat/tee.c b/libavformat/tee.c index d59ad4d..c668e95 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -40,6 +40,8 @@ typedef struct { AVBSFContext **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; + int use_fifo; + AVDictionary *fifo_options; /** map from input to output streams indexes, * disabled output streams are set to -1 */ @@ -52,15 +54,28 @@ typedef struct TeeContext { unsigned nb_slaves; unsigned nb_alive; TeeSlave *slaves; + int use_fifo; + AVDictionary *fifo_options; + char *fifo_options_str; } TeeContext; static const char *const slave_delim = "|"; static const char *const slave_bsfs_spec_sep = "/"; static const char *const slave_select_sep = ","; +#define OFFSET(x) offsetof(TeeContext, x) +static const AVOption options[] = { + {"use_fifo", "Use fifo pseudo-muxer to separate actual muxers from encoder", + OFFSET(use_fifo), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + {"fifo_options", "fifo pseudo-muxer options", OFFSET(fifo_options_str), + AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, + {NULL} +}; + static const AVClass tee_muxer_class = { .class_name = "Tee muxer", .item_name = av_default_item_name, + .option = options, .version = LIBAVUTIL_VERSION_INT, }; @@ -81,6 +96,27 @@ static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *t return AVERROR(EINVAL); } +static int parse_slave_fifo_options(const char *use_fifo, + const char *fifo_options, TeeSlave *tee_slave) +{ + int ret = 0; + + if (use_fifo) { + if (av_match_name(use_fifo, "true,y,yes,enable,enabled,on,1")) { + tee_slave->use_fifo = 1; + } else if (av_match_name(use_fifo, "false,n,no,disable,disabled,off,0")) { + tee_slave->use_fifo = 0; + } else { + return AVERROR(EINVAL); + } + } + + if (fifo_options) + ret = av_dict_parse_string(&tee_slave->fifo_options, fifo_options, "=", ":", 0); + + return ret; +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -125,6 +161,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionaryEntry *entry; char *filename; char *format = NULL, *select = NULL, *on_fail = NULL; + char *use_fifo = NULL, *fifo_options_str = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -145,6 +182,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); STEAL_OPTION("onfail", on_fail); + STEAL_OPTION("use_fifo", use_fifo); + STEAL_OPTION("fifo_options", fifo_options_str); ret = parse_slave_failure_policy_option(on_fail, tee_slave); if (ret < 0) { @@ -153,7 +192,41 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) goto end; } - ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); + ret = parse_slave_fifo_options(use_fifo, fifo_options_str, tee_slave); + if (ret < 0) { + av_log(avf, AV_LOG_ERROR, "Error parsing fifo options: %s\n", av_err2str(ret)); + goto end; + } + + if (tee_slave->use_fifo) { + + if (options) { + char *format_options_str = NULL; + ret = av_dict_get_string(options, &format_options_str, '=', ':'); + if (ret < 0) + goto end; + + ret = av_dict_set(&tee_slave->fifo_options, "format_options", format_options_str, + AV_DICT_DONT_STRDUP_VAL); + if (ret < 0) + goto end; + } + + if (format) { + ret = av_dict_set(&tee_slave->fifo_options, "fifo_format", format, + AV_DICT_DONT_STRDUP_VAL); + format = NULL; + if (ret < 0) + goto end; + } + + av_dict_free(&options); + options = tee_slave->fifo_options; + + ret = avformat_alloc_output_context2(&avf2, NULL, "fifo", filename); + } else { + ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); + } if (ret < 0) goto end; tee_slave->avf = avf2; @@ -394,6 +467,12 @@ static int tee_write_header(AVFormatContext *avf) filename++; } + if (tee->fifo_options_str) { + ret = av_dict_parse_string(&tee->fifo_options, tee->fifo_options_str, "=", ":", 0); + if (ret < 0) + goto fail; + } + if (!(tee->slaves = av_mallocz_array(nb_slaves, sizeof(*tee->slaves)))) { ret = AVERROR(ENOMEM); goto fail; @@ -401,6 +480,12 @@ static int tee_write_header(AVFormatContext *avf) tee->nb_slaves = tee->nb_alive = nb_slaves; for (i = 0; i < nb_slaves; i++) { + + tee->slaves[i].use_fifo = tee->use_fifo; + ret = av_dict_copy(&tee->slaves[i].fifo_options, tee->fifo_options, 0); + if (ret < 0) + goto fail; + if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) { ret = tee_process_slave_failure(avf, i, ret); if (ret < 0)