1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

libavformat/tee: Add fifo support for tee

Signed-off-by: Jan Sebechlebsky <sebechlebskyjan@gmail.com>
This commit is contained in:
Jan Sebechlebsky 2016-10-30 19:18:21 +01:00
parent 616513ef6e
commit 7c91ee01cc
2 changed files with 106 additions and 1 deletions

View File

@ -1494,6 +1494,7 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove)
@end table @end table
@anchor{fifo}
@section fifo @section fifo
The fifo pseudo-muxer allows the separation of encoding and muxing by using The fifo pseudo-muxer allows the separation of encoding and muxing by using
@ -1601,6 +1602,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 useful when using the libavformat API directly because it is then possible
to feed the same packets to several muxers directly. 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, The slave outputs are specified in the file name given to the muxer,
separated by '|'. If any of the slave name contains the '|' separator, separated by '|'. If any of the slave name contains the '|' separator,
leading or trailing spaces or any special character, it must be leading or trailing spaces or any special character, it must be
@ -1622,6 +1635,13 @@ output name suffix.
Specify a list of bitstream filters to apply to the specified Specify a list of bitstream filters to apply to the specified
output. 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 It is possible to specify to which streams a given bitstream filter
applies, by appending a stream specifier to the option separated by applies, by appending a stream specifier to the option separated by
@code{/}. @var{spec} must be a stream specifier (see @ref{Format @code{/}. @var{spec} must be a stream specifier (see @ref{Format

View File

@ -40,6 +40,8 @@ typedef struct {
AVBSFContext **bsfs; ///< bitstream filters per stream AVBSFContext **bsfs; ///< bitstream filters per stream
SlaveFailurePolicy on_fail; SlaveFailurePolicy on_fail;
int use_fifo;
AVDictionary *fifo_options;
/** map from input to output streams indexes, /** map from input to output streams indexes,
* disabled output streams are set to -1 */ * disabled output streams are set to -1 */
@ -52,15 +54,28 @@ typedef struct TeeContext {
unsigned nb_slaves; unsigned nb_slaves;
unsigned nb_alive; unsigned nb_alive;
TeeSlave *slaves; TeeSlave *slaves;
int use_fifo;
AVDictionary *fifo_options;
char *fifo_options_str;
} TeeContext; } TeeContext;
static const char *const slave_delim = "|"; static const char *const slave_delim = "|";
static const char *const slave_bsfs_spec_sep = "/"; static const char *const slave_bsfs_spec_sep = "/";
static const char *const slave_select_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 = { static const AVClass tee_muxer_class = {
.class_name = "Tee muxer", .class_name = "Tee muxer",
.item_name = av_default_item_name, .item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT, .version = LIBAVUTIL_VERSION_INT,
}; };
@ -81,6 +96,29 @@ static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *t
return AVERROR(EINVAL); 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) static int close_slave(TeeSlave *tee_slave)
{ {
AVFormatContext *avf; AVFormatContext *avf;
@ -125,6 +163,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
AVDictionaryEntry *entry; AVDictionaryEntry *entry;
char *filename; char *filename;
char *format = NULL, *select = NULL, *on_fail = NULL; char *format = NULL, *select = NULL, *on_fail = NULL;
char *use_fifo = NULL, *fifo_options_str = NULL;
AVFormatContext *avf2 = NULL; AVFormatContext *avf2 = NULL;
AVStream *st, *st2; AVStream *st, *st2;
int stream_count; int stream_count;
@ -145,6 +184,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
STEAL_OPTION("f", format); STEAL_OPTION("f", format);
STEAL_OPTION("select", select); STEAL_OPTION("select", select);
STEAL_OPTION("onfail", on_fail); 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); ret = parse_slave_failure_policy_option(on_fail, tee_slave);
if (ret < 0) { if (ret < 0) {
@ -153,7 +194,39 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
goto end; 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) if (ret < 0)
goto end; goto end;
tee_slave->avf = avf2; tee_slave->avf = avf2;
@ -394,6 +467,12 @@ static int tee_write_header(AVFormatContext *avf)
filename++; 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)))) { if (!(tee->slaves = av_mallocz_array(nb_slaves, sizeof(*tee->slaves)))) {
ret = AVERROR(ENOMEM); ret = AVERROR(ENOMEM);
goto fail; goto fail;
@ -401,6 +480,12 @@ static int tee_write_header(AVFormatContext *avf)
tee->nb_slaves = tee->nb_alive = nb_slaves; tee->nb_slaves = tee->nb_alive = nb_slaves;
for (i = 0; i < nb_slaves; i++) { 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) { if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) {
ret = tee_process_slave_failure(avf, i, ret); ret = tee_process_slave_failure(avf, i, ret);
if (ret < 0) if (ret < 0)