From patchwork Thu Oct 6 23:03:03 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: 901 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.66 with SMTP id o63csp1136812vsd; Thu, 6 Oct 2016 16:06:00 -0700 (PDT) X-Received: by 10.28.22.200 with SMTP id 191mr8763114wmw.3.1475795159950; Thu, 06 Oct 2016 16:05:59 -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 uf1si19349593wjc.223.2016.10.06.16.05.57; Thu, 06 Oct 2016 16:05:59 -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 D3EC4689C60; Fri, 7 Oct 2016 02:05:39 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f66.google.com (mail-wm0-f66.google.com [74.125.82.66]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 28D076899E0 for ; Fri, 7 Oct 2016 02:05:33 +0300 (EEST) Received: by mail-wm0-f66.google.com with SMTP id i130so153908wmg.0 for ; Thu, 06 Oct 2016 16:05: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=IKRhCrQUNjgzVKDCXU7T7fWGvnE6ZWTSMFLJIq64DN4=; b=E2dLtttAgTvEGsYVyC9xqA2VLGL3lDJ+XqaiVoAFxJ9vVG7qh26N6scoi8wtFIHWOY AMMCUrO0KJmDvkHvpmPgA1e0c6l7T4rgUNFbzx7P3hzVYZAKpEBoo1KVVHqCF6SIsz16 c4hASrPBSa+kENUTIB5fTFv3HlxYzuttQvIpt5H4OQ8N+UB+WMP5IOa2CvAvNPCZkFTv Y/w1P7QZuXUtai3JzG9gzqPzebk9XRfOcWhqpDa5z/5zKdyBLRaaPaZgtZcuYwoIGBBc +f9uFGxX4lqZ+mgSqP/DOxAzeWK4ptc6NeEYECPm/WMaeEuQkRBeiS+2tMwVv5HKP5tN AwIw== 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=IKRhCrQUNjgzVKDCXU7T7fWGvnE6ZWTSMFLJIq64DN4=; b=BTJ+g912lRMH2asnfw6L/9NCAKwXL1Wy8VbbJWPzBb8GrkIUHbCFz6sG3yuv8GAnJo 6pF/Yvzhvmtz8nbgRS6sE3dDuNOT/fGWCJG47xloH9YIBA9xXO6qUU0dPgWbgDQ9CyyI PhLgMeCjLxMS43z3HPm6PSoxTLUgjqGxKSX71Z+oesrD1Jsy+hHsN/mrjExGkvUsqxss r78y8fq9UWtq6ebmu0aLZ5DAs9WkKnMX+KCpWHL5GUby7X8fE5zDZPA25sf+zL+wv6Tt AtOYXq+wmB3Jlak+1Cz2KiXMoEZCWseHLvBgySZzonBP5sUKplxpcgYI+DAw3aOE/iH4 iHcg== X-Gm-Message-State: AA6/9RmtWSw5OEq0FF0ariB5RIpwNP3906zoz3NKTgip71M6PL2XnOJoLnF99p0vl7dLNA== X-Received: by 10.194.152.167 with SMTP id uz7mr13896747wjb.178.1475795148383; Thu, 06 Oct 2016 16:05:48 -0700 (PDT) Received: from localhost.localdomain (157.174.broadband3.iol.cz. [85.70.174.157]) by smtp.gmail.com with ESMTPSA id u78sm126292wmd.4.2016.10.06.16.05.47 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 06 Oct 2016 16:05:47 -0700 (PDT) From: sebechlebskyjan@gmail.com To: ffmpeg-devel@ffmpeg.org Date: Fri, 7 Oct 2016 01:03:03 +0200 Message-Id: <1475794983-13753-1-git-send-email-sebechlebskyjan@gmail.com> X-Mailer: git-send-email 1.9.1 Subject: [FFmpeg-devel] [PATCH] 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 --- This commit makes use of fifo muxer together with tee muxer easier, fifo muxer does not have to be explicitly specified for each slave. For the most simple use case it is sufficient to turn fifo muxer on for all slaves by switching on use_fifo option. Same options can be passed to all fifo muxer instances of slaves by assigning them to fifo_options of tee. Both use_fifo option and individual fifo_options can be overriden per slave if needed. doc/muxers.texi | 20 +++++++++++++ libavformat/tee.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 9ec2e31..b88b83c 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..764135d 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,43 @@ 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) { + av_free(format_options_str); + goto end; + } + } + + if (format) { + ret = av_dict_set(&tee_slave->fifo_options, "fifo_format", format, + AV_DICT_DONT_STRDUP_VAL); + if (ret < 0) + goto end; + format = NULL; + } + + 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 +469,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 +482,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)