@end table
+@anchor{fifo}
@section fifo
The fifo pseudo-muxer allows the separation of encoding and muxing by using
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
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
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 */
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,
};
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) {
+ /*TODO - change this to use proper function for parsing boolean
+ * options when there is one */
+ 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;
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;
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) {
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,
+ tee_slave->use_fifo ? "fifo" :format, filename);
if (ret < 0)
goto end;
tee_slave->avf = avf2;
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;
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)