[FFmpeg-devel] [PATCH] libavformat/fifo: Fix initialization of underlying AVFormatContext
From: Jan SebechlebskyPass filename to AVFormatContext of underlying muxer. This commit fixes bug #6308. Signed-off-by: Jan Sebechlebsky --- libavformat/fifo.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libavformat/fifo.c b/libavformat/fifo.c index 2cbe5c5..c881f31 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -442,13 +442,14 @@ static void *fifo_consumer_thread(void *data) return NULL; } -static int fifo_mux_init(AVFormatContext *avf, AVOutputFormat *oformat) +static int fifo_mux_init(AVFormatContext *avf, AVOutputFormat *oformat, + const char *filename) { FifoContext *fifo = avf->priv_data; AVFormatContext *avf2; int ret = 0, i; -ret = avformat_alloc_output_context2(, oformat, NULL, NULL); +ret = avformat_alloc_output_context2(, oformat, NULL, filename); if (ret < 0) return ret; @@ -505,7 +506,7 @@ static int fifo_init(AVFormatContext *avf) return ret; } -ret = fifo_mux_init(avf, oformat); +ret = fifo_mux_init(avf, oformat, avf->filename); if (ret < 0) return ret; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] libavformat/tee: Add possibility to pass fifo options by using fifo_ prefix
From: Jan Sebechlebsky--- doc/muxers.texi | 9 + libavformat/tee.c | 20 2 files changed, 29 insertions(+) diff --git a/doc/muxers.texi b/doc/muxers.texi index ced223e..139ced0 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1603,6 +1603,11 @@ 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}. +@item fifo_[opt] +Option will be passed as [opt] to fifo pseudo-muxer instances. For example +fifo_queue_size will set queue_size option of fifo pseudo muxer. +This allows to specify fifo_options without need of extensive escaping. + @end table The slave outputs are specified in the file name given to the muxer, @@ -1633,6 +1638,10 @@ This allows to override tee muxer use_fifo option for individual slave muxer. This allows to override tee muxer fifo_options for individual slave muxer. See @ref{fifo}. +@item fifo_[opt] +This allows to override tee muxer fifo_options by setting [opt] for fifo of +individual slave muxer, without need of another level of escaping. + 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 99259a7..7001e38 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -156,6 +156,23 @@ static void close_slaves(AVFormatContext *avf) av_freep(>slaves); } +static int steal_fifo_options(AVDictionary **src_options, AVDictionary **dst_options) +{ +int ret; +AVDictionaryEntry *entry; + +while((entry = av_dict_get(*src_options, "fifo_", NULL, AV_DICT_IGNORE_SUFFIX))) { +ret = av_dict_set(dst_options, entry->key + 5, /* 5 = strlen("fifo_") */ + entry->value, AV_DICT_DONT_STRDUP_VAL); +if (ret < 0) +return ret; + +entry->value = NULL; +ret = av_dict_set(src_options, entry->key, NULL, 0); +} +return 0; +} + static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) { int i, ret; @@ -186,6 +203,9 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("onfail", on_fail); STEAL_OPTION("use_fifo", use_fifo); STEAL_OPTION("fifo_options", fifo_options_str); +ret = steal_fifo_options(, _slave->fifo_options); +if (ret < 0) +goto end; ret = parse_slave_failure_policy_option(on_fail, tee_slave); if (ret < 0) { -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 0/2] Fifo support for tee
From: Jan SebechlebskyHello, I am re-sending the patch(es) adding support for fifo pseudo-muxer in tee muxer. I will apply the first one in few days (since it already has been reviewed by Nicolas, and it differs only in issues Nicolas pointed out in review and these should be fixed now). The second one adds feature suggested by Nicolas - possibility to pass options to fifo muxer by prefixing fifo options with fifo_ string. I will wait with that second patch for a reaction. Thanks, Jan Jan Sebechlebsky (2): libavformat/tee: Add fifo support for tee libavformat/tee: Add possibility to pass fifo options by using fifo_ prefix doc/muxers.texi | 29 +++ libavformat/tee.c | 105 +- 2 files changed, 133 insertions(+), 1 deletion(-) -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 1/2] libavformat/tee: Add fifo support for tee
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - Refactoring based on Nicolas's comments - Added TODO regarding boolean option parsing 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 075b8d3..ced223e 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1485,6 +1485,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 @@ -1592,6 +1593,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 @@ -1613,6 +1626,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..99259a7 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,29 @@ 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) { +/*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(_slave->fifo_options, fifo_options, "=", ":", 0); + +return ret; +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -125,6 +163,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 +184,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format);
[FFmpeg-devel] [PATCH v2] avformat/tee: Add FATE tests for tee
From: Jan SebechlebskyThis commit also adds new diff option for fate tests allowing do compare multiple tuples of files. Signed-off-by: Jan Sebechlebsky --- Changes since the last version: - fixed out of tree build (previous version refered to SRC_PATH instead of TARGET_PATH, thanks to Michael for noticing that) tests/Makefile| 1 + tests/fate-run.sh | 7 tests/fate/tee-muxer.mak | 22 ++ tests/ref/fate/tee-muxer-h264 | 2 + tests/ref/fate/tee-muxer-h264-audio | 30 + tests/ref/fate/tee-muxer-h264-copy| 47 + tests/ref/fate/tee-muxer-ignorefail | 79 +++ tests/ref/fate/tee-muxer-tstsrc | 2 + tests/ref/fate/tee-muxer-tstsrc-audio | 49 ++ 9 files changed, 239 insertions(+) create mode 100644 tests/fate/tee-muxer.mak create mode 100644 tests/ref/fate/tee-muxer-h264 create mode 100644 tests/ref/fate/tee-muxer-h264-audio create mode 100644 tests/ref/fate/tee-muxer-h264-copy create mode 100644 tests/ref/fate/tee-muxer-ignorefail create mode 100644 tests/ref/fate/tee-muxer-tstsrc create mode 100644 tests/ref/fate/tee-muxer-tstsrc-audio diff --git a/tests/Makefile b/tests/Makefile index 8e810ff..e23260f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -164,6 +164,7 @@ include $(SRC_PATH)/tests/fate/real.mak include $(SRC_PATH)/tests/fate/screen.mak include $(SRC_PATH)/tests/fate/source.mak include $(SRC_PATH)/tests/fate/subtitles.mak +include $(SRC_PATH)/tests/fate/tee-muxer.mak include $(SRC_PATH)/tests/fate/utvideo.mak include $(SRC_PATH)/tests/fate/video.mak include $(SRC_PATH)/tests/fate/voice.mak diff --git a/tests/fate-run.sh b/tests/fate-run.sh index c640cc5..9c90ea5 100755 --- a/tests/fate-run.sh +++ b/tests/fate-run.sh @@ -73,6 +73,12 @@ oneline(){ printf '%s\n' "$1" | diff -u -b - "$2" } +multidiff(){ +while read -r ref_file out_file; do +diff -u -b "${base}/ref/fate/${ref_file}" "${outdir}/${out_file}" || return $? +done <"$1" +} + run(){ test "${V:-0}" -gt 0 && echo "$target_exec" $target_path/"$@" >&3 $target_exec $target_path/"$@" @@ -350,6 +356,7 @@ if test -e "$ref" || test $cmp = "oneline" || test $cmp = "grep" ; then case $cmp in diff) diff -u -b "$ref" "$outfile">$cmpfile ;; rawdiff)diff -u"$ref" "$outfile">$cmpfile ;; +mdiff) multidiff "$ref" >$cmpfile ;; oneoff) oneoff "$ref" "$outfile">$cmpfile ;; stddev) stddev "$ref" "$outfile">$cmpfile ;; oneline)oneline"$ref" "$outfile">$cmpfile ;; diff --git a/tests/fate/tee-muxer.mak b/tests/fate/tee-muxer.mak new file mode 100644 index 000..a76cb18 --- /dev/null +++ b/tests/fate/tee-muxer.mak @@ -0,0 +1,22 @@ +fate-tee-muxer-h264: CMD = ffmpeg -i $(TARGET_SAMPLES)/mkv/1242-small.mkv -vframes 11\ + -c:v copy -c:a copy -map v:0 -map a:0 -flags +bitexact\ + -fflags +bitexact -fflags +bitexact -f tee\ + "[f=framecrc]$(TARGET_PATH)/tests/data/fate/tee-muxer-h264-copy|[f=framecrc:select=1]$(TARGET_PATH)/tests/data/fate/tee-muxer-h264-audio" +fate-tee-muxer-h264: CMP = mdiff +FATE-SAMPLES-TEE-MUXER-$(call ALLYES, TEE_MUXER, MATROSKA_DEMUXER, H264_DECODER) += fate-tee-muxer-h264 + +fate-tee-muxer-ignorefail: CMD = ./ffmpeg -f lavfi -i "testsrc=s=640x480" -f lavfi -i "sine"\ +-t 1 -map 0:v -map 1:a -c:v copy -c:a copy -flags +bitexact -fflags +bitexact -f tee\ + "[f=framecrc]$(TARGET_PATH)/tests/data/fate/tee-muxer-ignorefail|[f=framecrc:onfail=ignore]$(TARGET_PATH)/dev/full" +FATE-TEE-MUXER-$(CONFIG_TEE_MUXER) += fate-tee-muxer-ignorefail + +fate-tee-muxer-tstsrc: CMD = ./ffmpeg -f lavfi -i "testsrc=s=640x480" -f lavfi -i "sine"\ + -t 1 -map 0:v -map 1:a -c:v copy -c:a copy -flags +bitexact -fflags +bitexact -f tee\ + "[f=framecrc]$(TARGET_PATH)/tests/data/fate/tee-muxer-tstsrc-copy|[f=framecrc:select=1]$(TARGET_PATH)/tests/data/fate/tee-muxer-tstsrc-audio" +fate-tee-muxer-tstsrc: CMP = mdiff +FATE-TEE-MUXER-$(CONFIG_TEE_MUXER) += fate-tee-muxer-tstsrc + +FATE_SAMPLES_FFMPEG += $(FATE-SAMPLES-TEE-MUXER-yes) +FATE_FFMPEG += $(FATE-TEE-MUXER-yes) + +fate-tee-muxer: $(FATE-TEE-MUXER-yes) $(FATE-SAMPLES-TEE-MUXER-yes) diff --git a/tests/ref/fate/tee-muxer-h264 b/tests/ref/fate/tee-muxer-h264 new file mode 100644 index 000..2a99a6b --- /dev/null +++ b/tests/ref/fate/tee-muxer-h264 @@ -0,0 +1,2 @@ +tee-muxer-h264-copy tee-muxer-h264-copy +tee-muxer-h264-audio tee-muxer-h264-audio \ No newline at end of file diff --git a/tests/ref/fate/tee-muxer-h264-audio b/tests/ref/fate/tee-muxer-h264-audio new
[FFmpeg-devel] [PATCH v2] libavformat/tee: Add fifo support for tee
From: Jan SebechlebskySigned-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(_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",
[FFmpeg-devel] [PATCH] libavformat/tee: Add fifo support for tee
From: Jan SebechlebskySigned-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(_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
[FFmpeg-devel] [PATCH 1/2] avformat/tee: Copy interrupt callback and flags to slave
From: Jan SebechlebskyCopy interrupt callback to slave format context to allow user to interrupt IO. Copy format flags as well. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavformat/tee.c b/libavformat/tee.c index 518af4a..d59ad4d 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -161,6 +161,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) avf2->opaque = avf->opaque; avf2->io_open = avf->io_open; avf2->io_close = avf->io_close; +avf2->interrupt_callback = avf->interrupt_callback; +avf2->flags = avf->flags; tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map)); if (!tee_slave->stream_map) { -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/tee: Add FATE tests for tee
From: Jan SebechlebskyThis commit also adds new diff option for fate tests allowing do compare multiple tuples of files. Signed-off-by: Jan Sebechlebsky --- tests/Makefile| 1 + tests/fate-run.sh | 7 tests/fate/tee-muxer.mak | 22 ++ tests/ref/fate/tee-muxer-h264 | 2 + tests/ref/fate/tee-muxer-h264-audio | 30 + tests/ref/fate/tee-muxer-h264-copy| 47 + tests/ref/fate/tee-muxer-ignorefail | 79 +++ tests/ref/fate/tee-muxer-tstsrc | 2 + tests/ref/fate/tee-muxer-tstsrc-audio | 49 ++ 9 files changed, 239 insertions(+) create mode 100644 tests/fate/tee-muxer.mak create mode 100644 tests/ref/fate/tee-muxer-h264 create mode 100644 tests/ref/fate/tee-muxer-h264-audio create mode 100644 tests/ref/fate/tee-muxer-h264-copy create mode 100644 tests/ref/fate/tee-muxer-ignorefail create mode 100644 tests/ref/fate/tee-muxer-tstsrc create mode 100644 tests/ref/fate/tee-muxer-tstsrc-audio diff --git a/tests/Makefile b/tests/Makefile index 8e810ff..e23260f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -164,6 +164,7 @@ include $(SRC_PATH)/tests/fate/real.mak include $(SRC_PATH)/tests/fate/screen.mak include $(SRC_PATH)/tests/fate/source.mak include $(SRC_PATH)/tests/fate/subtitles.mak +include $(SRC_PATH)/tests/fate/tee-muxer.mak include $(SRC_PATH)/tests/fate/utvideo.mak include $(SRC_PATH)/tests/fate/video.mak include $(SRC_PATH)/tests/fate/voice.mak diff --git a/tests/fate-run.sh b/tests/fate-run.sh index c640cc5..9c90ea5 100755 --- a/tests/fate-run.sh +++ b/tests/fate-run.sh @@ -73,6 +73,12 @@ oneline(){ printf '%s\n' "$1" | diff -u -b - "$2" } +multidiff(){ +while read -r ref_file out_file; do +diff -u -b "${base}/ref/fate/${ref_file}" "${outdir}/${out_file}" || return $? +done <"$1" +} + run(){ test "${V:-0}" -gt 0 && echo "$target_exec" $target_path/"$@" >&3 $target_exec $target_path/"$@" @@ -350,6 +356,7 @@ if test -e "$ref" || test $cmp = "oneline" || test $cmp = "grep" ; then case $cmp in diff) diff -u -b "$ref" "$outfile">$cmpfile ;; rawdiff)diff -u"$ref" "$outfile">$cmpfile ;; +mdiff) multidiff "$ref" >$cmpfile ;; oneoff) oneoff "$ref" "$outfile">$cmpfile ;; stddev) stddev "$ref" "$outfile">$cmpfile ;; oneline)oneline"$ref" "$outfile">$cmpfile ;; diff --git a/tests/fate/tee-muxer.mak b/tests/fate/tee-muxer.mak new file mode 100644 index 000..b760ea1 --- /dev/null +++ b/tests/fate/tee-muxer.mak @@ -0,0 +1,22 @@ +fate-tee-muxer-h264: CMD = ffmpeg -i $(TARGET_SAMPLES)/mkv/1242-small.mkv -vframes 11\ + -c:v copy -c:a copy -map v:0 -map a:0 -flags +bitexact\ + -fflags +bitexact -fflags +bitexact -f tee\ + "[f=framecrc]$(SRC_PATH)/tests/data/fate/tee-muxer-h264-copy|[f=framecrc:select=1]$(SRC_PATH)/tests/data/fate/tee-muxer-h264-audio" +fate-tee-muxer-h264: CMP = mdiff +FATE-SAMPLES-TEE-MUXER-$(call ALLYES, TEE_MUXER, MATROSKA_DEMUXER, H264_DECODER) += fate-tee-muxer-h264 + +fate-tee-muxer-ignorefail: CMD = ./ffmpeg -f lavfi -i "testsrc=s=640x480" -f lavfi -i "sine"\ +-t 1 -map 0:v -map 1:a -c:v copy -c:a copy -flags +bitexact -fflags +bitexact -f tee\ + "[f=framecrc]$(SRC_PATH)/tests/data/fate/tee-muxer-ignorefail|[f=framecrc:onfail=ignore]$(SRC_PATH)/dev/full" +FATE-TEE-MUXER-$(CONFIG_TEE_MUXER) += fate-tee-muxer-ignorefail + +fate-tee-muxer-tstsrc: CMD = ./ffmpeg -f lavfi -i "testsrc=s=640x480" -f lavfi -i "sine"\ + -t 1 -map 0:v -map 1:a -c:v copy -c:a copy -flags +bitexact -fflags +bitexact -f tee\ + "[f=framecrc]$(SRC_PATH)/tests/data/fate/tee-muxer-tstsrc-copy|[f=framecrc:select=1]$(SRC_PATH)/tests/data/fate/tee-muxer-tstsrc-audio" +fate-tee-muxer-tstsrc: CMP = mdiff +FATE-TEE-MUXER-$(CONFIG_TEE_MUXER) += fate-tee-muxer-tstsrc + +FATE_SAMPLES_FFMPEG += $(FATE-SAMPLES-TEE-MUXER-yes) +FATE_FFMPEG += $(FATE-TEE-MUXER-yes) + +fate-tee-muxer: $(FATE-TEE-MUXER-yes) $(FATE-SAMPLES-TEE-MUXER-yes) diff --git a/tests/ref/fate/tee-muxer-h264 b/tests/ref/fate/tee-muxer-h264 new file mode 100644 index 000..2a99a6b --- /dev/null +++ b/tests/ref/fate/tee-muxer-h264 @@ -0,0 +1,2 @@ +tee-muxer-h264-copy tee-muxer-h264-copy +tee-muxer-h264-audio tee-muxer-h264-audio \ No newline at end of file diff --git a/tests/ref/fate/tee-muxer-h264-audio b/tests/ref/fate/tee-muxer-h264-audio new file mode 100644 index 000..0b42d11 --- /dev/null +++ b/tests/ref/fate/tee-muxer-h264-audio @@ -0,0 +1,30 @@ +#extradata 0:2, 0x00b200a1 +#tb 0: 1/1000 +#media_type 0:
[FFmpeg-devel] [PATCH v5 5/5] avformat/tee: Use BSF list API
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - added check for avcodec_parameters_copy return value - fixed stray space - rewritten cycle receiving packets from bsf so case when av_interleaved_write_frame returns EAGAIN is treated as error. libavformat/tee.c | 137 -- 1 file changed, 70 insertions(+), 67 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 5689ca3..c3c30a6 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -37,7 +37,7 @@ typedef enum { typedef struct { AVFormatContext *avf; -AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +AVBSFContext **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; @@ -64,46 +64,6 @@ static const AVClass tee_muxer_class = { .version= LIBAVUTIL_VERSION_INT, }; -/** - * Parse list of bitstream filters and add them to the list of filters - * pointed to by bsfs. - * - * The list must be specified in the form: - * BSFS ::= BSF[,BSFS] - */ -static int parse_bsfs(void *log_ctx, const char *bsfs_spec, - AVBitStreamFilterContext **bsfs) -{ -char *bsf_name, *buf, *dup, *saveptr; -int ret = 0; - -if (!(dup = buf = av_strdup(bsfs_spec))) -return AVERROR(ENOMEM); - -while (bsf_name = av_strtok(buf, ",", )) { -AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name); - -if (!bsf) { -av_log(log_ctx, AV_LOG_ERROR, - "Cannot initialize bitstream filter with name '%s', " - "unknown filter or internal error happened\n", - bsf_name); -ret = AVERROR_UNKNOWN; -goto end; -} - -/* append bsf context to the list of bsf contexts */ -*bsfs = bsf; -bsfs = >next; - -buf = NULL; -} - -end: -av_free(dup); -return ret; -} - static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) { if (!opt) { @@ -135,14 +95,8 @@ static int close_slave(TeeSlave *tee_slave) ret = av_write_trailer(avf); if (tee_slave->bsfs) { -for (i = 0; i < avf->nb_streams; ++i) { -AVBitStreamFilterContext *bsf_next, *bsf = tee_slave->bsfs[i]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} +for (i = 0; i < avf->nb_streams; ++i) +av_bsf_free(_slave->bsfs[i]); } av_freep(_slave->stream_map); av_freep(_slave->bsfs); @@ -312,7 +266,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) "output '%s', filters will be ignored\n", i, filename); continue; } -ret = parse_bsfs(avf, entry->value, _slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, _slave->bsfs[i]); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s' associated to " @@ -325,6 +279,37 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_dict_set(, entry->key, NULL, 0); } +for (i = 0; i < avf->nb_streams; i++){ +int target_stream = tee_slave->stream_map[i]; +if (target_stream < 0) +continue; + +if (!tee_slave->bsfs[target_stream]) { +/* Add pass-through bitstream filter */ +ret = av_bsf_get_null_filter(_slave->bsfs[target_stream]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Failed to create pass-through bitstream filter: %s\n", + av_err2str(ret)); +goto end; +} +} + +tee_slave->bsfs[target_stream]->time_base_in = avf->streams[i]->time_base; +ret = avcodec_parameters_copy(tee_slave->bsfs[target_stream]->par_in, + avf->streams[i]->codecpar); +if (ret < 0) +goto end; + +ret = av_bsf_init(tee_slave->bsfs[target_stream]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, +"Failed to initialize bitstream filter(s): %s\n", +av_err2str(ret)); +goto end; +} +} + if (options) { entry = NULL; while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) @@ -349,20 +334,16 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) slave->avf->filename, slave->avf->oformat->name); for (i = 0; i < slave->avf->nb_streams; i++) { AVStream *st = slave->avf->streams[i]; -AVBitStreamFilterContext *bsf =
[FFmpeg-devel] [PATCH] libavcodec/bsfs: Fix bsf option setting
From: Jan SebechlebskyAV_OPT_SEARCH_CHILDREN flag must be passed to av_opt_set_dict() to set options for private context. Signed-off-by: Jan Sebechlebsky --- libavcodec/bsf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 2462e62..dfb127e 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -432,7 +432,7 @@ int av_bsf_list_append2(AVBSFList *lst, const char *bsf_name, AVDictionary ** op return ret; if (options) { -ret = av_opt_set_dict(bsf, options); +ret = av_opt_set_dict2(bsf, options, AV_OPT_SEARCH_CHILDREN); if (ret < 0) goto end; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v4 5/5] avformat/tee: Use BSF list API
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- I believe I have fixed handling input / output timebase and input parameters to bitstream filters list. libavformat/tee.c | 131 ++ 1 file changed, 64 insertions(+), 67 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 5689ca3..ba852c3 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -37,7 +37,7 @@ typedef enum { typedef struct { AVFormatContext *avf; -AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +AVBSFContext **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; @@ -64,46 +64,6 @@ static const AVClass tee_muxer_class = { .version= LIBAVUTIL_VERSION_INT, }; -/** - * Parse list of bitstream filters and add them to the list of filters - * pointed to by bsfs. - * - * The list must be specified in the form: - * BSFS ::= BSF[,BSFS] - */ -static int parse_bsfs(void *log_ctx, const char *bsfs_spec, - AVBitStreamFilterContext **bsfs) -{ -char *bsf_name, *buf, *dup, *saveptr; -int ret = 0; - -if (!(dup = buf = av_strdup(bsfs_spec))) -return AVERROR(ENOMEM); - -while (bsf_name = av_strtok(buf, ",", )) { -AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name); - -if (!bsf) { -av_log(log_ctx, AV_LOG_ERROR, - "Cannot initialize bitstream filter with name '%s', " - "unknown filter or internal error happened\n", - bsf_name); -ret = AVERROR_UNKNOWN; -goto end; -} - -/* append bsf context to the list of bsf contexts */ -*bsfs = bsf; -bsfs = >next; - -buf = NULL; -} - -end: -av_free(dup); -return ret; -} - static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) { if (!opt) { @@ -135,14 +95,8 @@ static int close_slave(TeeSlave *tee_slave) ret = av_write_trailer(avf); if (tee_slave->bsfs) { -for (i = 0; i < avf->nb_streams; ++i) { -AVBitStreamFilterContext *bsf_next, *bsf = tee_slave->bsfs[i]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} +for (i = 0; i < avf->nb_streams; ++i) +av_bsf_free(_slave->bsfs[i]); } av_freep(_slave->stream_map); av_freep(_slave->bsfs); @@ -312,7 +266,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) "output '%s', filters will be ignored\n", i, filename); continue; } -ret = parse_bsfs(avf, entry->value, _slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, _slave->bsfs[i]); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s' associated to " @@ -325,6 +279,35 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_dict_set(, entry->key, NULL, 0); } +for (i = 0; i < avf->nb_streams; i++){ +int target_stream = tee_slave->stream_map[i]; +if (target_stream < 0) +continue; + +if (!tee_slave->bsfs[target_stream]) { +/* Add pass-through bitstream filter */ +ret = av_bsf_get_null_filter(_slave->bsfs[target_stream]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Failed to create pass-through bitstream filter: %s\n", + av_err2str(ret)); +goto end; +} +} + +tee_slave->bsfs[target_stream]->time_base_in = avf->streams[i]->time_base; +ret = avcodec_parameters_copy(tee_slave->bsfs[target_stream]->par_in, + avf->streams[i]->codecpar); + +ret = av_bsf_init(tee_slave->bsfs[target_stream]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, +"Failed to initialize bitstream filter(s): %s\n", +av_err2str(ret)); +goto end; +} +} + if (options) { entry = NULL; while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) @@ -349,20 +332,16 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) slave->avf->filename, slave->avf->oformat->name); for (i = 0; i < slave->avf->nb_streams; i++) { AVStream *st = slave->avf->streams[i]; -AVBitStreamFilterContext *bsf = slave->bsfs[i]; +AVBSFContext *bsf = slave->bsfs[i]; +const char * bsf_name; av_log(log_ctx, log_level, "stream:%d codec:%s type:%s", i,
[FFmpeg-devel] [PATCH v7 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan SebechlebskyAdd support for nonblocking calls. Signed-off-by: Jan Sebechlebsky --- No changes since the last version of the patch, just rebased because of changes in previous fifo patch. libavformat/fifo.c | 62 -- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/libavformat/fifo.c b/libavformat/fifo.c index 15435fe..7ab5be6 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -19,12 +19,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/atomic.h" #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/thread.h" #include "libavutil/threadmessage.h" #include "avformat.h" #include "internal.h" +#include "url.h" #define FIFO_DEFAULT_QUEUE_SIZE 60 #define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 0 @@ -76,6 +78,17 @@ typedef struct FifoContext { /* Value > 0 signals queue overflow */ volatile uint8_t overflow_flag; +/* Whether termination was requested by invoking deinit + * before the thread was finished. Used only in non-blocking + * mode - when AVFMT_FLAG_NONBLOCK is set. */ +volatile int termination_requested; + +/* Initially 0, set to 1 immediately before thread function + * returns */ +volatile int thread_finished_flag; + +/* Original interrupt callback of the underlying muxer. */ +AVIOInterruptCB orig_interrupt_callback; } FifoContext; typedef struct FifoThreadContext { @@ -110,6 +123,16 @@ typedef struct FifoMessage { AVPacket pkt; } FifoMessage; +static int fifo_interrupt_callback_wrapper(void *arg) +{ +FifoContext *ctx = arg; + +if (avpriv_atomic_int_get(>termination_requested)) +return 1; + +return ff_check_interrupt(>orig_interrupt_callback); +} + static int fifo_thread_write_header(FifoThreadContext *ctx) { AVFormatContext *avf = ctx->avf; @@ -435,12 +458,17 @@ static void *fifo_consumer_thread(void *data) fifo->write_trailer_ret = fifo_thread_write_trailer(_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(>thread_finished_flag, 1); return NULL; } static int fifo_mux_init(AVFormatContext *avf, AVOutputFormat *oformat) { FifoContext *fifo = avf->priv_data; +AVIOInterruptCB interrupt_cb = {.callback = fifo_interrupt_callback_wrapper, +.opaque = fifo}; AVFormatContext *avf2; int ret = 0, i; @@ -450,7 +478,8 @@ static int fifo_mux_init(AVFormatContext *avf, AVOutputFormat *oformat) fifo->avf = avf2; -avf2->interrupt_callback = avf->interrupt_callback; +fifo->orig_interrupt_callback = avf->interrupt_callback; +avf2->interrupt_callback = interrupt_cb; avf2->max_delay = avf->max_delay; ret = av_dict_copy(>metadata, avf->metadata, 0); if (ret < 0) @@ -538,7 +567,7 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) { FifoContext *fifo = avf->priv_data; FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT}; -int ret; +int ret, queue_flags = 0; if (pkt) { av_init_packet(); @@ -547,15 +576,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, , - fifo->drop_pkts_on_overflow ? - AV_THREAD_MESSAGE_NONBLOCK : 0); +if (fifo->drop_pkts_on_overflow || (avf->flags & AVFMT_FLAG_NONBLOCK)) +queue_flags |= AV_THREAD_MESSAGE_NONBLOCK; + +ret = av_thread_message_queue_send(fifo->queue, , queue_flags); + if (ret == AVERROR(EAGAIN)) { -uint8_t overflow_set = 0; +uint8_t overflow_set; + +if (avf->flags & AVFMT_FLAG_NONBLOCK) +goto fail; /* Queue is full, set fifo->overflow_flag to 1 * to let consumer thread know the queue should * be flushed. */ +overflow_set = 0; pthread_mutex_lock(>overflow_flag_lock); if (!fifo->overflow_flag) fifo->overflow_flag = overflow_set = 1; @@ -583,6 +618,10 @@ static int fifo_write_trailer(AVFormatContext *avf) av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF); +if ((avf->flags & AVFMT_FLAG_NONBLOCK) && +!avpriv_atomic_int_get(>thread_finished_flag)) + return AVERROR(EAGAIN); + ret = pthread_join(fifo->writer_thread, NULL); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", @@ -598,6 +637,17 @@ static void fifo_deinit(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +if (avf->flags & AVFMT_FLAG_NONBLOCK) { +int ret; +avpriv_atomic_int_set(>termination_requested, 1); +
[FFmpeg-devel] [PATCH v12 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - fixed mistakes in docs (missing "is", 2xmisspelled "unsuccessful") - removed braces around return statement in fifo_mux_init() - fixed "return 0;" -> "return ret;" in fifo_write_header() Changelog| 1 + configure| 1 + doc/muxers.texi | 95 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 660 +++ libavformat/version.h| 4 +- 7 files changed, 761 insertions(+), 2 deletions(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index 7e88e64..fdc0d80 100644 --- a/Changelog +++ b/Changelog @@ -16,6 +16,7 @@ version : - crystalizer audio filter - acrusher audio filter - bitplanenoise video filter +- fifo muxer version 3.1: diff --git a/configure b/configure index 9b92426..894e7a9 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..12730a2 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,101 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows the separation of encoding and muxing by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and can be used to +send data to several destinations with different reliability/writing speed/latency. + +API users should be aware that callback functions (interrupt_callback, +io_open and io_close) used within its AVFormatContext must be thread-safe. + +The behavior of the fifo muxer if the queue fills up or if the output fails is +selectable, + +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the input, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option is set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsuccessful recovery attempts after which +the output fails permanently. By default this option is set to 0 (unlimited). + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessful +recovery attempt. Default value is 5 seconds. + +@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least @var{recovery_wait_time} +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain (usually permanent) errors the recovery is not attempted even when +@var{attempt_recovery} is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server, continue processing the stream at real-time +rate even in case of temporary failure
[FFmpeg-devel] [PATCH v11 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Conflicting patch was applied meanwhile, so I am resending this one. No changes since the last version. Changelog| 1 + configure| 1 + doc/muxers.texi | 95 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 661 +++ libavformat/version.h| 4 +- 7 files changed, 762 insertions(+), 2 deletions(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index 7e88e64..fdc0d80 100644 --- a/Changelog +++ b/Changelog @@ -16,6 +16,7 @@ version : - crystalizer audio filter - acrusher audio filter - bitplanenoise video filter +- fifo muxer version 3.1: diff --git a/configure b/configure index 9b92426..894e7a9 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..0f36ddc 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,101 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows the separation of encoding and muxing by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and can be used to +send data to several destinations with different reliability/writing speed/latency. + +API users should be aware that callback functions (interrupt_callback, +io_open and io_close) used within its AVFormatContext must be thread-safe. + +The behavior of the fifo muxer if the queue fills up or if the output fails is +selectable, + +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the input, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. By default this option is set to 0 (unlimited). + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least @var{recovery_wait_time} +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain (usually permanent) errors the recovery is not attempted even when +@var{attempt_recovery} is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server, continue processing the stream at real-time +rate even in case of temporary failure (network outage) and attempt to recover +streaming every second indefinitely. +@example +ffmpeg -re -i ... -c:v libx264 -c:a aac -f fifo
[FFmpeg-devel] [PATCH v2] doc/APIchanges: Document addition of list BSF API in lavc
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- I've noticed that conflicting patch was applied meanwhile, so I'm resending this. Please apply :) doc/APIchanges | 7 +++ 1 file changed, 7 insertions(+) diff --git a/doc/APIchanges b/doc/APIchanges index 74145b2..582563a 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -19,6 +19,13 @@ API changes, most recent first: Add trailing_padding to AVCodecContext to match the corresponding field in AVCodecParameters. +2016-08-15 - b746ed7 - lavc 57.52.100 - avcodec.h + Add a new API for chained BSF filters and passthrough (null) BSF -- + av_bsf_list_alloc(), av_bsf_list_free(), av_bsf_list_append(), + av_bsf_list_append2(), av_bsf_list_finalize(), av_bsf_list_parse_str() + and av_bsf_get_null_filter(). + + 2016-08-04 - xxx - lavf 57.46.100 - avformat.h Add av_get_frame_filename2() -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v10 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - Added note in fifo muxer documentation in muxers.texi regarding thread-safety requirement for callbacks in AVFormatContext Changelog| 1 + configure| 1 + doc/muxers.texi | 95 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 661 +++ libavformat/version.h| 2 +- 7 files changed, 761 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index b903e31..c693d34 100644 --- a/Changelog +++ b/Changelog @@ -15,6 +15,7 @@ version : - True Audio (TTA) muxer - crystalizer audio filter - acrusher audio filter +- fifo muxer version 3.1: diff --git a/configure b/configure index 9b92426..894e7a9 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..0f36ddc 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,101 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows the separation of encoding and muxing by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and can be used to +send data to several destinations with different reliability/writing speed/latency. + +API users should be aware that callback functions (interrupt_callback, +io_open and io_close) used within its AVFormatContext must be thread-safe. + +The behavior of the fifo muxer if the queue fills up or if the output fails is +selectable, + +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the input, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. By default this option is set to 0 (unlimited). + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least @var{recovery_wait_time} +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain (usually permanent) errors the recovery is not attempted even when +@var{attempt_recovery} is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server, continue processing the stream at real-time +rate even in case of temporary failure (network outage) and attempt to recover +streaming every second
[FFmpeg-devel] [PATCH] avformat: Document thread-safety requirement for interrupt callback
From: Jan SebechlebskyMuxing might be running in a separate thread if actual muxer is run inside of fifo pseudo-muxer. Callback should be therefore thread-safe. Signed-off-by: Jan Sebechlebsky --- libavformat/avformat.h | 2 +- libavformat/avio.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 79c2511..8550dca 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1564,7 +1564,7 @@ typedef struct AVFormatContext { * muxing: set by the user before avformat_write_header() * (mainly useful for AVFMT_NOFILE formats). The callback * should also be passed to avio_open2() if it's used to - * open the file. + * open the file. The callback must be thread-safe. */ AVIOInterruptCB interrupt_callback; diff --git a/libavformat/avio.h b/libavformat/avio.h index b1ce1d1..b5d1396 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -43,6 +43,8 @@ * opaque as parameter. If the callback returns 1, the * blocking operation will be aborted. * + * If the callback is used in muxing context, it has to be thread-safe. + * * No members can be added to this struct without a major bump, if * new elements have been added after this struct in AVFormatContext * or AVIOContext. -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v6 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan SebechlebskyAdd support for nonblocking calls. Signed-off-by: Jan Sebechlebsky --- No chanes since the last version of patch, rebased because of changes in the patch adding fifo. libavformat/fifo.c | 62 -- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/libavformat/fifo.c b/libavformat/fifo.c index 859cbb4..689581f 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -19,12 +19,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/atomic.h" #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/thread.h" #include "libavutil/threadmessage.h" #include "avformat.h" #include "internal.h" +#include "url.h" #define FIFO_DEFAULT_QUEUE_SIZE 60 #define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 0 @@ -76,6 +78,17 @@ typedef struct FifoContext { /* Value > 0 signals queue overflow */ volatile uint8_t overflow_flag; +/* Whether termination was requested by invoking deinit + * before the thread was finished. Used only in non-blocking + * mode - when AVFMT_FLAG_NONBLOCK is set. */ +volatile int termination_requested; + +/* Initially 0, set to 1 immediately before thread function + * returns */ +volatile int thread_finished_flag; + +/* Original interrupt callback of the underlying muxer. */ +AVIOInterruptCB orig_interrupt_callback; } FifoContext; typedef struct FifoThreadContext { @@ -110,6 +123,16 @@ typedef struct FifoMessage { AVPacket pkt; } FifoMessage; +static int fifo_interrupt_callback_wrapper(void *arg) +{ +FifoContext *ctx = arg; + +if (avpriv_atomic_int_get(>termination_requested)) +return 1; + +return ff_check_interrupt(>orig_interrupt_callback); +} + static int fifo_thread_write_header(FifoThreadContext *ctx) { AVFormatContext *avf = ctx->avf; @@ -435,12 +458,17 @@ static void *fifo_consumer_thread(void *data) fifo->write_trailer_ret = fifo_thread_write_trailer(_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(>thread_finished_flag, 1); return NULL; } static int fifo_mux_init(AVFormatContext *avf, AVOutputFormat *oformat) { FifoContext *fifo = avf->priv_data; +AVIOInterruptCB interrupt_cb = {.callback = fifo_interrupt_callback_wrapper, +.opaque = fifo}; AVFormatContext *avf2; int ret = 0, i; @@ -451,7 +479,8 @@ static int fifo_mux_init(AVFormatContext *avf, AVOutputFormat *oformat) fifo->avf = avf2; -avf2->interrupt_callback = avf->interrupt_callback; +fifo->orig_interrupt_callback = avf->interrupt_callback; +avf2->interrupt_callback = interrupt_cb; avf2->max_delay = avf->max_delay; ret = av_dict_copy(>metadata, avf->metadata, 0); if (ret < 0) @@ -539,7 +568,7 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) { FifoContext *fifo = avf->priv_data; FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT}; -int ret; +int ret, queue_flags = 0; if (pkt) { av_init_packet(); @@ -548,15 +577,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, , - fifo->drop_pkts_on_overflow ? - AV_THREAD_MESSAGE_NONBLOCK : 0); +if (fifo->drop_pkts_on_overflow || (avf->flags & AVFMT_FLAG_NONBLOCK)) +queue_flags |= AV_THREAD_MESSAGE_NONBLOCK; + +ret = av_thread_message_queue_send(fifo->queue, , queue_flags); + if (ret == AVERROR(EAGAIN)) { -uint8_t overflow_set = 0; +uint8_t overflow_set; + +if (avf->flags & AVFMT_FLAG_NONBLOCK) +goto fail; /* Queue is full, set fifo->overflow_flag to 1 * to let consumer thread know the queue should * be flushed. */ +overflow_set = 0; pthread_mutex_lock(>overflow_flag_lock); if (!fifo->overflow_flag) fifo->overflow_flag = overflow_set = 1; @@ -584,6 +619,10 @@ static int fifo_write_trailer(AVFormatContext *avf) av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF); +if ((avf->flags & AVFMT_FLAG_NONBLOCK) && +!avpriv_atomic_int_get(>thread_finished_flag)) + return AVERROR(EAGAIN); + ret = pthread_join(fifo->writer_thread, NULL); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", @@ -599,6 +638,17 @@ static void fifo_deinit(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +if (avf->flags & AVFMT_FLAG_NONBLOCK) { +int ret; +avpriv_atomic_int_set(>termination_requested, 1); +ret =
[FFmpeg-devel] [PATCH v9 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - fixed "s@item" in muxers.texi - fixed second -> seconds in FIFO_DEFAULT_RECOVERY_WAIT_TIME_USEC comment - removed AVFormat *oformat from FifoContext (it is local variable now) - if recovery uses stream time to wait, last_recovery_ts is set to AV_NOPTS_VALUE, and when recovery is attempted and last_recovery_ts recovery is performed immediately - fixed stray space in pthread_join in fifo_write_trailer - fixed superfluous tests and av_thread_message_flush() in fifo_deinit - changed upper bound for queue_size option to INT_MAX Changelog| 1 + configure| 1 + doc/muxers.texi | 92 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 661 +++ libavformat/version.h| 2 +- 7 files changed, 758 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index b903e31..c693d34 100644 --- a/Changelog +++ b/Changelog @@ -15,6 +15,7 @@ version : - True Audio (TTA) muxer - crystalizer audio filter - acrusher audio filter +- fifo muxer version 3.1: diff --git a/configure b/configure index 9b92426..894e7a9 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..617c9a3 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,98 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows the separation of encoding and muxing by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and can be used to +send data to several destinations with different reliability/writing speed/latency. + +The behavior of the fifo muxer if the queue fills up or if the output fails is +selectable, + +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the input, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. By default this option is set to 0 (unlimited). + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least @var{recovery_wait_time} +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain (usually permanent) errors the recovery is not attempted even when +@var{attempt_recovery} is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0
[FFmpeg-devel] [PATCH v3 4/5] avcodec/bsf: Add custom item name func for BSF list
From: Jan SebechlebskyAdd custom item name function for bsf list, which will construct string description of filter chain. This is done using lazy-initialization, so there is no overhead if item name is never accessed. Signed-off-by: Jan Sebechlebsky Conflicts: libavcodec/bsf.c --- Changes since the last version of the patch: - added av_freep(>item_name) from previous commit to bsf_list_close libavcodec/bsf.c | 29 - 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 664565f..2462e62 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -248,6 +248,7 @@ typedef struct BSFListContext { unsigned idx; // index of currently processed BSF unsigned flushed_idx; // index of BSF being flushed +char * item_name; } BSFListContext; @@ -345,11 +346,37 @@ static void bsf_list_close(AVBSFContext *bsf) for (i = 0; i < lst->nb_bsfs; ++i) av_bsf_free(>bsfs[i]); av_freep(>bsfs); +av_freep(>item_name); +} + +static const char *bsf_list_item_name(void *ctx) +{ +static const char *null_filter_name = "null"; +AVBSFContext *bsf_ctx = ctx; +BSFListContext *lst = bsf_ctx->priv_data; + +if (!lst->nb_bsfs) +return null_filter_name; + +if (!lst->item_name) { +int i; +AVBPrint bp; +av_bprint_init(, 16, 128); + +av_bprintf(, "bsf_list("); +for (i = 0; i < lst->nb_bsfs; i++) +av_bprintf(, i ? ",%s" : "%s", lst->bsfs[i]->filter->name); +av_bprintf(, ")"); + +av_bprint_finalize(, >item_name); +} + +return lst->item_name; } static const AVClass bsf_list_class = { .class_name = "bsf_list", -.item_name = av_default_item_name, +.item_name = bsf_list_item_name, .version= LIBAVUTIL_VERSION_INT, }; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v5 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan SebechlebskyAdd support for nonblocking calls. Signed-off-by: Jan Sebechlebsky --- No changes since the last version of the patch, just rebased because of changes in previous patch. libavformat/fifo.c | 61 -- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/libavformat/fifo.c b/libavformat/fifo.c index 3748fa9..ef0fda4 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -19,12 +19,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/atomic.h" #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/thread.h" #include "libavutil/threadmessage.h" #include "avformat.h" #include "internal.h" +#include "url.h" #define FIFO_DEFAULT_QUEUE_SIZE 60 #define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 0 @@ -77,6 +79,17 @@ typedef struct FifoContext { /* Value > 0 signals queue overflow */ volatile uint8_t overflow_flag; +/* Whether termination was requested by invoking deinit + * before the thread was finished. Used only in non-blocking + * mode - when AVFMT_FLAG_NONBLOCK is set. */ +volatile int termination_requested; + +/* Initially 0, set to 1 immediately before thread function + * returns */ +volatile int thread_finished_flag; + +/* Original interrupt callback of the underlying muxer. */ +AVIOInterruptCB orig_interrupt_callback; } FifoContext; typedef struct FifoThreadContext { @@ -111,6 +124,16 @@ typedef struct FifoMessage { AVPacket pkt; } FifoMessage; +static int fifo_interrupt_callback_wrapper(void *arg) +{ +FifoContext *ctx = arg; + +if (avpriv_atomic_int_get(>termination_requested)) +return 1; + +return ff_check_interrupt(>orig_interrupt_callback); +} + static int fifo_thread_write_header(FifoThreadContext *ctx) { AVFormatContext *avf = ctx->avf; @@ -430,12 +453,17 @@ static void *fifo_consumer_thread(void *data) fifo->write_trailer_ret = fifo_thread_write_trailer(_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(>thread_finished_flag, 1); return NULL; } static int fifo_mux_init(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +AVIOInterruptCB interrupt_cb = {.callback = fifo_interrupt_callback_wrapper, +.opaque = fifo}; AVFormatContext *avf2; int ret = 0, i; @@ -446,7 +474,8 @@ static int fifo_mux_init(AVFormatContext *avf) fifo->avf = avf2; -avf2->interrupt_callback = avf->interrupt_callback; +fifo->orig_interrupt_callback = avf->interrupt_callback; +avf2->interrupt_callback = interrupt_cb; avf2->max_delay = avf->max_delay; ret = av_dict_copy(>metadata, avf->metadata, 0); if (ret < 0) @@ -533,7 +562,7 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) { FifoContext *fifo = avf->priv_data; FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT}; -int ret; +int ret, queue_flags = 0; if (pkt) { av_init_packet(); @@ -542,15 +571,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, , - fifo->drop_pkts_on_overflow ? - AV_THREAD_MESSAGE_NONBLOCK : 0); +if (fifo->drop_pkts_on_overflow || (avf->flags & AVFMT_FLAG_NONBLOCK)) +queue_flags |= AV_THREAD_MESSAGE_NONBLOCK; + +ret = av_thread_message_queue_send(fifo->queue, , queue_flags); + if (ret == AVERROR(EAGAIN)) { -uint8_t overflow_set = 0; +uint8_t overflow_set; + +if (avf->flags & AVFMT_FLAG_NONBLOCK) +goto fail; /* Queue is full, set fifo->overflow_flag to 1 * to let consumer thread know the queue should * be flushed. */ +overflow_set = 0; pthread_mutex_lock(>overflow_flag_lock); if (!fifo->overflow_flag) fifo->overflow_flag = overflow_set = 1; @@ -578,6 +613,10 @@ static int fifo_write_trailer(AVFormatContext *avf) av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF); +if ((avf->flags & AVFMT_FLAG_NONBLOCK) && +!avpriv_atomic_int_get(>thread_finished_flag)) + return AVERROR(EAGAIN); + ret = pthread_join( fifo->writer_thread, NULL); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", @@ -593,6 +632,16 @@ static void fifo_deinit(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +if (avf->flags & AVFMT_FLAG_NONBLOCK) { +int ret; +avpriv_atomic_int_set(>termination_requested, 1); +ret = pthread_join( fifo->writer_thread, NULL); +
[FFmpeg-devel] [PATCH v8 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - Fixed documentation (apart from the Marton's suggestions I've also changed example, since it used block_on_overflow option from the earlier version of patch) - Changed max_recovery_attempts default value to 0 (unlimited) - Removed unnecessary check for null ptr in free_message - Changed log level when loggin recovery attempt to AV_LOG_VERBOSE Changelog| 1 + configure| 1 + doc/muxers.texi | 92 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 662 +++ libavformat/version.h| 2 +- 7 files changed, 759 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index b903e31..c693d34 100644 --- a/Changelog +++ b/Changelog @@ -15,6 +15,7 @@ version : - True Audio (TTA) muxer - crystalizer audio filter - acrusher audio filter +- fifo muxer version 3.1: diff --git a/configure b/configure index bff8159..a252354 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..b4c3886 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,98 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows the separation of encoding and muxing by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and can be used to +send data to several destinations with different reliability/writing speed/latency. + +The behavior of the fifo muxer if the queue fills up or if the output fails is +selectable, + +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the input, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. By default this option is set to 0 (unlimited). + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +s@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least @var{recovery_wait_time} +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain (usually permanent) errors the recovery is not attempted even when +@var{attempt_recovery} is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server, continue processing the stream at real-time +rate even in case of temporary
[FFmpeg-devel] [PATCH v5 03/11] avformat/fifo: Add fate test
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - fixed print_deinit_summary to use int instead of uint8_t libavformat/Makefile | 1 + libavformat/tests/fifo_muxer.c | 443 + tests/Makefile | 1 + tests/fate/fifo-muxer.mak | 20 ++ tests/ref/fate/fifo-muxer-tst | 11 + 5 files changed, 476 insertions(+) create mode 100644 libavformat/tests/fifo_muxer.c create mode 100644 tests/fate/fifo-muxer.mak create mode 100644 tests/ref/fate/fifo-muxer-tst diff --git a/libavformat/Makefile b/libavformat/Makefile index 2d2b78c..5d827d31 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -591,6 +591,7 @@ TESTPROGS = seek \ url \ # async \ +TESTPROGS-$(CONFIG_FIFO_MUXER) += fifo_muxer TESTPROGS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh TESTPROGS-$(CONFIG_MOV_MUXER)+= movenc TESTPROGS-$(CONFIG_NETWORK) += noproxy diff --git a/libavformat/tests/fifo_muxer.c b/libavformat/tests/fifo_muxer.c new file mode 100644 index 000..9659198 --- /dev/null +++ b/libavformat/tests/fifo_muxer.c @@ -0,0 +1,443 @@ +/* + * FIFO pseudo-muxer + * Copyright (c) 2016 Jan Sebechlebsky + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpeg; if not, write to the Free Software * Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "libavutil/avassert.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" + +#define MAX_TST_PACKETS 128 +#define SLEEPTIME_50_MS 5 +#define SLEEPTIME_10_MS 1 + +/* Implementation of mock muxer to simulate real muxer failures */ + +/* This is structure of data sent in packets to + * failing muxer */ +typedef struct FailingMuxerPacketData { +int ret; /* return value of write_packet call*/ +int recover_after; /* set ret to zero after this number of recovery attempts */ +unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */ +} FailingMuxerPacketData; + + +typedef struct FailingMuxerContext { +AVClass *class; +int write_header_ret; +int write_trailer_ret; +/* If non-zero, summary of processed packets will be printed in deinit */ +int print_deinit_summary; + +int flush_count; +int pts_written[MAX_TST_PACKETS]; +int pts_written_nr; +} FailingMuxerContext; + +static int failing_write_header(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_header_ret; +} + +static int failing_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ +FailingMuxerContext *ctx = avf->priv_data; +int ret = 0; +if (!pkt) { +ctx->flush_count++; +} else { +FailingMuxerPacketData *data = (FailingMuxerPacketData*) pkt->data; + +if (!data->recover_after) { +data->ret = 0; +} else { +data->recover_after--; +} + +ret = data->ret; + +if (data->sleep_time) { +int64_t slept = 0; +while (slept < data->sleep_time) { +if (ff_check_interrupt(>interrupt_callback)) +return AVERROR_EXIT; +av_usleep(SLEEPTIME_10_MS); +slept += SLEEPTIME_10_MS; +} +} + +if (!ret) { +ctx->pts_written[ctx->pts_written_nr++] = pkt->pts; +av_packet_unref(pkt); +} +} +return ret; +} + +static int failing_write_trailer(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_trailer_ret; +} + +static void failing_deinit(AVFormatContext *avf) +{ +int i; +FailingMuxerContext *ctx = avf->priv_data; + +if (!ctx->print_deinit_summary) +return; + +printf("flush count: %d\n", ctx->flush_count); +printf("pts seen nr: %d\n", ctx->pts_written_nr); +printf("pts seen: "); +for (i = 0; i < ctx->pts_written_nr; ++i ) { +printf(i ? ",%d" : "%d", ctx->pts_written[i]); +} +
[FFmpeg-devel] [PATCH v4 03/11] avformat/fifo: Add fate test
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of patch: - Fixed make dependencies so the tests are not executed when required components are disabled libavformat/Makefile | 1 + libavformat/tests/fifo_muxer.c | 443 + tests/Makefile | 1 + tests/fate/fifo-muxer.mak | 20 ++ tests/ref/fate/fifo-muxer-tst | 11 + 5 files changed, 476 insertions(+) create mode 100644 libavformat/tests/fifo_muxer.c create mode 100644 tests/fate/fifo-muxer.mak create mode 100644 tests/ref/fate/fifo-muxer-tst diff --git a/libavformat/Makefile b/libavformat/Makefile index 2d2b78c..5d827d31 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -591,6 +591,7 @@ TESTPROGS = seek \ url \ # async \ +TESTPROGS-$(CONFIG_FIFO_MUXER) += fifo_muxer TESTPROGS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh TESTPROGS-$(CONFIG_MOV_MUXER)+= movenc TESTPROGS-$(CONFIG_NETWORK) += noproxy diff --git a/libavformat/tests/fifo_muxer.c b/libavformat/tests/fifo_muxer.c new file mode 100644 index 000..d6ce85d --- /dev/null +++ b/libavformat/tests/fifo_muxer.c @@ -0,0 +1,443 @@ +/* + * FIFO pseudo-muxer + * Copyright (c) 2016 Jan Sebechlebsky + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpeg; if not, write to the Free Software * Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "libavutil/avassert.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" + +#define MAX_TST_PACKETS 128 +#define SLEEPTIME_50_MS 5 +#define SLEEPTIME_10_MS 1 + +/* Implementation of mock muxer to simulate real muxer failures */ + +/* This is structure of data sent in packets to + * failing muxer */ +typedef struct FailingMuxerPacketData { +int ret; /* return value of write_packet call*/ +int recover_after; /* set ret to zero after this number of recovery attempts */ +unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */ +} FailingMuxerPacketData; + + +typedef struct FailingMuxerContext { +AVClass *class; +int write_header_ret; +int write_trailer_ret; +/* If non-zero, summary of processed packets will be printed in deinit */ +uint8_t print_deinit_summary; + +int flush_count; +int pts_written[MAX_TST_PACKETS]; +int pts_written_nr; +} FailingMuxerContext; + +static int failing_write_header(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_header_ret; +} + +static int failing_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ +FailingMuxerContext *ctx = avf->priv_data; +int ret = 0; +if (!pkt) { +ctx->flush_count++; +} else { +FailingMuxerPacketData *data = (FailingMuxerPacketData*) pkt->data; + +if (!data->recover_after) { +data->ret = 0; +} else { +data->recover_after--; +} + +ret = data->ret; + +if (data->sleep_time) { +int64_t slept = 0; +while (slept < data->sleep_time) { +if (ff_check_interrupt(>interrupt_callback)) +return AVERROR_EXIT; +av_usleep(SLEEPTIME_10_MS); +slept += SLEEPTIME_10_MS; +} +} + +if (!ret) { +ctx->pts_written[ctx->pts_written_nr++] = pkt->pts; +av_packet_unref(pkt); +} +} +return ret; +} + +static int failing_write_trailer(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_trailer_ret; +} + +static void failing_deinit(AVFormatContext *avf) +{ +int i; +FailingMuxerContext *ctx = avf->priv_data; + +if (!ctx->print_deinit_summary) +return; + +printf("flush count: %d\n", ctx->flush_count); +printf("pts seen nr: %d\n", ctx->pts_written_nr); +printf("pts seen: "); +for (i = 0; i < ctx->pts_written_nr; ++i ) { +printf(i ? ",%d"
[FFmpeg-devel] [PATCH v3 11/11] avformat/fifo: Add test for nonblocking mode
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- No changes since the last version of the patch, just rebased because of changes in previous fate test patch. libavformat/tests/fifo_muxer.c | 139 + tests/ref/fate/fifo-muxer-tst | 5 ++ 2 files changed, 144 insertions(+) diff --git a/libavformat/tests/fifo_muxer.c b/libavformat/tests/fifo_muxer.c index d6ce85d..deb2656 100644 --- a/libavformat/tests/fifo_muxer.c +++ b/libavformat/tests/fifo_muxer.c @@ -336,6 +336,137 @@ fail: return ret; } +static int retry_write_frame(AVFormatContext *oc, AVPacket *pkt, int max_retries) +{ +int ret = 0, retry_count = 0; +do { +ret = av_write_frame(oc, pkt); +if (ret == AVERROR(EAGAIN)) { +av_usleep(SLEEPTIME_10_MS); +retry_count++; +} +} while ( ret == AVERROR(EAGAIN) && retry_count < max_retries); +return ret; +} + +static int retry_write_trailer(AVFormatContext *oc, int max_retries) +{ +int ret = 0, retry_count = 0; +do { +ret = av_write_trailer(oc); +if (ret == AVERROR(EAGAIN)) { +av_usleep(SLEEPTIME_10_MS); +retry_count++; +} +} while (ret == AVERROR(EAGAIN) && retry_count < max_retries); +return ret; +} + +static int fifo_nonblock_test(AVFormatContext *oc, AVDictionary **opts, + const FailingMuxerPacketData *pkt_data) +{ +int ret = 0, i; +AVPacket pkt; + +av_init_packet(); + +oc->flags |= AVFMT_FLAG_NONBLOCK; + +ret = avformat_write_header(oc, opts); +if (ret) { +fprintf(stderr, "Unexpected write_header failure: %s\n", +av_err2str(ret)); +return ret; +} + +for (i = 0; i < 16; i++ ) { +ret = prepare_packet(, pkt_data, i); +if (ret < 0) { +fprintf(stderr, "Failed to prepare test packet: %s\n", +av_err2str(ret)); +goto fail; +} +ret = retry_write_frame(oc, , 100); +av_packet_unref(); +if (ret < 0) +break; +} + +if (ret) { +fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret)); +goto fail; +} + +ret = retry_write_trailer(oc, 100); +if (ret == AVERROR(EAGAIN)) { +fprintf(stderr, "write_trailer() operation timeout\n"); +goto fail; +} else if (ret < 0) +fprintf(stderr, "Unexpected write_trailer error: %s\n", av_err2str(ret)); + +return ret; +fail: +avformat_write_abort(oc); +return ret; +} + +static int fifo_nonblock_abort_test(AVFormatContext *oc, AVDictionary **opts, +const FailingMuxerPacketData *pkt_data) +{ +int ret = 0, i; +AVPacket pkt; +int64_t start_time, end_time, duration; + +av_init_packet(); + +oc->flags |= AVFMT_FLAG_NONBLOCK; + +ret = avformat_write_header(oc, opts); +if (ret) { +fprintf(stderr, "Unexpected write header failure: %s\n", +av_err2str(ret)); +goto fail; +} + +av_assert0(pkt_data->sleep_time > 0); + +start_time = av_gettime_relative(); +for (i = 0; i < 16; i++ ) { +ret = prepare_packet(, pkt_data, i); +if (ret < 0) { +fprintf(stderr, "Failed to prepare test packet: %s\n", +av_err2str(ret)); +goto fail; +} +ret = retry_write_frame(oc, , 100); +av_packet_unref(); +if (ret < 0) +break; +} + +if (ret) { +fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret)); +goto fail; +} + +avformat_write_abort(oc); + +end_time = av_gettime_relative(); +duration = end_time - start_time; + +if (duration > (16*pkt_data->sleep_time)/2 ) { +fprintf(stderr, "Aborting output took too much time: %u us," +" expected time if not aborted %u us\n", +(unsigned) duration, 16*pkt_data->sleep_time); +ret = AVERROR(ETIMEDOUT); +} + +return ret; +fail: +avformat_write_abort(oc); +return ret; +} + typedef struct TestCase { int (*test_func)(AVFormatContext *, AVDictionary **,const FailingMuxerPacketData *pkt_data); const char *test_name; @@ -423,6 +554,14 @@ const TestCase tests[] = { {fifo_overflow_drop_test, "overflow with packet dropping", "queue_size=3:drop_pkts_on_overflow=1", 0, 0, 0, {0, 0, SLEEPTIME_50_MS}}, +/* Simple test of nonblocking mode, the consumer should receive all the packets. */ +{fifo_nonblock_test, "nonblocking mode test", "queue_size=3", + 1, 0, 0, {0, 0, SLEEPTIME_10_MS}}, + +/* Test of terminating fifo muxer with av_abort_format() */ +{fifo_nonblock_abort_test, "abort in nonblocking mode", "queue_size=16", + 0, 0, 0, {0, 0, SLEEPTIME_50_MS}}, +
[FFmpeg-devel] [PATCH v4 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan SebechlebskyAdd support for nonblocking calls. Signed-off-by: Jan Sebechlebsky --- Changes since the last version: - fixed wrong flag passed to av_thread_message_queue_recv() - fixed memleak when queue is full in nonblocking mode libavformat/fifo.c | 61 -- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/libavformat/fifo.c b/libavformat/fifo.c index 8896b9e..cdf9f49 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -19,12 +19,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/atomic.h" #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/thread.h" #include "libavutil/threadmessage.h" #include "avformat.h" #include "internal.h" +#include "url.h" #define FIFO_DEFAULT_QUEUE_SIZE 60 #define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 16 @@ -77,6 +79,17 @@ typedef struct FifoContext { /* Value > 0 signals queue overflow */ volatile uint8_t overflow_flag; +/* Whether termination was requested by invoking deinit + * before the thread was finished. Used only in non-blocking + * mode - when AVFMT_FLAG_NONBLOCK is set. */ +volatile int termination_requested; + +/* Initially 0, set to 1 immediately before thread function + * returns */ +volatile int thread_finished_flag; + +/* Original interrupt callback of the underlying muxer. */ +AVIOInterruptCB orig_interrupt_callback; } FifoContext; typedef struct FifoThreadContext { @@ -111,6 +124,16 @@ typedef struct FifoMessage { AVPacket pkt; } FifoMessage; +static int fifo_interrupt_callback_wrapper(void *arg) +{ +FifoContext *ctx = arg; + +if (avpriv_atomic_int_get(>termination_requested)) +return 1; + +return ff_check_interrupt(>orig_interrupt_callback); +} + static int fifo_thread_write_header(FifoThreadContext *ctx) { AVFormatContext *avf = ctx->avf; @@ -433,12 +456,17 @@ static void *fifo_consumer_thread(void *data) fifo->write_trailer_ret = fifo_thread_write_trailer(_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(>thread_finished_flag, 1); return NULL; } static int fifo_mux_init(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +AVIOInterruptCB interrupt_cb = {.callback = fifo_interrupt_callback_wrapper, +.opaque = fifo}; AVFormatContext *avf2; int ret = 0, i; @@ -449,7 +477,8 @@ static int fifo_mux_init(AVFormatContext *avf) fifo->avf = avf2; -avf2->interrupt_callback = avf->interrupt_callback; +fifo->orig_interrupt_callback = avf->interrupt_callback; +avf2->interrupt_callback = interrupt_cb; avf2->max_delay = avf->max_delay; ret = av_dict_copy(>metadata, avf->metadata, 0); if (ret < 0) @@ -536,7 +565,7 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) { FifoContext *fifo = avf->priv_data; FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT}; -int ret; +int ret, queue_flags = 0; if (pkt) { av_init_packet(); @@ -545,15 +574,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, , - fifo->drop_pkts_on_overflow ? - AV_THREAD_MESSAGE_NONBLOCK : 0); +if (fifo->drop_pkts_on_overflow || (avf->flags & AVFMT_FLAG_NONBLOCK)) +queue_flags |= AV_THREAD_MESSAGE_NONBLOCK; + +ret = av_thread_message_queue_send(fifo->queue, , queue_flags); + if (ret == AVERROR(EAGAIN)) { -uint8_t overflow_set = 0; +uint8_t overflow_set; + +if (avf->flags & AVFMT_FLAG_NONBLOCK) +goto fail; /* Queue is full, set fifo->overflow_flag to 1 * to let consumer thread know the queue should * be flushed. */ +overflow_set = 0; pthread_mutex_lock(>overflow_flag_lock); if (!fifo->overflow_flag) fifo->overflow_flag = overflow_set = 1; @@ -581,6 +616,10 @@ static int fifo_write_trailer(AVFormatContext *avf) av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF); +if ((avf->flags & AVFMT_FLAG_NONBLOCK) && +!avpriv_atomic_int_get(>thread_finished_flag)) + return AVERROR(EAGAIN); + ret = pthread_join( fifo->writer_thread, NULL); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", @@ -596,6 +635,16 @@ static void fifo_deinit(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +if (avf->flags & AVFMT_FLAG_NONBLOCK) { +int ret; +avpriv_atomic_int_set(>termination_requested, 1); +ret =
[FFmpeg-devel] [PATCH v3 07/11] avformat/mux: Comment and assert AVFMT_FLAG_NONBLOCK
From: Jan SebechlebskyAdd comments regarding AVFMG_FLAG_NONBLOCK usage with muxers. Add assert forbiding use of nonblocking muxer with av_interleaved_write_frame. Signed-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - added assert to the beginning of av_interleaved_write_frame ensuring it is not called with muxer in nonblocking mode. - Changed commit description. libavformat/avformat.h | 9 - libavformat/mux.c | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 83903b5..79c2511 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1428,7 +1428,7 @@ typedef struct AVFormatContext { int flags; #define AVFMT_FLAG_GENPTS 0x0001 ///< Generate missing pts even if it requires parsing future frames. #define AVFMT_FLAG_IGNIDX 0x0002 ///< Ignore index. -#define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input. +#define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input / writing packets to output. #define AVFMT_FLAG_IGNDTS 0x0008 ///< Ignore DTS on frames that contain both DTS & PTS #define AVFMT_FLAG_NOFILLIN 0x0010 ///< Do not infer any values from other values, just return what is stored in the container #define AVFMT_FLAG_NOPARSE 0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled @@ -2391,6 +2391,10 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options); * the interleaving should call av_interleaved_write_frame() instead of this * function. * + * In case the muxer is operating in non-blocking mode (AVFMT_FLAG_NONBLOCK + * is set), this function can return AVERROR(EAGAIN) meaning the call should + * be repeated. + * * @param s media file handle * @param pkt The packet containing the data to be written. Note that unlike *av_interleaved_write_frame(), this function does not take @@ -2433,6 +2437,9 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt); * knowledge of future packets, improving e.g. the behaviour of the mp4 * muxer for VFR content in fragmenting mode. * + * This call is not supported and must not be called if muxer is operating + * in non-blocking mode (AVFMT_FLAG_NONBLOCK is set). + * * @param s media file handle * @param pkt The packet containing the data to be written. * diff --git a/libavformat/mux.c b/libavformat/mux.c index 0f002a3..b4698b7 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1139,6 +1139,8 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) { int ret, flush = 0; +av_assert0(!(s->flags & AVFMT_FLAG_NONBLOCK)); + ret = prepare_input_packet(s, pkt); if (ret < 0) goto fail; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 06/11] avformat: add avformat_write_abort() function
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- No changes since the last version of the patch, just rebased because of the changes in previous patches. libavformat/avformat.h | 15 +++ libavformat/mux.c | 13 + 2 files changed, 28 insertions(+) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 2cc3156..83903b5 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -2512,6 +2512,8 @@ int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); * * If AVFMT_FLAG_NONBLOCK is set, this call may return AVERROR(EAGAIN) * meaning the operation is pending and the call should be repeated. + * If caller decides to abort operation (after too many calls have returned + * AVERROR(EAGAIN)), it can be done by calling @ref avformat_write_abort(). * * @param s media file handle * @return 0 if OK, AVERROR(EAGAIN) in case call should be repeated, @@ -2520,6 +2522,19 @@ int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); int av_write_trailer(AVFormatContext *s); /** + * Abort muxer operation and free private data. + * For muxer operating in blocking mode, this is equivalent to calling + * av_write_trailer. For muxer operating in non-blocking mode, this will + * call deinitialize routine even if there is operation pending + * and @ref av_write_trailer() keeps returning AVERROR(EAGAIN). + * May only be called after a successful call to avformat_write_header. + * + * @param s Media file handle + * return >= 0 on success, negative AVERROR on error + */ +int avformat_write_abort(AVFormatContext *s); + +/** * Return the output format in the list of registered output formats * which best matches the provided parameters, or return NULL if * there is no match. diff --git a/libavformat/mux.c b/libavformat/mux.c index 45f1401..0f002a3 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1270,6 +1270,19 @@ fail: return ret; } +int avformat_write_abort(AVFormatContext *s) +{ +int ret; + +ret = av_write_trailer(s); +if (ret == AVERROR(EAGAIN)) { +deinit_muxer(s); +ret = 0; +} + +return ret; +} + int av_get_output_timestamp(struct AVFormatContext *s, int stream, int64_t *dts, int64_t *wall) { -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 05/11] avformat/mux: Refactor muxer deinit from av_write_trailer
From: Jan SebechlebskyMove muxer deinitialization and private resources freeing in a separate static function free_muxer(AVFormatContext*). Signed-off-by: Jan Sebechlebsky --- No changes since the last version, just rebased because of changes in previous patch. libavformat/mux.c | 31 --- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/libavformat/mux.c b/libavformat/mux.c index 3ae924c..45f1401 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1197,9 +1197,27 @@ fail: return ret; } +static void deinit_muxer(AVFormatContext *s) +{ +int i; + +if (s->oformat->deinit) +s->oformat->deinit(s); + +for (i = 0; i < s->nb_streams; i++) { +av_freep(>streams[i]->priv_data); +av_freep(>streams[i]->index_entries); +} + +if (s->oformat->priv_class) +av_opt_free(s->priv_data); + +av_freep(>priv_data); +} + int av_write_trailer(AVFormatContext *s) { -int ret, i; +int ret; for (;; ) { AVPacket pkt; @@ -1244,20 +1262,11 @@ fail: if (ret == AVERROR(EAGAIN)) return ret; -if (s->oformat->deinit) -s->oformat->deinit(s); - if (s->pb) avio_flush(s->pb); if (ret == 0) ret = s->pb ? s->pb->error : 0; -for (i = 0; i < s->nb_streams; i++) { -av_freep(>streams[i]->priv_data); -av_freep(>streams[i]->index_entries); -} -if (s->oformat->priv_class) -av_opt_free(s->priv_data); -av_freep(>priv_data); +deinit_muxer(s); return ret; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 04/11] avformat/muxers: Add non-blocking mode support for av_write_trailer
From: Jan SebechlebskyThis makes av_write_trailer not to free the resources if write_trailer call returns AVERROR(EAGAIN) allowing repeated calls of write_trailer of non-blocking muxer. Signed-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - Added assert to the part of the code dealing with flushing interleaved packets which should not be entered if muxer in non-blocking mode is used. (also there is assert for the same condition added into av_interleaved_write_packet in one of the following patches). libavformat/avformat.h | 6 +- libavformat/mux.c | 10 -- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index d8a6cf3..2cc3156 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -2510,8 +2510,12 @@ int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); * * May only be called after a successful call to avformat_write_header. * + * If AVFMT_FLAG_NONBLOCK is set, this call may return AVERROR(EAGAIN) + * meaning the operation is pending and the call should be repeated. + * * @param s media file handle - * @return 0 if OK, AVERROR_xxx on error + * @return 0 if OK, AVERROR(EAGAIN) in case call should be repeated, + * other AVERROR on error */ int av_write_trailer(AVFormatContext *s); diff --git a/libavformat/mux.c b/libavformat/mux.c index e9973ed..3ae924c 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1204,11 +1204,14 @@ int av_write_trailer(AVFormatContext *s) for (;; ) { AVPacket pkt; ret = interleave_packet(s, , NULL, 1); -if (ret < 0) -goto fail; if (!ret) break; +av_assert0(!(s->flags & AVFMT_FLAG_NONBLOCK)); + +if (ret < 0) +goto fail; + ret = write_packet(s, ); if (ret >= 0) s->streams[pkt.stream_index]->nb_frames++; @@ -1238,6 +1241,9 @@ fail: } } +if (ret == AVERROR(EAGAIN)) +return ret; + if (s->oformat->deinit) s->oformat->deinit(s); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 03/11] avformat/fifo: Add fate test
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - Fixed whitespace and missing $(EXESUF) in fifo-muxer.mak - Fixed "overflow with packet dropping" test which skipped write_trailer call in case of failure. libavformat/Makefile | 1 + libavformat/tests/fifo_muxer.c | 443 + tests/Makefile | 1 + tests/fate/fifo-muxer.mak | 20 ++ tests/ref/fate/fifo-muxer-tst | 11 + 5 files changed, 476 insertions(+) create mode 100644 libavformat/tests/fifo_muxer.c create mode 100644 tests/fate/fifo-muxer.mak create mode 100644 tests/ref/fate/fifo-muxer-tst diff --git a/libavformat/Makefile b/libavformat/Makefile index 2d2b78c..5d827d31 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -591,6 +591,7 @@ TESTPROGS = seek \ url \ # async \ +TESTPROGS-$(CONFIG_FIFO_MUXER) += fifo_muxer TESTPROGS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh TESTPROGS-$(CONFIG_MOV_MUXER)+= movenc TESTPROGS-$(CONFIG_NETWORK) += noproxy diff --git a/libavformat/tests/fifo_muxer.c b/libavformat/tests/fifo_muxer.c new file mode 100644 index 000..d6ce85d --- /dev/null +++ b/libavformat/tests/fifo_muxer.c @@ -0,0 +1,443 @@ +/* + * FIFO pseudo-muxer + * Copyright (c) 2016 Jan Sebechlebsky + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpeg; if not, write to the Free Software * Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "libavutil/avassert.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" + +#define MAX_TST_PACKETS 128 +#define SLEEPTIME_50_MS 5 +#define SLEEPTIME_10_MS 1 + +/* Implementation of mock muxer to simulate real muxer failures */ + +/* This is structure of data sent in packets to + * failing muxer */ +typedef struct FailingMuxerPacketData { +int ret; /* return value of write_packet call*/ +int recover_after; /* set ret to zero after this number of recovery attempts */ +unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */ +} FailingMuxerPacketData; + + +typedef struct FailingMuxerContext { +AVClass *class; +int write_header_ret; +int write_trailer_ret; +/* If non-zero, summary of processed packets will be printed in deinit */ +uint8_t print_deinit_summary; + +int flush_count; +int pts_written[MAX_TST_PACKETS]; +int pts_written_nr; +} FailingMuxerContext; + +static int failing_write_header(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_header_ret; +} + +static int failing_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ +FailingMuxerContext *ctx = avf->priv_data; +int ret = 0; +if (!pkt) { +ctx->flush_count++; +} else { +FailingMuxerPacketData *data = (FailingMuxerPacketData*) pkt->data; + +if (!data->recover_after) { +data->ret = 0; +} else { +data->recover_after--; +} + +ret = data->ret; + +if (data->sleep_time) { +int64_t slept = 0; +while (slept < data->sleep_time) { +if (ff_check_interrupt(>interrupt_callback)) +return AVERROR_EXIT; +av_usleep(SLEEPTIME_10_MS); +slept += SLEEPTIME_10_MS; +} +} + +if (!ret) { +ctx->pts_written[ctx->pts_written_nr++] = pkt->pts; +av_packet_unref(pkt); +} +} +return ret; +} + +static int failing_write_trailer(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_trailer_ret; +} + +static void failing_deinit(AVFormatContext *avf) +{ +int i; +FailingMuxerContext *ctx = avf->priv_data; + +if (!ctx->print_deinit_summary) +return; + +printf("flush count: %d\n", ctx->flush_count); +printf("pts seen nr: %d\n", ctx->pts_written_nr); +printf("pts seen: "); +
[FFmpeg-devel] [PATCH v7 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of the patch: - Fixed thread include (old patch included pthread.h directly) Changelog| 1 + configure| 1 + doc/muxers.texi | 93 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 665 +++ libavformat/version.h| 2 +- 7 files changed, 763 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index d9b6ecb..db6e415 100644 --- a/Changelog +++ b/Changelog @@ -14,6 +14,7 @@ version : - MediaCodec hwaccel - True Audio (TTA) muxer - crystalizer audio filter +- fifo muxer version 3.1: diff --git a/configure b/configure index 8e30c68..7599dc1 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..e2f06d3 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,99 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows the separation of encoding and muxing by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and output to +several destinations with different reliability/writing speed/latency. + +The behavior of the fifo muxer if the queue fills up or if the output fails is +selectable, + +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the input, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. Unlimited if set to zero. Default value is 16. + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +s@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least @var{recovery_wait_time} +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain (usually permanent) errors the recovery is not attempted even when +@var{attempt_recovery} is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server, continue processing the stream at real-time +rate even in case of temporary failure (network outage) and attempt to recover +streaming every second indefinitely. +@example +ffmpeg -re -i ... -c:v libx264 -c:a mp2 -f fifo -fifo_format flv -map 0:v -map 0:a + -block_on_overflow 0 -attempt_recovery 1 -recovery_wait_time 1 + -max_recovery_attempts 0 rtmp://example.com/live/stream_name +@end
[FFmpeg-devel] [PATCH v3 5/5] avformat/tee: Use BSF list API
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version of patch: - removed parse_bsfs() function I accidentaly left out (it's replaced by av_bsf_list_parse_str()) libavformat/tee.c | 120 ++ 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 5689ca3..7218b02 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -37,7 +37,7 @@ typedef enum { typedef struct { AVFormatContext *avf; -AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +AVBSFContext **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; @@ -64,46 +64,6 @@ static const AVClass tee_muxer_class = { .version= LIBAVUTIL_VERSION_INT, }; -/** - * Parse list of bitstream filters and add them to the list of filters - * pointed to by bsfs. - * - * The list must be specified in the form: - * BSFS ::= BSF[,BSFS] - */ -static int parse_bsfs(void *log_ctx, const char *bsfs_spec, - AVBitStreamFilterContext **bsfs) -{ -char *bsf_name, *buf, *dup, *saveptr; -int ret = 0; - -if (!(dup = buf = av_strdup(bsfs_spec))) -return AVERROR(ENOMEM); - -while (bsf_name = av_strtok(buf, ",", )) { -AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name); - -if (!bsf) { -av_log(log_ctx, AV_LOG_ERROR, - "Cannot initialize bitstream filter with name '%s', " - "unknown filter or internal error happened\n", - bsf_name); -ret = AVERROR_UNKNOWN; -goto end; -} - -/* append bsf context to the list of bsf contexts */ -*bsfs = bsf; -bsfs = >next; - -buf = NULL; -} - -end: -av_free(dup); -return ret; -} - static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) { if (!opt) { @@ -135,14 +95,8 @@ static int close_slave(TeeSlave *tee_slave) ret = av_write_trailer(avf); if (tee_slave->bsfs) { -for (i = 0; i < avf->nb_streams; ++i) { -AVBitStreamFilterContext *bsf_next, *bsf = tee_slave->bsfs[i]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} +for (i = 0; i < avf->nb_streams; ++i) +av_bsf_free(_slave->bsfs[i]); } av_freep(_slave->stream_map); av_freep(_slave->bsfs); @@ -312,7 +266,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) "output '%s', filters will be ignored\n", i, filename); continue; } -ret = parse_bsfs(avf, entry->value, _slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, _slave->bsfs[i]); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s' associated to " @@ -325,6 +279,31 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_dict_set(, entry->key, NULL, 0); } +for (i = 0; i < avf2->nb_streams; i++){ +if (!tee_slave->bsfs[i]) { +/* Add pass-through bitstream filter */ +ret = av_bsf_get_null_filter(_slave->bsfs[i]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Failed to create pass-through bitstream filter: %s\n", + av_err2str(ret)); +goto end; +} +} + +tee_slave->bsfs[i]->time_base_in = avf2->streams[i]->time_base; +ret = avcodec_parameters_copy(tee_slave->bsfs[i]->par_in, + avf2->streams[i]->codecpar); + +ret = av_bsf_init(tee_slave->bsfs[i]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, +"Failed to initialize bitstream filter(s): %s\n", +av_err2str(ret)); +goto end; +} +} + if (options) { entry = NULL; while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) @@ -349,20 +328,16 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) slave->avf->filename, slave->avf->oformat->name); for (i = 0; i < slave->avf->nb_streams; i++) { AVStream *st = slave->avf->streams[i]; -AVBitStreamFilterContext *bsf = slave->bsfs[i]; +AVBSFContext *bsf = slave->bsfs[i]; +const char * bsf_name; av_log(log_ctx, log_level, "stream:%d codec:%s type:%s", i, avcodec_get_name(st->codecpar->codec_id), av_get_media_type_string(st->codecpar->codec_type)); -if (bsf) { -
[FFmpeg-devel] [PATCH v3 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan SebechlebskyAdd support for nonblocking calls. Signed-off-by: Jan Sebechlebsky --- No changes since the last version of the patch, just rebased because of changes in the previous commit. libavformat/fifo.c | 61 -- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/libavformat/fifo.c b/libavformat/fifo.c index a4bf410..c3f17d5 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -19,12 +19,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/atomic.h" #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/threadmessage.h" #include "avformat.h" #include "internal.h" #include "pthread.h" +#include "url.h" #define FIFO_DEFAULT_QUEUE_SIZE 60 #define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 16 @@ -77,6 +79,17 @@ typedef struct FifoContext { /* Value > 0 signals queue overflow */ volatile uint8_t overflow_flag; +/* Whether termination was requested by invoking deinit + * before the thread was finished. Used only in non-blocking + * mode - when AVFMT_FLAG_NONBLOCK is set. */ +volatile int termination_requested; + +/* Initially 0, set to 1 immediately before thread function + * returns */ +volatile int thread_finished_flag; + +/* Original interrupt callback of the underlying muxer. */ +AVIOInterruptCB orig_interrupt_callback; } FifoContext; typedef struct FifoThreadContext { @@ -111,6 +124,16 @@ typedef struct FifoMessage { AVPacket pkt; } FifoMessage; +static int fifo_interrupt_callback_wrapper(void *arg) +{ +FifoContext *ctx = arg; + +if (avpriv_atomic_int_get(>termination_requested)) +return 1; + +return ff_check_interrupt(>orig_interrupt_callback); +} + static int fifo_thread_write_header(FifoThreadContext *ctx) { AVFormatContext *avf = ctx->avf; @@ -433,12 +456,17 @@ static void *fifo_consumer_thread(void *data) fifo->write_trailer_ret = fifo_thread_write_trailer(_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(>thread_finished_flag, 1); return NULL; } static int fifo_mux_init(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +AVIOInterruptCB interrupt_cb = {.callback = fifo_interrupt_callback_wrapper, +.opaque = fifo}; AVFormatContext *avf2; int ret = 0, i; @@ -449,7 +477,8 @@ static int fifo_mux_init(AVFormatContext *avf) fifo->avf = avf2; -avf2->interrupt_callback = avf->interrupt_callback; +fifo->orig_interrupt_callback = avf->interrupt_callback; +avf2->interrupt_callback = interrupt_cb; avf2->max_delay = avf->max_delay; ret = av_dict_copy(>metadata, avf->metadata, 0); if (ret < 0) @@ -536,7 +565,7 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) { FifoContext *fifo = avf->priv_data; FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT}; -int ret; +int ret, queue_flags = 0; if (pkt) { av_init_packet(); @@ -545,15 +574,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, , - fifo->drop_pkts_on_overflow ? - AV_THREAD_MESSAGE_NONBLOCK : 0); +if (fifo->drop_pkts_on_overflow || (avf->flags & AVFMT_FLAG_NONBLOCK)) +queue_flags |= AVFMT_FLAG_NONBLOCK; + +ret = av_thread_message_queue_send(fifo->queue, , queue_flags); + if (ret == AVERROR(EAGAIN)) { -uint8_t overflow_set = 0; +uint8_t overflow_set; + +if (avf->flags & AVFMT_FLAG_NONBLOCK) +return ret; /* Queue is full, set fifo->overflow_flag to 1 * to let consumer thread know the queue should * be flushed. */ +overflow_set = 0; pthread_mutex_lock(>overflow_flag_lock); if (!fifo->overflow_flag) fifo->overflow_flag = overflow_set = 1; @@ -581,6 +616,10 @@ static int fifo_write_trailer(AVFormatContext *avf) av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF); +if ((avf->flags & AVFMT_FLAG_NONBLOCK) && +!avpriv_atomic_int_get(>thread_finished_flag)) + return AVERROR(EAGAIN); + ret = pthread_join( fifo->writer_thread, NULL); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", @@ -596,6 +635,16 @@ static void fifo_deinit(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +if (avf->flags & AVFMT_FLAG_NONBLOCK) { +int ret; +avpriv_atomic_int_set(>termination_requested, 1); +ret = pthread_join( fifo->writer_thread, NULL); +if (ret
[FFmpeg-devel] [PATCH v2 03/11] avformat/fifo: Add fate test
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version: - Removed empty lines at the end of fifo_muxer.c file libavformat/Makefile | 1 + libavformat/tests/fifo_muxer.c | 443 + tests/Makefile | 1 + tests/fate/fifo-muxer.mak | 20 ++ tests/ref/fate/fifo-muxer-tst | 11 + 5 files changed, 476 insertions(+) create mode 100644 libavformat/tests/fifo_muxer.c create mode 100644 tests/fate/fifo-muxer.mak create mode 100644 tests/ref/fate/fifo-muxer-tst diff --git a/libavformat/Makefile b/libavformat/Makefile index 2d2b78c..5d827d31 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -591,6 +591,7 @@ TESTPROGS = seek \ url \ # async \ +TESTPROGS-$(CONFIG_FIFO_MUXER) += fifo_muxer TESTPROGS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh TESTPROGS-$(CONFIG_MOV_MUXER)+= movenc TESTPROGS-$(CONFIG_NETWORK) += noproxy diff --git a/libavformat/tests/fifo_muxer.c b/libavformat/tests/fifo_muxer.c new file mode 100644 index 000..94510c4 --- /dev/null +++ b/libavformat/tests/fifo_muxer.c @@ -0,0 +1,443 @@ +/* + * FIFO pseudo-muxer + * Copyright (c) 2016 Jan Sebechlebsky + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpeg; if not, write to the Free Software * Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "libavutil/avassert.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" + +#define MAX_TST_PACKETS 128 +#define SLEEPTIME_50_MS 5 +#define SLEEPTIME_10_MS 1 + +/* Implementation of mock muxer to simulate real muxer failures */ + +/* This is structure of data sent in packets to + * failing muxer */ +typedef struct FailingMuxerPacketData { +int ret; /* return value of write_packet call*/ +int recover_after; /* set ret to zero after this number of recovery attempts */ +unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */ +} FailingMuxerPacketData; + + +typedef struct FailingMuxerContext { +AVClass *class; +int write_header_ret; +int write_trailer_ret; +/* If non-zero, summary of processed packets will be printed in deinit */ +uint8_t print_deinit_summary; + +int flush_count; +int pts_written[MAX_TST_PACKETS]; +int pts_written_nr; +} FailingMuxerContext; + +static int failing_write_header(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_header_ret; +} + +static int failing_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ +FailingMuxerContext *ctx = avf->priv_data; +int ret = 0; +if (!pkt) { +ctx->flush_count++; +} else { +FailingMuxerPacketData *data = (FailingMuxerPacketData*) pkt->data; + +if (!data->recover_after) { +data->ret = 0; +} else { +data->recover_after--; +} + +ret = data->ret; + +if (data->sleep_time) { +int64_t slept = 0; +while (slept < data->sleep_time) { +if (ff_check_interrupt(>interrupt_callback)) +return AVERROR_EXIT; +av_usleep(SLEEPTIME_10_MS); +slept += SLEEPTIME_10_MS; +} +} + +if (!ret) { +ctx->pts_written[ctx->pts_written_nr++] = pkt->pts; +av_packet_unref(pkt); +} +} +return ret; +} + +static int failing_write_trailer(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_trailer_ret; +} + +static void failing_deinit(AVFormatContext *avf) +{ +int i; +FailingMuxerContext *ctx = avf->priv_data; + +if (!ctx->print_deinit_summary) +return; + +printf("flush count: %d\n", ctx->flush_count); +printf("pts seen nr: %d\n", ctx->pts_written_nr); +printf("pts seen: "); +for (i = 0; i < ctx->pts_written_nr; ++i ) { +printf(i ? ",%d" : "%d", ctx->pts_written[i]); +} +
[FFmpeg-devel] [PATCH v6 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes since the last version done according Martons review: - Documentation fixes as requested - Rearanged message processing in while loop inside fifo_cosumer_thread, added FIFO_WRITE_HEADER message type, message is initialized to FIFO_WRITE_HEADER in fifo_consumer_thread so that this message is processed immediately after entering the while loop. - Fixed error description when using streamtime is requested, but drop_pkts_on_overflow is turned off. - Fixed queue allocation return value check - Removed avf check prior to freeing avf in fifo_deinit since it is safe to pass NULL to avformat_free_context - Removed empty line at the end of the fifo.c file Changelog| 1 + configure| 1 + doc/muxers.texi | 93 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 665 +++ libavformat/version.h| 2 +- 7 files changed, 763 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index d9b6ecb..db6e415 100644 --- a/Changelog +++ b/Changelog @@ -14,6 +14,7 @@ version : - MediaCodec hwaccel - True Audio (TTA) muxer - crystalizer audio filter +- fifo muxer version 3.1: diff --git a/configure b/configure index 8e30c68..7599dc1 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..e2f06d3 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,99 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows the separation of encoding and muxing by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and output to +several destinations with different reliability/writing speed/latency. + +The behavior of the fifo muxer if the queue fills up or if the output fails is +selectable, + +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the input, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. Unlimited if set to zero. Default value is 16. + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +s@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least @var{recovery_wait_time} +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain (usually permanent) errors the recovery is not attempted even when +@var{attempt_recovery} is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after
[FFmpeg-devel] [PATCH v2 5/5] avformat/tee: Use BSF list API
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- No changes from the last version, just rebased after Michael's commit. libavformat/tee.c | 80 +++ 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 5689ca3..179ad2d 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -37,7 +37,7 @@ typedef enum { typedef struct { AVFormatContext *avf; -AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +AVBSFContext **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; @@ -135,14 +135,8 @@ static int close_slave(TeeSlave *tee_slave) ret = av_write_trailer(avf); if (tee_slave->bsfs) { -for (i = 0; i < avf->nb_streams; ++i) { -AVBitStreamFilterContext *bsf_next, *bsf = tee_slave->bsfs[i]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} +for (i = 0; i < avf->nb_streams; ++i) +av_bsf_free(_slave->bsfs[i]); } av_freep(_slave->stream_map); av_freep(_slave->bsfs); @@ -312,7 +306,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) "output '%s', filters will be ignored\n", i, filename); continue; } -ret = parse_bsfs(avf, entry->value, _slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, _slave->bsfs[i]); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s' associated to " @@ -325,6 +319,31 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_dict_set(, entry->key, NULL, 0); } +for (i = 0; i < avf2->nb_streams; i++){ +if (!tee_slave->bsfs[i]) { +/* Add pass-through bitstream filter */ +ret = av_bsf_get_null_filter(_slave->bsfs[i]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Failed to create pass-through bitstream filter: %s\n", + av_err2str(ret)); +goto end; +} +} + +tee_slave->bsfs[i]->time_base_in = avf2->streams[i]->time_base; +ret = avcodec_parameters_copy(tee_slave->bsfs[i]->par_in, + avf2->streams[i]->codecpar); + +ret = av_bsf_init(tee_slave->bsfs[i]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, +"Failed to initialize bitstream filter(s): %s\n", +av_err2str(ret)); +goto end; +} +} + if (options) { entry = NULL; while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) @@ -349,20 +368,16 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) slave->avf->filename, slave->avf->oformat->name); for (i = 0; i < slave->avf->nb_streams; i++) { AVStream *st = slave->avf->streams[i]; -AVBitStreamFilterContext *bsf = slave->bsfs[i]; +AVBSFContext *bsf = slave->bsfs[i]; +const char * bsf_name; av_log(log_ctx, log_level, "stream:%d codec:%s type:%s", i, avcodec_get_name(st->codecpar->codec_id), av_get_media_type_string(st->codecpar->codec_type)); -if (bsf) { -av_log(log_ctx, log_level, " bsfs:"); -while (bsf) { -av_log(log_ctx, log_level, "%s%s", - bsf->filter->name, bsf->next ? "," : ""); -bsf = bsf->next; -} -} -av_log(log_ctx, log_level, "\n"); + +bsf_name = bsf->filter->priv_class ? + bsf->filter->priv_class->item_name(bsf) : bsf->filter->name; +av_log(log_ctx, log_level, " bsfs: %s\n", bsf_name); } } @@ -506,13 +521,32 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) av_packet_rescale_ts(, tb, tb2); pkt2.stream_index = s2; -if ((ret = av_apply_bitstream_filters(avf2->streams[s2]->codec, , - tee->slaves[i].bsfs[s2])) < 0 || -(ret = av_interleaved_write_frame(avf2, )) < 0) { +ret = av_bsf_send_packet(tee->slaves[i].bsfs[s2], ); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, "Error while sending packet to bitstream filter: %s\n", + av_err2str(ret)); ret = tee_process_slave_failure(avf, i, ret); if (!ret_all && ret < 0) ret_all = ret; } + +do { +ret = av_bsf_receive_packet(tee->slaves[i].bsfs[s2], ); +if (ret < 0) +
[FFmpeg-devel] [PATCH v2 4/5] avcodec/bsf: Add custom item name func for BSF list
From: Jan SebechlebskyAdd custom item name function for bsf list, which will construct string description of filter chain. This is done using lazy-initialization, so there is no overhead if item name is never accessed. Signed-off-by: Jan Sebechlebsky --- No changes from last version, just rebased because of the changes in previous patch. libavcodec/bsf.c | 28 +++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index dcdc1d3..2462e62 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -248,6 +248,7 @@ typedef struct BSFListContext { unsigned idx; // index of currently processed BSF unsigned flushed_idx; // index of BSF being flushed +char * item_name; } BSFListContext; @@ -348,9 +349,34 @@ static void bsf_list_close(AVBSFContext *bsf) av_freep(>item_name); } +static const char *bsf_list_item_name(void *ctx) +{ +static const char *null_filter_name = "null"; +AVBSFContext *bsf_ctx = ctx; +BSFListContext *lst = bsf_ctx->priv_data; + +if (!lst->nb_bsfs) +return null_filter_name; + +if (!lst->item_name) { +int i; +AVBPrint bp; +av_bprint_init(, 16, 128); + +av_bprintf(, "bsf_list("); +for (i = 0; i < lst->nb_bsfs; i++) +av_bprintf(, i ? ",%s" : "%s", lst->bsfs[i]->filter->name); +av_bprintf(, ")"); + +av_bprint_finalize(, >item_name); +} + +return lst->item_name; +} + static const AVClass bsf_list_class = { .class_name = "bsf_list", -.item_name = av_default_item_name, +.item_name = bsf_list_item_name, .version= LIBAVUTIL_VERSION_INT, }; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 3/5] avcodec/bsf: Add list BSF API
From: Jan Sebechlebsky--- Changes from last version: - fixed doxygen comments - added av_bsf_list_append2() function - changed names of array and length fields to follow naming convention - idx and flushed_idx is now unsigned - merged bsf_list_flush to bsf_list_filter to reduce code duplication - aligned structure fields initialization - added check for NULL to av_bsf_free() - fixed memleak in av_bsf_finalize in case there is only 1 filter libavcodec/avcodec.h | 85 libavcodec/bsf.c | 279 +++ 2 files changed, 364 insertions(+) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 6c2c4a7..06c2b89 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -5951,6 +5951,91 @@ void av_bsf_free(AVBSFContext **ctx); */ const AVClass *av_bsf_get_class(void); +/** + * Structure for chain/list of bitstream filters. + * Empty list can be allocated by av_bsf_list_alloc(). + */ +typedef struct AVBSFList AVBSFList; + +/** + * Allocate empty list of bitstream filters. + * The list must be later freed by av_bsf_list_free() + * or finalized by av_bsf_list_finalize(). + * + * @return Pointer to @ref AVBSFList on success, NULL in case of failure + */ +AVBSFList *av_bsf_list_alloc(void); + +/** + * Free list of bitstream filters. + * + * @param lst Pointer to pointer returned by av_bsf_list_alloc() + */ +void av_bsf_list_free(AVBSFList **lst); + +/** + * Append bitstream filter to the list of bitstream filters. + * + * @param lst List to append to + * @param bsf Filter context to be appended + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append(AVBSFList *lst, AVBSFContext *bsf); + +/** + * Construct new bitstream filter context given it's name and options + * and append it to the list of bitstream filters. + * + * @param lst List to append to + * @param bsf_name Name of the bitstream filter + * @param options Options for the bitstream filter, can be set to NULL + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append2(AVBSFList *lst, const char * bsf_name, AVDictionary **options); +/** + * Finalize list of bitstream filters. + * + * This function will transform @ref AVBSFList to single @ref AVBSFContext, + * so the whole chain of bitstream filters can be treated as single filter + * freshly allocated by av_bsf_alloc(). + * If the call is successfull, @ref AVBSFList structure is freed and lst + * will be set to NULL. In case of failure, caller is responsible for + * freeing the structure by av_bsf_list_free() + * + * @param lst Filter list structure to be transformed + * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf); + +/** + * Parse string describing list of bitstream filters and create single + * @ref AVBSFContext describing the whole chain of bitstream filters. + * Resulting @ref AVBSFContext can be treated as any other @ref AVBSFContext freshly + * allocated by av_bsf_alloc(). + * + * @param str String describing chain of bitstream filters in format + * `bsf1[=opt1=val1:opt2=val2][,bsf2]` + * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf); + +/** + * Get null/pass-through bitstream filter. + * + * @param[out] bsf Pointer to be set to new instance of pass-through bitstream filter + * + * @return + */ +int av_bsf_get_null_filter(AVBSFContext **bsf); + /* memory */ /** diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 40fc925..dcdc1d3 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -22,6 +22,8 @@ #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" #include "avcodec.h" #include "bsf.h" @@ -236,3 +238,280 @@ int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt) return 0; } + +typedef struct BSFListContext { +const AVClass *class; + +AVBSFContext **bsfs; +int nb_bsfs; + +unsigned idx; // index of currently processed BSF +unsigned flushed_idx; // index of BSF being flushed + +} BSFListContext; + + +static int bsf_list_init(AVBSFContext *bsf) +{ +BSFListContext *lst = bsf->priv_data; +int ret, i; +const AVCodecParameters *cod_par = bsf->par_in; +AVRational tb = bsf->time_base_in; + +for (i = 0; i < lst->nb_bsfs; ++i) { +ret = avcodec_parameters_copy(lst->bsfs[i]->par_in, cod_par); +if (ret < 0) +goto fail; + +
[FFmpeg-devel] [PATCH v2 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan SebechlebskyAdd support for nonblocking calls. Signed-off-by: Jan Sebechlebsky --- Changes from the last version: - boolean flags accessed from both threads are ints now and are accessed with atomic operations. - pthread_tryjoin_np is replaced by flag set before fifo_consumer_thread returns libavformat/fifo.c | 61 -- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/libavformat/fifo.c b/libavformat/fifo.c index bd9d934..9b92eab 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -19,12 +19,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/atomic.h" #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/threadmessage.h" #include "avformat.h" #include "internal.h" #include "pthread.h" +#include "url.h" #define FIFO_DEFAULT_QUEUE_SIZE 60 #define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 16 @@ -77,6 +79,17 @@ typedef struct FifoContext { /* Value > 0 signalizes queue overflow */ volatile uint8_t overflow_flag; +/* Whether termination was requested by invoking deinit + * before the thread was finished. Used only in non-blocking + * mode - when AVFMT_FLAG_NONBLOCK is set. */ +volatile int termination_requested; + +/* Initially 0, set to 1 immediately before thread function + * returns */ +volatile int thread_finished_flag; + +/* Original interrupt callback of the underlying muxer. */ +AVIOInterruptCB orig_interrupt_callback; } FifoContext; typedef struct FifoThreadContext { @@ -110,6 +123,16 @@ typedef struct FifoMessage { AVPacket pkt; } FifoMessage; +static int fifo_interrupt_callback_wrapper(void *arg) +{ +FifoContext *ctx = arg; + +if (avpriv_atomic_int_get(>termination_requested)) +return 1; + +return ff_check_interrupt(>orig_interrupt_callback); +} + static int fifo_thread_write_header(FifoThreadContext *ctx) { AVFormatContext *avf = ctx->avf; @@ -442,12 +465,17 @@ static void *fifo_consumer_thread(void *data) fifo->write_trailer_ret = fifo_thread_write_trailer(_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(>thread_finished_flag, 1); return NULL; } static int fifo_mux_init(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +AVIOInterruptCB interrupt_cb = {.callback = fifo_interrupt_callback_wrapper, +.opaque = fifo}; AVFormatContext *avf2; int ret = 0, i; @@ -458,7 +486,8 @@ static int fifo_mux_init(AVFormatContext *avf) fifo->avf = avf2; -avf2->interrupt_callback = avf->interrupt_callback; +fifo->orig_interrupt_callback = avf->interrupt_callback; +avf2->interrupt_callback = interrupt_cb; avf2->max_delay = avf->max_delay; ret = av_dict_copy(>metadata, avf->metadata, 0); if (ret < 0) @@ -543,7 +572,7 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) { FifoContext *fifo = avf->priv_data; FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT}; -int ret; +int ret, queue_flags = 0; if (pkt) { av_init_packet(); @@ -552,15 +581,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, , - fifo->drop_pkts_on_overflow ? - AV_THREAD_MESSAGE_NONBLOCK : 0); +if (fifo->drop_pkts_on_overflow || (avf->flags & AVFMT_FLAG_NONBLOCK)) +queue_flags |= AVFMT_FLAG_NONBLOCK; + +ret = av_thread_message_queue_send(fifo->queue, , queue_flags); + if (ret == AVERROR(EAGAIN)) { -uint8_t overflow_set = 0; +uint8_t overflow_set; + +if (avf->flags & AVFMT_FLAG_NONBLOCK) +return ret; /* Queue is full, set fifo->overflow_flag to 1 * to let consumer thread know the queue should * be flushed. */ +overflow_set = 0; pthread_mutex_lock(>overflow_flag_lock); if (!fifo->overflow_flag) fifo->overflow_flag = overflow_set = 1; @@ -588,6 +623,10 @@ static int fifo_write_trailer(AVFormatContext *avf) av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF); +if ((avf->flags & AVFMT_FLAG_NONBLOCK) && +!avpriv_atomic_int_get(>thread_finished_flag)) + return AVERROR(EAGAIN); + ret = pthread_join( fifo->writer_thread, NULL); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", @@ -603,6 +642,16 @@ static void fifo_deinit(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +if (avf->flags & AVFMT_FLAG_NONBLOCK) { +int ret; +
[FFmpeg-devel] [PATCH v2 06/11] avformat: add avformat_write_abort() function
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes from last version of patch: - removed AVFMT_FLAG_NONBLOCK check and modified comment so it states how function behaves with both blocking / non-blocking muxer libavformat/avformat.h | 15 +++ libavformat/mux.c | 13 + 2 files changed, 28 insertions(+) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 578f99f..3f6a6eb 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -2512,6 +2512,8 @@ int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); * * If AVFMT_FLAG_NONBLOCK is set, this call may return AVERROR(EAGAIN) * meaning the operation is pending and the call should be repeated. + * If caller decides to abort operation (after too many calls have returned + * AVERROR(EAGAIN)), it can be done by calling @ref avformat_write_abort(). * * @param s media file handle * @return 0 if OK, AVERROR(EAGAIN) in case call should be repeated, @@ -2520,6 +2522,19 @@ int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); int av_write_trailer(AVFormatContext *s); /** + * Abort muxer operation and free private data. + * For muxer operating in blocking mode, this is equivalent to calling + * av_write_trailer. For muxer operating in non-blocking mode, this will + * call deinitialize routine even if there is operation pending + * and @ref av_write_trailer() keeps returning AVERROR(EAGAIN). + * May only be called after a successful call to avformat_write_header. + * + * @param s Media file handle + * return >= 0 on success, negative AVERROR on error + */ +int avformat_write_abort(AVFormatContext *s); + +/** * Return the output format in the list of registered output formats * which best matches the provided parameters, or return NULL if * there is no match. diff --git a/libavformat/mux.c b/libavformat/mux.c index bc9c98f..a3c1d8a 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1267,6 +1267,19 @@ fail: return ret; } +int avformat_write_abort(AVFormatContext *s) +{ +int ret; + +ret = av_write_trailer(s); +if (ret == AVERROR(EAGAIN)) { +deinit_muxer(s); +ret = 0; +} + +return ret; +} + int av_get_output_timestamp(struct AVFormatContext *s, int stream, int64_t *dts, int64_t *wall) { -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v5 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes from the last version of patch: - boolean AVOptions are now ints, this was the cause of fate test segfault reported by Michael Changelog| 1 + configure| 1 + doc/muxers.texi | 90 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 674 +++ libavformat/version.h| 2 +- 7 files changed, 769 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index 0f9b4cf..3f858f1 100644 --- a/Changelog +++ b/Changelog @@ -12,6 +12,7 @@ version : - 16-bit support in selectivecolor filter - OpenH264 decoder wrapper - MediaCodec hwaccel +- fifo muxer version 3.1: diff --git a/configure b/configure index 9f5b31f..4651f5f 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..e2bc290 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,96 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows to separate encoding from any other muxer by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and output to +several destinations with different reliability/writing speed/latency. + +The behavior of fifo muxer in case of failure can be configured: +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the output, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. Unlimited if set to zero. Default value is 16. + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +s@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least recovery_wait_time +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain errors the recovery is not attempted even when @ref{attempt_recovery} +is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server, continue processing the stream at real-time +rate even in case of temporary failure (network outage) and attempt to recover +streaming every second indefinitely. +@example +ffmpeg -re -i ... -c:v libx264 -c:a mp2 -f fifo -fifo_format flv -map 0:v -map 0:a + -block_on_overflow 0 -attempt_recovery 1 -recovery_wait_time 1 + -max_recovery_attempts 0 rtmp://example.com/live/stream_name +@end
[FFmpeg-devel] [PATCH 11/11] avformat/fifo: Add test for nonblocking mode
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavformat/tests/fifo_muxer.c | 139 + tests/ref/fate/fifo-muxer-tst | 5 ++ 2 files changed, 144 insertions(+) diff --git a/libavformat/tests/fifo_muxer.c b/libavformat/tests/fifo_muxer.c index 0b5a95e..0a90f36 100644 --- a/libavformat/tests/fifo_muxer.c +++ b/libavformat/tests/fifo_muxer.c @@ -336,6 +336,137 @@ fail: return ret; } +static int retry_write_frame(AVFormatContext *oc, AVPacket *pkt, int max_retries) +{ +int ret = 0, retry_count = 0; +do { +ret = av_write_frame(oc, pkt); +if (ret == AVERROR(EAGAIN)) { +av_usleep(SLEEPTIME_10_MS); +retry_count++; +} +} while ( ret == AVERROR(EAGAIN) && retry_count < max_retries); +return ret; +} + +static int retry_write_trailer(AVFormatContext *oc, int max_retries) +{ +int ret = 0, retry_count = 0; +do { +ret = av_write_trailer(oc); +if (ret == AVERROR(EAGAIN)) { +av_usleep(SLEEPTIME_10_MS); +retry_count++; +} +} while (ret == AVERROR(EAGAIN) && retry_count < max_retries); +return ret; +} + +static int fifo_nonblock_test(AVFormatContext *oc, AVDictionary **opts, + const FailingMuxerPacketData *pkt_data) +{ +int ret = 0, i; +AVPacket pkt; + +av_init_packet(); + +oc->flags |= AVFMT_FLAG_NONBLOCK; + +ret = avformat_write_header(oc, opts); +if (ret) { +fprintf(stderr, "Unexpected write_header failure: %s\n", +av_err2str(ret)); +return ret; +} + +for (i = 0; i < 16; i++ ) { +ret = prepare_packet(, pkt_data, i); +if (ret < 0) { +fprintf(stderr, "Failed to prepare test packet: %s\n", +av_err2str(ret)); +goto fail; +} +ret = retry_write_frame(oc, , 100); +av_packet_unref(); +if (ret < 0) +break; +} + +if (ret) { +fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret)); +goto fail; +} + +ret = retry_write_trailer(oc, 100); +if (ret == AVERROR(EAGAIN)) { +fprintf(stderr, "write_trailer() operation timeout\n"); +goto fail; +} else if (ret < 0) +fprintf(stderr, "Unexpected write_trailer error: %s\n", av_err2str(ret)); + +return ret; +fail: +av_abort_output(oc); +return ret; +} + +static int fifo_nonblock_abort_test(AVFormatContext *oc, AVDictionary **opts, +const FailingMuxerPacketData *pkt_data) +{ +int ret = 0, i; +AVPacket pkt; +int64_t start_time, end_time, duration; + +av_init_packet(); + +oc->flags |= AVFMT_FLAG_NONBLOCK; + +ret = avformat_write_header(oc, opts); +if (ret) { +fprintf(stderr, "Unexpected write header failure: %s\n", +av_err2str(ret)); +goto fail; +} + +av_assert0(pkt_data->sleep_time > 0); + +start_time = av_gettime_relative(); +for (i = 0; i < 16; i++ ) { +ret = prepare_packet(, pkt_data, i); +if (ret < 0) { +fprintf(stderr, "Failed to prepare test packet: %s\n", +av_err2str(ret)); +goto fail; +} +ret = retry_write_frame(oc, , 100); +av_packet_unref(); +if (ret < 0) +break; +} + +if (ret) { +fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret)); +goto fail; +} + +av_abort_output(oc); + +end_time = av_gettime_relative(); +duration = end_time - start_time; + +if (duration > (16*pkt_data->sleep_time)/2 ) { +fprintf(stderr, "Aborting output took too much time: %u us," +" expected time if not aborted %u us\n", +(unsigned) duration, 16*pkt_data->sleep_time); +ret = AVERROR(ETIMEDOUT); +} + +return ret; +fail: +av_abort_output(oc); +return ret; +} + typedef struct TestCase { int (*test_func)(AVFormatContext *, AVDictionary **,const FailingMuxerPacketData *pkt_data); const char *test_name; @@ -423,6 +554,14 @@ const TestCase tests[] = { {fifo_overflow_drop_test, "overflow with packet dropping", "queue_size=3:drop_pkts_on_overflow=1", 0, 0, 0, {0, 0, SLEEPTIME_50_MS}}, +/* Simple test of nonblocking mode, the consumer should receive all the packets. */ +{fifo_nonblock_test, "nonblocking mode test", "queue_size=3", + 1, 0, 0, {0, 0, SLEEPTIME_10_MS}}, + +/* Test of terminating fifo muxer with av_abort_format() */ +{fifo_nonblock_abort_test, "abort in nonblocking mode", "queue_size=16", + 0, 0, 0, {0, 0, SLEEPTIME_50_MS}}, + {NULL} }; diff --git a/tests/ref/fate/fifo-muxer-tst b/tests/ref/fate/fifo-muxer-tst index ca7e294..1c18887 100644 ---
[FFmpeg-devel] [GSoC] fifo muxer
From: Jan SebechlebskyHello, I am resending fifo muxer related patchset, now also with patches adding support for nonblocking calls (AVFMT_FLAG_NONBLOCK) and fate tests. Regards, Jan Jan Sebechlebsky (11): avformat: Add fifo pseudo-muxer MAINTAINERS: Add myself as maintainer of fifo muxer avformat/fifo: Add fate test avformat/muxers: Add non-blocking mode support for av_write_trailer avformat/mux: Refactor muxer deinit from av_write_trailer avformat: add av_abort_output() function avformat/avformat.h: Add comments regarding AVFMT_FLAG_NONBLOCK. avformat/mux: Restore original ts in write_packet on error avformat/mux: Restore stream ts in av_write_packet on EAGAIN avformat/fifo: Add AVFMT_FLAG_NONBLOCK support avformat/fifo: Add test for nonblocking mode Changelog | 1 + MAINTAINERS| 1 + configure | 1 + doc/muxers.texi| 90 + libavformat/Makefile | 2 + libavformat/allformats.c | 1 + libavformat/avformat.h | 29 +- libavformat/fifo.c | 722 + libavformat/mux.c | 69 +++- libavformat/tests/fifo_muxer.c | 586 + libavformat/version.h | 2 +- tests/Makefile | 1 + tests/fate/fifo-muxer.mak | 20 ++ tests/ref/fate/fifo-muxer-tst | 16 + 14 files changed, 1528 insertions(+), 13 deletions(-) create mode 100644 libavformat/fifo.c create mode 100644 libavformat/tests/fifo_muxer.c create mode 100644 tests/fate/fifo-muxer.mak create mode 100644 tests/ref/fate/fifo-muxer-tst -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 08/11] avformat/mux: Restore original ts in write_packet on error
From: Jan SebechlebskyRestore original timestamps in write_packet() if the actual write operation was not successfull. This allows to pass the same packet to nonblocking muxer repeatedly without corrupting the timestamps. Signed-off-by: Jan Sebechlebsky --- libavformat/mux.c | 9 + 1 file changed, 9 insertions(+) diff --git a/libavformat/mux.c b/libavformat/mux.c index 888a9f1..ef4720a 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -657,6 +657,10 @@ FF_ENABLE_DEPRECATION_WARNINGS static int write_packet(AVFormatContext *s, AVPacket *pkt) { int ret, did_split; +int64_t pts_backup, dts_backup; + +pts_backup = pkt->pts; +dts_backup = pkt->dts; if (s->output_ts_offset) { AVStream *st = s->streams[pkt->stream_index]; @@ -743,6 +747,11 @@ fail: if (did_split) av_packet_merge_side_data(pkt); +if (ret < 0) { +pkt->pts = pts_backup; +pkt->dts = dts_backup; +} + return ret; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 05/11] avformat/mux: Refactor muxer deinit from av_write_trailer
From: Jan SebechlebskyMove muxer deinitialization and private resources freeing in a separate static function free_muxer(AVFormatContext*). Signed-off-by: Jan Sebechlebsky --- libavformat/mux.c | 31 --- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/libavformat/mux.c b/libavformat/mux.c index b58e4c1..bc9c98f 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1197,9 +1197,27 @@ fail: return ret; } +static void deinit_muxer(AVFormatContext *s) +{ +int i; + +if (s->oformat->deinit) +s->oformat->deinit(s); + +for (i = 0; i < s->nb_streams; i++) { +av_freep(>streams[i]->priv_data); +av_freep(>streams[i]->index_entries); +} + +if (s->oformat->priv_class) +av_opt_free(s->priv_data); + +av_freep(>priv_data); +} + int av_write_trailer(AVFormatContext *s) { -int ret, i; +int ret; for (;; ) { AVPacket pkt; @@ -1241,20 +1259,11 @@ fail: if (ret == AVERROR(EAGAIN)) return ret; -if (s->oformat->deinit) -s->oformat->deinit(s); - if (s->pb) avio_flush(s->pb); if (ret == 0) ret = s->pb ? s->pb->error : 0; -for (i = 0; i < s->nb_streams; i++) { -av_freep(>streams[i]->priv_data); -av_freep(>streams[i]->index_entries); -} -if (s->oformat->priv_class) -av_opt_free(s->priv_data); -av_freep(>priv_data); +deinit_muxer(s); return ret; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 02/11] MAINTAINERS: Add myself as maintainer of fifo muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 932e6fb..9fab34d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -164,6 +164,7 @@ Codecs: exif.c, exif.hThilo Borgmann ffv1* Michael Niedermayer ffwavesynth.c Nicolas George + fifo.cJan Sebechlebsky flicvideo.c Mike Melanson g722.cMartin Storsjo g726.cRoman Shaposhnik -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v4 01/11] avformat: Add fifo pseudo-muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Changes from the last version of patch: - I got rid of write header message, and pulled initial write_header call out of the while loop as Nicolas originaly suggested. Changelog| 1 + configure| 1 + doc/muxers.texi | 90 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 674 +++ libavformat/version.h| 2 +- 7 files changed, 769 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index 0f9b4cf..3f858f1 100644 --- a/Changelog +++ b/Changelog @@ -12,6 +12,7 @@ version : - 16-bit support in selectivecolor filter - OpenH264 decoder wrapper - MediaCodec hwaccel +- fifo muxer version 3.1: diff --git a/configure b/configure index 9f5b31f..4651f5f 100755 --- a/configure +++ b/configure @@ -2834,6 +2834,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..e2bc290 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,96 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows to separate encoding from any other muxer by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and output to +several destinations with different reliability/writing speed/latency. + +The behavior of fifo muxer in case of failure can be configured: +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the output, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. Unlimited if set to zero. Default value is 16. + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +s@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least recovery_wait_time +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain errors the recovery is not attempted even when @ref{attempt_recovery} +is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server, continue processing the stream at real-time +rate even in case of temporary failure (network outage) and attempt to recover +streaming every second indefinitely. +@example +ffmpeg -re -i ... -c:v libx264 -c:a mp2 -f fifo -fifo_format flv -map 0:v -map 0:a + -block_on_overflow 0 -attempt_recovery 1 -recovery_wait_time 1 + -max_recovery_attempts 0
[FFmpeg-devel] [PATCH 04/11] avformat/muxers: Add non-blocking mode support for av_write_trailer
From: Jan SebechlebskyThis makes av_write_trailer not to free the resources if write_trailer call returns AVERROR(EAGAIN) allowing repeated calls of write_trailer of non-blocking muxer. Signed-off-by: Jan Sebechlebsky --- libavformat/avformat.h | 6 +- libavformat/mux.c | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 818184e..9191c69 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -2508,8 +2508,12 @@ int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); * * May only be called after a successful call to avformat_write_header. * + * If AVFMT_FLAG_NONBLOCK is set, this call may return AVERROR(EAGAIN) + * meaning the operation is pending and the call should be repeated. + * * @param s media file handle - * @return 0 if OK, AVERROR_xxx on error + * @return 0 if OK, AVERROR(EAGAIN) in case call should be repeated, + * other AVERROR on error */ int av_write_trailer(AVFormatContext *s); diff --git a/libavformat/mux.c b/libavformat/mux.c index e9973ed..b58e4c1 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1238,6 +1238,9 @@ fail: } } +if (ret == AVERROR(EAGAIN)) +return ret; + if (s->oformat->deinit) s->oformat->deinit(s); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 06/11] avformat: add av_abort_output() function
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavformat/avformat.h | 14 ++ libavformat/mux.c | 16 2 files changed, 30 insertions(+) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 9191c69..9173908 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -2510,6 +2510,8 @@ int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); * * If AVFMT_FLAG_NONBLOCK is set, this call may return AVERROR(EAGAIN) * meaning the operation is pending and the call should be repeated. + * If caller decides to abort operation (after too many calls have returned + * AVERROR(EAGAIN)), it can be done by calling @ref av_abort_output(). * * @param s media file handle * @return 0 if OK, AVERROR(EAGAIN) in case call should be repeated, @@ -2518,6 +2520,18 @@ int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); int av_write_trailer(AVFormatContext *s); /** + * Abort non-blocking muxer operation and free private data. + * + * May only be called after a successful call to avformat_write_header, + * and used only with muxer operating in non-blocking mode (AVFMT_FLAG_NONBLOCK) + * must be set. + * + * @param s media file handle + * return >= 0 on success, negative AVERROR on error + */ +int av_abort_output(AVFormatContext *s); + +/** * Return the output format in the list of registered output formats * which best matches the provided parameters, or return NULL if * there is no match. diff --git a/libavformat/mux.c b/libavformat/mux.c index bc9c98f..888a9f1 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1267,6 +1267,22 @@ fail: return ret; } +int av_abort_output(AVFormatContext *s) +{ +int ret; + +if (!(s->flags & AVFMT_FLAG_NONBLOCK)) +return AVERROR(EINVAL); + +ret = av_write_trailer(s); +if (ret == AVERROR(EAGAIN)) { +deinit_muxer(s); +ret = 0; +} + +return ret; +} + int av_get_output_timestamp(struct AVFormatContext *s, int stream, int64_t *dts, int64_t *wall) { -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 07/11] avformat/avformat.h: Add comments regarding AVFMT_FLAG_NONBLOCK.
From: Jan SebechlebskyAdd comments regarding AVFMG_FLAG_NONBLOCK usage with muxers. Signed-off-by: Jan Sebechlebsky --- libavformat/avformat.h | 9 - 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 9173908..6898c8c 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1426,7 +1426,7 @@ typedef struct AVFormatContext { int flags; #define AVFMT_FLAG_GENPTS 0x0001 ///< Generate missing pts even if it requires parsing future frames. #define AVFMT_FLAG_IGNIDX 0x0002 ///< Ignore index. -#define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input. +#define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input / writing packets to output. #define AVFMT_FLAG_IGNDTS 0x0008 ///< Ignore DTS on frames that contain both DTS & PTS #define AVFMT_FLAG_NOFILLIN 0x0010 ///< Do not infer any values from other values, just return what is stored in the container #define AVFMT_FLAG_NOPARSE 0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled @@ -2389,6 +2389,10 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options); * the interleaving should call av_interleaved_write_frame() instead of this * function. * + * In case the muxer is operating in non-blocking mode (AVFMT_FLAG_NONBLOCK + * is set), this function can return AVERROR(EAGAIN) meaning the call should + * be repeated. + * * @param s media file handle * @param pkt The packet containing the data to be written. Note that unlike *av_interleaved_write_frame(), this function does not take @@ -2431,6 +2435,9 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt); * knowledge of future packets, improving e.g. the behaviour of the mp4 * muxer for VFR content in fragmenting mode. * + * This call is not supported and must not be called if muxer is operating + * in non-blocking mode (AVFMT_FLAG_NONBLOCK is set). + * * @param s media file handle * @param pkt The packet containing the data to be written. * -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 03/11] avformat/fifo: Add fate test
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- This adds two tests checking that the output of muxers is the same as when fifo is used and stand-alone test program which covers behaviour in failure scenarios. libavformat/Makefile | 1 + libavformat/tests/fifo_muxer.c | 447 + tests/Makefile | 1 + tests/fate/fifo-muxer.mak | 20 ++ tests/ref/fate/fifo-muxer-tst | 11 + 5 files changed, 480 insertions(+) create mode 100644 libavformat/tests/fifo_muxer.c create mode 100644 tests/fate/fifo-muxer.mak create mode 100644 tests/ref/fate/fifo-muxer-tst diff --git a/libavformat/Makefile b/libavformat/Makefile index da94ef8..d2a1a27 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -590,6 +590,7 @@ TESTPROGS = seek \ url \ # async \ +TESTPROGS-$(CONFIG_FIFO_MUXER) += fifo_muxer TESTPROGS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh TESTPROGS-$(CONFIG_MOV_MUXER)+= movenc TESTPROGS-$(CONFIG_NETWORK) += noproxy diff --git a/libavformat/tests/fifo_muxer.c b/libavformat/tests/fifo_muxer.c new file mode 100644 index 000..0b5a95e --- /dev/null +++ b/libavformat/tests/fifo_muxer.c @@ -0,0 +1,447 @@ +/* + * FIFO pseudo-muxer + * Copyright (c) 2016 Jan Sebechlebsky + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpeg; if not, write to the Free Software * Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "libavutil/avassert.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" + +#define MAX_TST_PACKETS 128 +#define SLEEPTIME_50_MS 5 +#define SLEEPTIME_10_MS 1 + +/* Implementation of mock muxer to simulate real muxer failures */ + +/* This is structure of data sent in packets to + * failing muxer */ +typedef struct FailingMuxerPacketData { +int ret; /* return value of write_packet call*/ +int recover_after; /* set ret to zero after this number of recovery attempts */ +unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */ +} FailingMuxerPacketData; + + +typedef struct FailingMuxerContext { +AVClass *class; +int write_header_ret; +int write_trailer_ret; +/* If non-zero, summary of processed packets will be printed in deinit */ +uint8_t print_deinit_summary; + +int flush_count; +int pts_written[MAX_TST_PACKETS]; +int pts_written_nr; +} FailingMuxerContext; + +static int failing_write_header(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_header_ret; +} + +static int failing_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ +FailingMuxerContext *ctx = avf->priv_data; +int ret = 0; +if (!pkt) { +ctx->flush_count++; +} else { +FailingMuxerPacketData *data = (FailingMuxerPacketData*) pkt->data; + +if (!data->recover_after) { +data->ret = 0; +} else { +data->recover_after--; +} + +ret = data->ret; + +if (data->sleep_time) { +int64_t slept = 0; +while (slept < data->sleep_time) { +if (ff_check_interrupt(>interrupt_callback)) +return AVERROR_EXIT; +av_usleep(SLEEPTIME_10_MS); +slept += SLEEPTIME_10_MS; +} +} + +if (!ret) { +ctx->pts_written[ctx->pts_written_nr++] = pkt->pts; +av_packet_unref(pkt); +} +} +return ret; +} + +static int failing_write_trailer(AVFormatContext *avf) +{ +FailingMuxerContext *ctx = avf->priv_data; +return ctx->write_trailer_ret; +} + +static void failing_deinit(AVFormatContext *avf) +{ +int i; +FailingMuxerContext *ctx = avf->priv_data; + +if (!ctx->print_deinit_summary) +return; + +printf("flush count: %d\n", ctx->flush_count); +printf("pts seen nr: %d\n", ctx->pts_written_nr); +printf("pts seen: "); +for (i = 0; i < ctx->pts_written_nr; ++i ) { +
[FFmpeg-devel] [PATCH 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan SebechlebskyAdd support for nonblocking calls. Signed-off-by: Jan Sebechlebsky --- libavformat/fifo.c | 70 +- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/libavformat/fifo.c b/libavformat/fifo.c index bc8c973..efd91e3 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -25,6 +25,7 @@ #include "avformat.h" #include "internal.h" #include "pthread.h" +#include "url.h" #define FIFO_DEFAULT_QUEUE_SIZE 60 #define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 16 @@ -77,6 +78,13 @@ typedef struct FifoContext { * from failure or queue overflow */ uint8_t restart_with_keyframe; +/* Whether termination was requested by invoking deinit + * before the thread was finished. Used only in non-blocking + * mode - when AVFMT_FLAG_NONBLOCK is set. */ +volatile uint8_t termination_requested; + +/* Original interrupt callback of the underlying muxer. */ +AVIOInterruptCB orig_interrupt_callback; } FifoContext; typedef struct FifoThreadContext { @@ -110,6 +118,16 @@ typedef struct FifoMessage { AVPacket pkt; } FifoMessage; +static int fifo_interrupt_callback_wrapper(void *arg) +{ +FifoContext *ctx = arg; + +if (ctx->termination_requested) +return 1; + +return ff_check_interrupt(>orig_interrupt_callback); +} + static int fifo_thread_write_header(FifoThreadContext *ctx) { AVFormatContext *avf = ctx->avf; @@ -448,6 +466,8 @@ static void *fifo_consumer_thread(void *data) static int fifo_mux_init(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +AVIOInterruptCB interrupt_cb = {.callback = fifo_interrupt_callback_wrapper, +.opaque = fifo}; AVFormatContext *avf2; int ret = 0, i; @@ -458,7 +478,8 @@ static int fifo_mux_init(AVFormatContext *avf) fifo->avf = avf2; -avf2->interrupt_callback = avf->interrupt_callback; +fifo->orig_interrupt_callback = avf->interrupt_callback; +avf2->interrupt_callback = interrupt_cb; avf2->max_delay = avf->max_delay; ret = av_dict_copy(>metadata, avf->metadata, 0); if (ret < 0) @@ -543,7 +564,7 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) { FifoContext *fifo = avf->priv_data; FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT}; -int ret; +int ret, queue_flags = 0; if (pkt) { av_init_packet(); @@ -552,15 +573,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, , - fifo->drop_pkts_on_overflow ? - AV_THREAD_MESSAGE_NONBLOCK : 0); +if (fifo->drop_pkts_on_overflow || (avf->flags & AVFMT_FLAG_NONBLOCK)) +queue_flags |= AVFMT_FLAG_NONBLOCK; + +ret = av_thread_message_queue_send(fifo->queue, , queue_flags); + if (ret == AVERROR(EAGAIN)) { -uint8_t overflow_set = 0; +uint8_t overflow_set; + +if (avf->flags & AVFMT_FLAG_NONBLOCK) +return ret; /* Queue is full, set fifo->overflow_flag to 1 * to let consumer thread know the queue should * be flushed. */ +overflow_set = 0; pthread_mutex_lock(>overflow_flag_lock); if (!fifo->overflow_flag) fifo->overflow_flag = overflow_set = 1; @@ -588,11 +615,22 @@ static int fifo_write_trailer(AVFormatContext *avf) av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF); -ret = pthread_join( fifo->writer_thread, NULL); -if (ret < 0) { -av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", - av_err2str(AVERROR(ret))); -return AVERROR(ret); +if (!(avf->flags & AVFMT_FLAG_NONBLOCK)) { +ret = pthread_join( fifo->writer_thread, NULL); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", +av_err2str(AVERROR(ret))); +return AVERROR(ret); +} +} else { +ret = pthread_tryjoin_np( fifo->writer_thread, NULL); +if (ret == EBUSY) +return AVERROR(EAGAIN); +if (ret) { +av_log(avf, AV_LOG_ERROR, "pthread_tryjoin_np error: %s\n", + av_err2str(AVERROR(ret))); +return AVERROR(ret); +} } ret = fifo->write_trailer_ret; @@ -603,6 +641,16 @@ static void fifo_deinit(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; +if (avf->flags & AVFMT_FLAG_NONBLOCK) { +int ret; +fifo->termination_requested = 1; +ret = pthread_join( fifo->writer_thread, NULL); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n", + av_err2str(AVERROR(ret))); +} +} +
[FFmpeg-devel] [PATCH 09/11] avformat/mux: Restore stream ts in av_write_packet on EAGAIN
From: Jan Sebechlebskycompute_muxer_pkt_fields() stores the last seen timestamps in stream and produces error if the same timestamp is presented again. This is a problem if muxer works in non-blocking mode and calls av_write_packet repeatedly with the same packet. This patch saves stream fields affected by compute_muxer_pkt_fields and restores them in case AVERROR(EAGAIN) is returned from write_packet, and muxer has AVFMT_FLAG_NONBLOCK flag set. Signed-off-by: Jan Sebechlebsky --- libavformat/mux.c | 14 +- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libavformat/mux.c b/libavformat/mux.c index ef4720a..fdf3fd1 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -880,6 +880,9 @@ static int do_packet_auto_bsf(AVFormatContext *s, AVPacket *pkt) { int av_write_frame(AVFormatContext *s, AVPacket *pkt) { int ret; +#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX +int64_t st_cur_dts_backup, st_priv_pts_val_backup; +#endif ret = prepare_input_packet(s, pkt); if (ret < 0) @@ -907,6 +910,9 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt) return ret; #if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX +st_cur_dts_backup = s->streams[pkt->stream_index]->cur_dts; +st_priv_pts_val_backup = s->streams[pkt->stream_index]->priv_pts->val; + ret = compute_muxer_pkt_fields(s, s->streams[pkt->stream_index], pkt); if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) @@ -914,7 +920,13 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt) #endif ret = write_packet(s, pkt); -if (ret >= 0 && s->pb && s->pb->error < 0) +if (s->flags & AVFMT_FLAG_NONBLOCK && ret == AVERROR(EAGAIN)) { +#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX +s->streams[pkt->stream_index]->cur_dts = st_cur_dts_backup; +s->streams[pkt->stream_index]->priv_pts->val = st_priv_pts_val_backup; +#endif +return ret; +} else if (ret >= 0 && s->pb && s->pb->error < 0) ret = s->pb->error; if (ret >= 0) -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/5] doc/bsfs: Fix bsf options divider in documentation
From: Jan SebechlebskyThe actual implementation uses ':' divider, not '/' as documented. Signed-off-by: Jan Sebechlebsky --- doc/bitstream_filters.texi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index 6c58d02..a85327f 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -18,7 +18,7 @@ comma-separated list of filters, whose parameters follow the filter name after a '='. @example -ffmpeg -i INPUT -c:v copy -bsf:v filter1[=opt1=str1/opt2=str2][,filter2] OUTPUT +ffmpeg -i INPUT -c:v copy -bsf:v filter1[=opt1=str1:opt2=str2][,filter2] OUTPUT @end example Below is a description of the currently available bitstream filters, -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 4/5] avcodec/bsf: Add custom item name func for BSF list
From: Jan SebechlebskyAdd custom item name function for bsf list, which will construct string description of filter chain. This is done using lazy-initialization, so there is no overhead if item name is never accessed. Signed-off-by: Jan Sebechlebsky Conflicts: libavcodec/bsf.c --- This allows to get some text representation of list BSF filter for logging purposes. libavcodec/bsf.c | 30 +- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 3ae0a2b..c2e13f7 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -23,6 +23,7 @@ #include "libavutil/opt.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" +#include "libavutil/bprint.h" #include "avcodec.h" #include "bsf.h" @@ -247,6 +248,7 @@ typedef struct BSFListContext { int idx; // index of currently processed BSF int flushed_idx; // index of BSF being flushed +char * item_name; } BSFListContext; @@ -370,11 +372,37 @@ static void bsf_list_close(AVBSFContext *bsf) for (i = 0; i < lst->ctx_nr; ++i) av_bsf_free(>bsf_lst[i]); av_freep(>bsf_lst); +av_freep(>item_name); +} + +static const char *bsf_list_item_name(void *ctx) +{ +static const char *null_filter_name = "null"; +AVBSFContext *bsf_ctx = ctx; +BSFListContext *lst = bsf_ctx->priv_data; + +if (!lst->ctx_nr) +return null_filter_name; + +if (!lst->item_name) { +int i; +AVBPrint bp; +av_bprint_init(, 16, 128); + +av_bprintf(, "bsf_list("); +for (i = 0; i < lst->ctx_nr; i++) +av_bprintf(, i ? ",%s" : "%s", lst->bsf_lst[i]->filter->name); +av_bprintf(, ")"); + +av_bprint_finalize(, >item_name); +} + +return lst->item_name; } static const AVClass bsf_list_class = { .class_name = "bsf_list", -.item_name = av_default_item_name, +.item_name = bsf_list_item_name, .version = LIBAVUTIL_VERSION_INT, }; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 5/5] avformat/tee: Use BSF list API
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavformat/tee.c | 123 +- 1 file changed, 58 insertions(+), 65 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index b4158e1..1a055cd 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -36,7 +36,7 @@ typedef enum { typedef struct { AVFormatContext *avf; -AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +AVBSFContext **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; @@ -104,46 +104,6 @@ fail: return ret; } -/** - * Parse list of bitstream filters and add them to the list of filters - * pointed to by bsfs. - * - * The list must be specified in the form: - * BSFS ::= BSF[,BSFS] - */ -static int parse_bsfs(void *log_ctx, const char *bsfs_spec, - AVBitStreamFilterContext **bsfs) -{ -char *bsf_name, *buf, *dup, *saveptr; -int ret = 0; - -if (!(dup = buf = av_strdup(bsfs_spec))) -return AVERROR(ENOMEM); - -while (bsf_name = av_strtok(buf, ",", )) { -AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name); - -if (!bsf) { -av_log(log_ctx, AV_LOG_ERROR, - "Cannot initialize bitstream filter with name '%s', " - "unknown filter or internal error happened\n", - bsf_name); -ret = AVERROR_UNKNOWN; -goto end; -} - -/* append bsf context to the list of bsf contexts */ -*bsfs = bsf; -bsfs = >next; - -buf = NULL; -} - -end: -av_free(dup); -return ret; -} - static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) { if (!opt) { @@ -175,14 +135,8 @@ static int close_slave(TeeSlave *tee_slave) ret = av_write_trailer(avf); if (tee_slave->bsfs) { -for (i = 0; i < avf->nb_streams; ++i) { -AVBitStreamFilterContext *bsf_next, *bsf = tee_slave->bsfs[i]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} +for (i = 0; i < avf->nb_streams; ++i) +av_bsf_free(_slave->bsfs[i]); } av_freep(_slave->stream_map); av_freep(_slave->bsfs); @@ -352,7 +306,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) "output '%s', filters will be ignored\n", i, filename); continue; } -ret = parse_bsfs(avf, entry->value, _slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, _slave->bsfs[i]); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s' associated to " @@ -365,6 +319,31 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_dict_set(, entry->key, NULL, 0); } +for (i = 0; i < avf2->nb_streams; i++){ +if (!tee_slave->bsfs[i]) { +/* Add pass-through bitstream filter */ +ret = av_bsf_get_null_filter(_slave->bsfs[i]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Failed to create pass-through bitstream filter: %s\n", + av_err2str(ret)); +goto end; +} +} + +tee_slave->bsfs[i]->time_base_in = avf2->streams[i]->time_base; +ret = avcodec_parameters_copy(tee_slave->bsfs[i]->par_in, + avf2->streams[i]->codecpar); + +ret = av_bsf_init(tee_slave->bsfs[i]); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, +"Failed to initialize bitstream filter(s): %s\n", +av_err2str(ret)); +goto end; +} +} + if (options) { entry = NULL; while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) @@ -389,20 +368,16 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) slave->avf->filename, slave->avf->oformat->name); for (i = 0; i < slave->avf->nb_streams; i++) { AVStream *st = slave->avf->streams[i]; -AVBitStreamFilterContext *bsf = slave->bsfs[i]; +AVBSFContext *bsf = slave->bsfs[i]; +const char * bsf_name; av_log(log_ctx, log_level, "stream:%d codec:%s type:%s", i, avcodec_get_name(st->codecpar->codec_id), av_get_media_type_string(st->codecpar->codec_type)); -if (bsf) { -av_log(log_ctx, log_level, " bsfs:"); -while (bsf) { -av_log(log_ctx, log_level, "%s%s", - bsf->filter->name, bsf->next ? "," : ""); -
[FFmpeg-devel] [PATCH 2/5] avcodec/bsf: Add ff_bsf_get_packet_ref() function
From: Jan SebechlebskyUse of this function can save unnecessary malloc operation in bitstream filter. Signed-off-by: Jan Sebechlebsky --- libavcodec/bsf.c | 16 libavcodec/bsf.h | 11 +++ 2 files changed, 27 insertions(+) diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 8e36861..40fc925 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -220,3 +220,19 @@ int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt) return 0; } + +int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt) +{ +AVBSFInternal *in = ctx->internal; + +if (in->eof) +return AVERROR_EOF; + +if (!ctx->internal->buffer_pkt->data && +!ctx->internal->buffer_pkt->side_data_elems) +return AVERROR(EAGAIN); + +av_packet_move_ref(pkt, ctx->internal->buffer_pkt); + +return 0; +} diff --git a/libavcodec/bsf.h b/libavcodec/bsf.h index 3435df5..af035ee 100644 --- a/libavcodec/bsf.h +++ b/libavcodec/bsf.h @@ -28,6 +28,17 @@ */ int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt); +/** + * Called by bitstream filters to get packet for filtering. + * The reference to packet is moved to provided packet structure. + * + * @param ctx pointer to AVBSFContext of filter + * @param pkt pointer to packet to move reference to + * + * @return 0>= on success, negative AVERROR in case of failure + */ +int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt); + const AVClass *ff_bsf_child_class_next(const AVClass *prev); #endif /* AVCODEC_BSF_H */ -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 3/5] avcodec/bsf: Add list BSF API
From: Jan Sebechlebsky--- libavcodec/avcodec.h | 74 ++ libavcodec/bsf.c | 284 +++ 2 files changed, 358 insertions(+) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 36f7935..39106ee 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -5949,6 +5949,80 @@ void av_bsf_free(AVBSFContext **ctx); */ const AVClass *av_bsf_get_class(void); +/** + * Structure for chain/list of bitstream filters. + * Empty list can be allocated by av_bsf_list_alloc(). + */ +typedef struct AVBSFList AVBSFList; + +/** + * Allocate empty list of bitstream filters. + * The list must be later freed by av_bsf_list_free() + * or finalized by av_bsf_list_finalize(). + * + * @return pointer to AVBSFList on success, NULL in case of failure + */ +AVBSFList *av_bsf_list_alloc(void); + +/** + * Free list of bitstream filters. + * + * @param lst pointer to pointer returned by av_bsf_list_alloc() + */ +void av_bsf_list_free(AVBSFList **lst); + +/** + * Append bitstream filter to the list of bitstream filters. + * + * @param lst list to append to + * @param bsf AVBSFContext to be appended + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append(AVBSFList *lst, AVBSFContext *bsf); + +/** + * Finalize list of bitstream filters. + * + * This function will transform AVBSFList to single AVBSFContext, + * so the whole chain of bitstream filters can be treated as single filter + * freshly allocated by av_bsf_alloc(). + * The AVBSFList structure is destroyed after this call and must not + * be accessed. + * + * @param lst AVBSFList structure to be transformed + * @param bsf[out] pointer to be set to newly created AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf); + +/** + * Parse string describing list of bitstream filters and create single + * AVBSFContext describing the whole chain of bitstream filters. + * Resulting AVBSFContext can be treated as any other AVBSFContext freshly + * allocated by av_bsf_alloc(). + * + * @param str string describing chain of bitstream filters in format + * bsf1[=opt1=val1:opt2=val2][,bsf2] + * @param bsf[out] pointer to be set to newly created AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf); + +/** + * Get null/pass-through bitstream filter. + * + * @param bsf[out] pointer to be set to new instance of pass-through + * bitstream filter + * + * @return + */ +int av_bsf_get_null_filter(AVBSFContext **bsf); + /* memory */ /** diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 40fc925..3ae0a2b 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -22,6 +22,7 @@ #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/avassert.h" +#include "libavutil/avstring.h" #include "avcodec.h" #include "bsf.h" @@ -236,3 +237,286 @@ int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt) return 0; } + +typedef struct BSFListContext { +const AVClass *class; + +AVBSFContext **bsf_lst; +int ctx_nr; + +int idx; // index of currently processed BSF +int flushed_idx; // index of BSF being flushed + +} BSFListContext; + + +static int bsf_list_init(AVBSFContext *bsf) +{ +BSFListContext *lst = bsf->priv_data; +int ret, i; +const AVCodecParameters *cod_par = bsf->par_in; +AVRational tb = bsf->time_base_in; + +for (i = 0; i < lst->ctx_nr; ++i) { +ret = avcodec_parameters_copy(lst->bsf_lst[i]->par_in, cod_par); +if (ret < 0) +goto fail; + +lst->bsf_lst[i]->time_base_in = tb; + +ret = av_bsf_init(lst->bsf_lst[i]); +if (ret < 0) +goto fail; + +cod_par = lst->bsf_lst[i]->par_out; +tb = lst->bsf_lst[i]->time_base_out; +} + +bsf->time_base_out = tb; +ret = avcodec_parameters_copy(bsf->par_out, cod_par); + +fail: +return ret; +} + +static int bsf_list_flush(AVBSFContext *bsf, AVPacket *out) +{ +BSFListContext *lst = bsf->priv_data; +int ret; + +if (lst->flushed_idx == lst->ctx_nr) +return AVERROR_EOF; + +while (1) { +if (lst->idx > lst->flushed_idx) { +ret = av_bsf_receive_packet(lst->bsf_lst[lst->idx-1], out); +if (ret == AVERROR(EAGAIN)) { +ret = 0; +lst->idx--; +continue; +} else if (ret == AVERROR_EOF) { +/* filter idx-1 is done, let's flush filter idx */ +lst->flushed_idx = lst->idx; +continue; +} else if (ret < 0) { +/*
[FFmpeg-devel] [PATCH v3 2/2] avcodec/bsf: Forbid packet without payload in av_bsf_send_packet
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavcodec/avcodec.h | 3 ++- libavcodec/bsf.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index ca8dba8..36f7935 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -5898,7 +5898,8 @@ int av_bsf_init(AVBSFContext *ctx); * av_bsf_receive_packet() repeatedly until it returns AVERROR(EAGAIN) or * AVERROR_EOF. * - * @param pkt the packet to filter. The bitstream filter will take ownership of + * @param pkt the packet to filter. pkt must contain some payload (i.e data or + * side data must be present in pkt). The bitstream filter will take ownership of * the packet and reset the contents of pkt. pkt is not touched if an error occurs. * This parameter may be NULL, which signals the end of the stream (i.e. no more * packets will be sent). That will cause the filter to output any packets it diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 9b9ada7..8e36861 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -21,6 +21,7 @@ #include "libavutil/log.h" #include "libavutil/mem.h" #include "libavutil/opt.h" +#include "libavutil/avassert.h" #include "avcodec.h" #include "bsf.h" @@ -177,6 +178,8 @@ int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) return 0; } +av_assert0(pkt->data || pkt->side_data); + if (ctx->internal->eof) { av_log(ctx, AV_LOG_ERROR, "A non-NULL packet sent after an EOF.\n"); return AVERROR(EINVAL); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 1/2] avcodec/bsf: Set EOF flag only in pkt == NULL
From: Jan SebechlebskySet BSF EOF flag only if pkt == NULL in av_bsf_send_packet(). Signed-off-by: Jan Sebechlebsky --- libavcodec/bsf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 88b7f29..9b9ada7 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -172,7 +172,7 @@ int av_bsf_init(AVBSFContext *ctx) int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) { -if (!pkt || !pkt->data) { +if (!pkt) { ctx->internal->eof = 1; return 0; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] MAINTAINERS: Add myself as maintainer of fifo muxer
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6d4c9f9..0e66170 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -164,6 +164,7 @@ Codecs: exif.c, exif.hThilo Borgmann ffv1* Michael Niedermayer ffwavesynth.c Nicolas George + fifo.cJan Sebechlebsky flicvideo.c Mike Melanson g722.cMartin Storsjo g726.cRoman Shaposhnik -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 1/2] libavformat: Add fifo pseudo-muxer
From: Jan SebechlebskyThe fifo pseudo-muxer allows to separate encoder from the actual output by using a first-in-first-out queue and running actual muxer asynchronously in separate thread. It can be configured to attempt transparent recovery of output on failure. Signed-off-by: Jan Sebechlebsky --- Changelog| 1 + configure| 1 + doc/muxers.texi | 90 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 666 +++ libavformat/version.h| 2 +- 7 files changed, 761 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c diff --git a/Changelog b/Changelog index 479f164..d7d0056 100644 --- a/Changelog +++ b/Changelog @@ -10,6 +10,7 @@ version : - curves filter doesn't automatically insert points at x=0 and x=1 anymore - 16-bit support in curves filter - 16-bit support in selectivecolor filter +- fifo muxer version 3.1: diff --git a/configure b/configure index 1b41303..903205b 100755 --- a/configure +++ b/configure @@ -2832,6 +2832,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index 5873269..e2bc290 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1436,6 +1436,96 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows to separate encoding from any other muxer by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and output to +several destinations with different reliability/writing speed/latency. + +The behavior of fifo muxer in case of failure can be configured: +@itemize @bullet + +@item +output can be transparently restarted with configurable delay between retries +based on real time or time of the processed stream. + +@item +encoding can be blocked during temporary failure, or continue transparently +dropping packets in case fifo queue fills up. + +@end itemize + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item drop_pkts_on_overflow @var{bool} +If set to 1 (true), in case the fifo queue fills up, packets will be dropped +rather than blocking the encoder. This allows to continue streaming without +delaying the output, at the cost of ommiting part of the stream. By default +this option is set to 0 (false), so in such cases the encoder will be blocked +until the muxer processes some of the packets and none of them is lost. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. Unlimited if set to zero. Default value is 16. + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +s@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least recovery_wait_time +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain errors the recovery is not attempted even when @ref{attempt_recovery} +is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server, continue processing the stream at real-time +rate even in case of temporary failure (network outage) and attempt to recover +streaming every second indefinitely. +@example +ffmpeg -re -i ... -c:v libx264 -c:a mp2 -f fifo -fifo_format
[FFmpeg-devel] [PATCH 0/2][GSoC] Add fifo pseudo-muxer
From: Jan SebechlebskyHello, I am sending next version of fifo pseudo-muxer. I will send patchset adding AVFMT_FLAG_NONBLOCK support requested by Nicolas later - it turned out that there are some more things to solve (av_write_frame does not support repeated calls in current state) and test. Regards, Jan Jan Sebechlebsky (2): libavformat: Add fifo pseudo-muxer MAINTAINERS: Add myself as maintainer of fifo muxer Changelog| 1 + MAINTAINERS | 1 + configure| 1 + doc/muxers.texi | 90 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 666 +++ libavformat/version.h| 2 +- 8 files changed, 762 insertions(+), 1 deletion(-) create mode 100644 libavformat/fifo.c -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avcodec/bsf: Set EOF flag only if pkt == NULL
From: Jan SebechlebskySet BSF EOF flag only if pkt == NULL in av_bsf_send_packet(). Signed-off-by: Jan Sebechlebsky --- I agree, it seems cleaner that way. Thanks, please apply this version of patch then and ignore the patch changing the comment. Regards, Jan libavcodec/bsf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 88b7f29..9b9ada7 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -172,7 +172,7 @@ int av_bsf_init(AVBSFContext *ctx) int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) { -if (!pkt || !pkt->data) { +if (!pkt) { ctx->internal->eof = 1; return 0; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avcodec/avcodec.h: Change av_bsf_send_packet() comment
From: Jan SebechlebskySpecify av_bsf_packet() behaviour in case that the packet does not contain any data more precisely in the comment. Signed-off-by: Jan Sebechlebsky --- libavcodec/avcodec.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index ca8dba8..09ca734 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -5900,9 +5900,9 @@ int av_bsf_init(AVBSFContext *ctx); * * @param pkt the packet to filter. The bitstream filter will take ownership of * the packet and reset the contents of pkt. pkt is not touched if an error occurs. - * This parameter may be NULL, which signals the end of the stream (i.e. no more - * packets will be sent). That will cause the filter to output any packets it - * may have buffered internally. + * If the parameter is NULL, or packet does not contain any data or side data, + * it signals the end of the stream (i.e. no more packets will be sent). + * That will cause the filter to output any packets it may have buffered internally. * * @return 0 on success, a negative AVERROR on error. */ -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avcodec/bsf: Check side data when setting BSF EOF flag.
From: Jan SebechlebskySet BSF EOF flag only if pkt == NULL or both data and side data are not present in packet. Signed-off-by: Jan Sebechlebsky --- I believe that side data should be checked too, and EOF flag set only when both data and side data are not present. I was testing new list BSF API I was working on, and with simple pass-though (empty list) BSF filter, some tests were failing because packets containing only side data were taken as EOF signal for bitsteam filter. Also, I've added comment about this behaviour in next patch. libavcodec/bsf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 88b7f29..94f0122 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -172,7 +172,7 @@ int av_bsf_init(AVBSFContext *ctx) int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) { -if (!pkt || !pkt->data) { +if (!pkt || (!pkt->data && !pkt->side_data_elems)) { ctx->internal->eof = 1; return 0; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avcodec/mpeg4_unpack_bframes_bsf: Check av_packet_from_data() return value
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavcodec/mpeg4_unpack_bframes_bsf.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libavcodec/mpeg4_unpack_bframes_bsf.c b/libavcodec/mpeg4_unpack_bframes_bsf.c index aee8ccb..e227f58 100644 --- a/libavcodec/mpeg4_unpack_bframes_bsf.c +++ b/libavcodec/mpeg4_unpack_bframes_bsf.c @@ -126,7 +126,11 @@ static int mpeg4_unpack_bframes_filter(AVBSFContext *ctx, AVPacket *out) return ret; } -av_packet_from_data(out, s->b_frame_buf, s->b_frame_buf_size); +ret = av_packet_from_data(out, s->b_frame_buf, s->b_frame_buf_size); +if (ret < 0) { +av_packet_free(); +return ret; +} if (in->size <= MAX_NVOP_SIZE) { /* N-VOP */ av_log(ctx, AV_LOG_DEBUG, "Skipping N-VOP.\n"); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avcodec/mpeg4_unpack_bframes_bsf: Copy packet props
From: Jan Sebechlebskympeg4_unpack_bframes_bsf bitstream filters constructs resulting packet using av_packet_from_data() function. This function however modifies only buffer (data) and leaves other fields untouched, so the content of other fields of the output packet is undefined. It is working with old BSF API, since old API filters just data and the packet fields are copied in av_apply_bitstream_filters from input packet. This change fixes the behaviour for the new BSF API. Signed-off-by: Jan Sebechlebsky --- libavcodec/mpeg4_unpack_bframes_bsf.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/libavcodec/mpeg4_unpack_bframes_bsf.c b/libavcodec/mpeg4_unpack_bframes_bsf.c index 0615621..aee8ccb 100644 --- a/libavcodec/mpeg4_unpack_bframes_bsf.c +++ b/libavcodec/mpeg4_unpack_bframes_bsf.c @@ -120,6 +120,12 @@ static int mpeg4_unpack_bframes_filter(AVBSFContext *ctx, AVPacket *out) if (nb_vop == 1 && s->b_frame_buf) { /* use frame from BSFContext */ +ret = av_packet_copy_props(out, in); +if (ret < 0) { +av_packet_free(); +return ret; +} + av_packet_from_data(out, s->b_frame_buf, s->b_frame_buf_size); if (in->size <= MAX_NVOP_SIZE) { /* N-VOP */ -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 3/7] avformat/tee: Rescale ts using av_packet_rescale_ts
From: Jan SebechlebskyThis ensures that AV_NOPTS_VALUE value is handled correctly. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index daddba5..b4158e1 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -543,9 +543,7 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) } 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); +av_packet_rescale_ts(, tb, tb2); pkt2.stream_index = s2; if ((ret = av_apply_bitstream_filters(avf2->streams[s2]->codec, , -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 03/10] avformat/tee: Support flushing by writing NULL pkt
From: Jan SebechlebskyThis will add support for flushing by writing NULL packet to the tee muxer, which propagates the action to slave muxers as expected. Signed-off-by: Jan Sebechlebsky --- Unfortunately, I've realized that I've forgotten to set AVFMT_ALLOW_FLUSH flag to the tee muxer to make it flushable. I've also changed commit message, because crash without AVFMT_ALLOW_FLUSH would not happen if write_packet is not called directly with NULL argument. This version should be allright. libavformat/tee.c | 13 - 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index c276a37..e750752 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -520,6 +520,17 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) if (!(avf2 = tee->slaves[i].avf)) continue; +/* Flush slave if pkt is NULL*/ +if (!pkt) { +ret = av_interleaved_write_frame(avf2, NULL); +if (ret < 0) { +ret = tee_process_slave_failure(avf, i, ret); +if (!ret_all && ret < 0) +ret_all = ret; +} +continue; +} + s = pkt->stream_index; s2 = tee->slaves[i].stream_map[s]; if (s2 < 0) @@ -557,5 +568,5 @@ AVOutputFormat ff_tee_muxer = { .write_trailer = tee_write_trailer, .write_packet = tee_write_packet, .priv_class= _muxer_class, -.flags = AVFMT_NOFILE, +.flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, }; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 5/7] avformat/utils: Add ff_format_output_open() function
From: Jan SebechlebskyAdd ff_format_output_open utility function to wrap io_open callback of AVFormatContext structure. Signed-off-by: Jan Sebechlebsky --- libavformat/internal.h | 10 ++ libavformat/utils.c| 10 ++ 2 files changed, 20 insertions(+) diff --git a/libavformat/internal.h b/libavformat/internal.h index 1b44bea..0119749 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -572,6 +572,16 @@ int ffio_open2_wrapper(struct AVFormatContext *s, AVIOContext **pb, const char * */ #define FFERROR_REDO FFERRTAG('R','E','D','O') +/** + * Utility function to open IO stream of output format. + * + * @param s AVFormatContext + * @param url URL or file name to open for writing + * @options optional options which will be passed to io_open callback + * @return >=0 on success, negative AVERROR in case of failure + */ +int ff_format_output_open(AVFormatContext *s, const char *url, AVDictionary **options); + /* * A wrapper around AVFormatContext.io_close that should be used * instead of calling the pointer directly. diff --git a/libavformat/utils.c b/libavformat/utils.c index 8c16374..d728ca3 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -5168,6 +5168,16 @@ int av_apply_bitstream_filters(AVCodecContext *codec, AVPacket *pkt, FF_ENABLE_DEPRECATION_WARNINGS #endif +int ff_format_output_open(AVFormatContext *s, const char *url, AVDictionary **options) +{ +if (!s->oformat) +return AVERROR(EINVAL); + +if (!(s->oformat->flags & AVFMT_NOFILE)) +return s->io_open(s, >pb, url, AVIO_FLAG_WRITE, options); +return 0; +} + void ff_format_io_close(AVFormatContext *s, AVIOContext **pb) { if (*pb) -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/7] avformat/utils: Add ff_stream_encode_params_copy()
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavformat/internal.h | 9 libavformat/utils.c| 56 ++ 2 files changed, 65 insertions(+) diff --git a/libavformat/internal.h b/libavformat/internal.h index 647ad65..1b44bea 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -491,6 +491,15 @@ int ff_generate_avci_extradata(AVStream *st); int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args); /** + * Copy encoding parameters from source to destination stream + * + * @param dst pointer to destination AVStream + * @param src pointer to source AVStream + * @return >=0 on success, AVERROR code on error + */ +int ff_stream_encode_params_copy(AVStream *dst, const AVStream *src); + +/** * Wrap errno on rename() error. * * @param oldpath source path diff --git a/libavformat/utils.c b/libavformat/utils.c index d2a709c..8c16374 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -3951,6 +3951,62 @@ int av_read_pause(AVFormatContext *s) return AVERROR(ENOSYS); } +int ff_stream_encode_params_copy(AVStream *dst, const AVStream *src) +{ +int ret, i; + +dst->id = src->id; +dst->time_base = src->time_base; +dst->nb_frames = src->nb_frames; +dst->disposition = src->disposition; +dst->sample_aspect_ratio = src->sample_aspect_ratio; +dst->avg_frame_rate = src->avg_frame_rate; +dst->r_frame_rate= src->r_frame_rate; + +ret = av_dict_copy(>metadata, src->metadata, 0); +if (ret < 0) +return ret; + +ret = avcodec_parameters_copy(dst->codecpar, src->codecpar); +if (ret < 0) +return ret; + +/* Free existing side data*/ +for (i = 0; i < dst->nb_side_data; i++) +av_free(dst->side_data[i].data); +av_freep(>side_data); +dst->nb_side_data = 0; + +/* Copy side data if present */ +if (src->nb_side_data) { +dst->side_data = av_mallocz_array(src->nb_side_data, + sizeof(AVPacketSideData)); +if (!dst->side_data) +return AVERROR(ENOMEM); +dst->nb_side_data = src->nb_side_data; + +for (i = 0; i < src->nb_side_data; i++) { +uint8_t *data = av_memdup(src->side_data[i].data, + src->side_data[i].size); +if (!data) +return AVERROR(ENOMEM); +dst->side_data[i].type = src->side_data[i].type; +dst->side_data[i].size = src->side_data[i].size; +dst->side_data[i].data = data; +} +} + +av_freep(>recommended_encoder_configuration); +if (src->recommended_encoder_configuration) { +const char *conf_str = src->recommended_encoder_configuration; +dst->recommended_encoder_configuration = av_strdup(conf_str); +if (!dst->recommended_encoder_configuration) +return AVERROR(ENOMEM); +} + +return 0; +} + static void free_stream(AVStream **pst) { AVStream *st = *pst; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 6/7] avformat/tee: Use ff_format_output_open() function
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavformat/tee.c | 11 +-- 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 996d64d..c60a77f 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -300,12 +300,11 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) goto end; } -if (!(avf2->oformat->flags & AVFMT_NOFILE)) { -if ((ret = avf2->io_open(avf2, >pb, filename, AVIO_FLAG_WRITE, NULL)) < 0) { -av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n", - slave, av_err2str(ret)); -goto end; -} +ret = ff_format_output_open(avf2, filename, NULL); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n", slave, + av_err2str(ret)); +goto end; } if ((ret = avformat_write_header(avf2, )) < 0) { -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 7/7] libavformat: Add FIFO pseudo-muxer
From: Jan SebechlebskyThe fifo pseudo-muxer allows to separate encoder from the actual output by using a first-in-first-out queue and running actual muxer asynchronously in separate thread. It can be configured to attempt transparent recovery of output on failure. Signed-off-by: Jan Sebechlebsky --- configure| 1 + doc/muxers.texi | 80 ++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 666 +++ 5 files changed, 749 insertions(+) create mode 100644 libavformat/fifo.c diff --git a/configure b/configure index 126d0d6..b71c75f 100755 --- a/configure +++ b/configure @@ -2826,6 +2826,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index c2ca0ba..7ca14f6 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1408,6 +1408,86 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows to separate encoding from any other muxer by using +first-in-first-out queue and running the actual muxer in a separate thread. This +is especially useful in combination with the @ref{tee} muxer and output to +several destinations with different reliability/writing speed/latency. + +The fifo muxer can operate in regular or non-blocking mode. This determines the +behaviour in case the queue fills up. In regular mode the encoding is blocked +until the muxer processes some of the packets from queue; in non-blocking mode +the packets are thrown away rather than blocking the encoder (this might be +useful in real-time streaming scenarios). + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets). Default value is 60. + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item block_on_overflow @var{bool} +If set to 0 (false), non-blocking mode will be used and in case the queue fills +up, packets will be dropped. By default this option is set to 1 (true), so in +case of queue overflow the encoder will be blocked until the muxer processes +some of the packets. + +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option set to 0 (false). + +@item max_recovery_attempts +Sets maximum number of successive unsucessful recovery attempts after which +the output fails permanently. Unlimited if set to zero. Default value is 16. + +@item recovery_wait_time @var{duration} +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. Default value is 5 seconds. + +s@item recovery_wait_streamtime @var{bool} +If set to 0 (false), the real time is used when waiting for the recovery +attempt (i.e. the recovery will be attempted after at least +recovery_wait_time seconds). +If set to 1 (true), the time of the processed stream is taken into account +instead (i.e. the recovery will be attempted after at least recovery_wait_time +seconds of the stream is omitted). +By default, this option is set to 0 (false). + +@item recover_any_error @var{bool} +If set to 1 (true), recovery will be attempted regardless of type of the error +causing the failure. By default this option is set to 0 (false) and in case of +certain errors the recovery is not attempted even when @ref{attempt_recovery} +is set to 1. + +@item restart_with_keyframe @var{bool} +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. This option is set to 0 (false) by default. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server using non-blocking mode and recover automatically +in case failure happens (for example the network becomes unavailable for a moment). +@example +ffmpeg -re -i ... -c:v libx264 -c:a mp2 -f fifo -fifo_format flv -map 0:v -map 0:a + -block_on_overflow 0 -attempt_recovery 1 rtmp://example.com/live/stream_name +@end example + +@end itemize + @section tee The tee muxer can be used to write the same data to several files or any diff --git a/libavformat/Makefile b/libavformat/Makefile index c49f9de..42fb9be 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -162,6 +162,7 @@ OBJS-$(CONFIG_FFM_DEMUXER) += ffmdec.o OBJS-$(CONFIG_FFM_MUXER) += ffmenc.o OBJS-$(CONFIG_FFMETADATA_DEMUXER)+= ffmetadec.o
[FFmpeg-devel] [PATCH 3/7] avformat/tee: Handle AV_NOPTS_VALUE correctly
From: Jan SebechlebskyDo not rescale pts and dts if they are set to AV_NOPTS_VALUE. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index c276a37..a56d449 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -533,8 +533,10 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) } 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); +if (pkt->pts != AV_NOPTS_VALUE) +pkt2.pts = av_rescale_q(pkt->pts, tb, tb2); +if (pkt->dts != AV_NOPTS_VALUE) +pkt2.dts = av_rescale_q(pkt->dts, tb, tb2); pkt2.duration = av_rescale_q(pkt->duration, tb, tb2); pkt2.stream_index = s2; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 4/7] avformat/tee: Support flushing by writing NULL pkt
From: Jan SebechlebskyThis will fix crash when caller attempts to flush by calling write_packet with NULL packet pointer and flushes slaves as expected. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 11 +++ 1 file changed, 11 insertions(+) diff --git a/libavformat/tee.c b/libavformat/tee.c index a56d449..996d64d 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -520,6 +520,17 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) if (!(avf2 = tee->slaves[i].avf)) continue; +/* Flush slave if pkt is NULL*/ +if (!pkt) { +ret = av_interleaved_write_frame(avf2, NULL); +if (ret < 0) { +ret = tee_process_slave_failure(avf, i, ret); +if (!ret_all && ret < 0) +ret_all = ret; +} +continue; +} + s = pkt->stream_index; s2 = tee->slaves[i].stream_map[s]; if (s2 < 0) -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 4/4] avformat/tee: Support flushing by writing NULL pkt
From: Jan SebechlebskyThis will fix crash when caller attempts to flush by calling write_packet with NULL packet pointer and flushes slaves as expected. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 11 +++ 1 file changed, 11 insertions(+) diff --git a/libavformat/tee.c b/libavformat/tee.c index 840bee3..5b0fd41 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -520,6 +520,17 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) if (!(avf2 = tee->slaves[i].avf)) continue; +/* Flush slave if pkt is NULL*/ +if (!pkt) { +ret = av_interleaved_write_frame(avf2, NULL); +if (ret < 0) { +ret = tee_process_slave_failure(avf, i, ret); +if (!ret_all && ret < 0) +ret_all = ret; +} +continue; +} + s = pkt->stream_index; s2 = tee->slaves[i].stream_map[s]; if (s2 < 0) -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/4] avformat/utils: Add ff_stream_encode_params_copy()
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- Nicolas - this is the second of the utility functions you asked for in reply to my fifo pseudo-muxer patch. I've tried to search for references to AVStream fields in muxers for the fields I wasn't are used, so hopefully this should copy everything which might be used and needed. libavformat/internal.h | 9 libavformat/utils.c| 56 ++ 2 files changed, 65 insertions(+) diff --git a/libavformat/internal.h b/libavformat/internal.h index 647ad65..1b44bea 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -491,6 +491,15 @@ int ff_generate_avci_extradata(AVStream *st); int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args); /** + * Copy encoding parameters from source to destination stream + * + * @param dst pointer to destination AVStream + * @param src pointer to source AVStream + * @return >=0 on success, AVERROR code on error + */ +int ff_stream_encode_params_copy(AVStream *dst, const AVStream *src); + +/** * Wrap errno on rename() error. * * @param oldpath source path diff --git a/libavformat/utils.c b/libavformat/utils.c index 6f343f2..ff57f57 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -3942,6 +3942,62 @@ int av_read_pause(AVFormatContext *s) return AVERROR(ENOSYS); } +int ff_stream_encode_params_copy(AVStream *dst, const AVStream *src) +{ +int ret, i; + +dst->id = src->id; +dst->time_base = src->time_base; +dst->nb_frames = src->nb_frames; +dst->disposition = src->disposition; +dst->sample_aspect_ratio = src->sample_aspect_ratio; +dst->avg_frame_rate = src->avg_frame_rate; +dst->r_frame_rate= src->r_frame_rate; + +ret = av_dict_copy(>metadata, src->metadata, 0); +if (ret < 0) +return ret; + +ret = avcodec_parameters_copy(dst->codecpar, src->codecpar); +if (ret < 0) +return ret; + +/* Free existing side data*/ +for (i = 0; i < dst->nb_side_data; i++) +av_free(dst->side_data[i].data); +av_freep(>side_data); +dst->nb_side_data = 0; + +/* Copy side data if present */ +if (src->nb_side_data) { +dst->side_data = av_mallocz_array(src->nb_side_data, + sizeof(AVPacketSideData)); +if (!dst->side_data) +return AVERROR(ENOMEM); +dst->nb_side_data = src->nb_side_data; + +for (i = 0; i < src->nb_side_data; i++) { +uint8_t *data = av_memdup(src->side_data[i].data, + src->side_data[i].size); +if (!data) +return AVERROR(ENOMEM); +dst->side_data[i].type = src->side_data[i].type; +dst->side_data[i].size = src->side_data[i].size; +dst->side_data[i].data = data; +} +} + +av_freep(>recommended_encoder_configuration); +if (src->recommended_encoder_configuration) { +const char *conf_str = src->recommended_encoder_configuration; +dst->recommended_encoder_configuration = av_strdup(conf_str); +if (!dst->recommended_encoder_configuration) +return AVERROR(ENOMEM); +} + +return 0; +} + static void free_stream(AVStream **pst) { AVStream *st = *pst; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 3/4] avformat/tee: Handle AV_NOPTS_VALUE correctly
From: Jan SebechlebskyDo not rescale pts and dts if they are set to AV_NOPTS_VALUE. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index c276a37..840bee3 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -533,8 +533,10 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) } 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); +if (pkt->pts != AV_NOPTS_VALUE) +pkt2.pts = av_rescale_q(pkt->pts, tb, tb2); +if (pkt->dts != AV_NOPTS_VALUE) +pkt2.dts = av_rescale_q(pkt->dts, tb, tb2); pkt2.duration = av_rescale_q(pkt->duration, tb, tb2); pkt2.stream_index = s2; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/4] avformat/tee: Use ff_stream_encode_params_copy()
From: Jan SebechlebskyUse ff_stream_encode_params_copy() to copy encoding-related fields (parameters) of stream. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 14 +++--- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 9d0a4cb..c276a37 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -294,17 +294,9 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) ret = AVERROR(ENOMEM); goto end; } -st2->id = st->id; -st2->r_frame_rate= st->r_frame_rate; -st2->time_base = st->time_base; -st2->start_time = st->start_time; -st2->duration= st->duration; -st2->nb_frames = st->nb_frames; -st2->disposition = st->disposition; -st2->sample_aspect_ratio = st->sample_aspect_ratio; -st2->avg_frame_rate = st->avg_frame_rate; -av_dict_copy(>metadata, st->metadata, 0); -if ((ret = avcodec_parameters_copy(st2->codecpar, st->codecpar)) < 0) + +ret = ff_stream_encode_params_copy(st2, st); +if (ret < 0) goto end; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] libavformat: Add FIFO pseudo-muxer
From: Jan SebechlebskyFIFO pseudo-muxer allows to separate decoder from the actual output by using first-in-first-out queue and running actual muxer asynchronously in separate thread. It can be configured to attempt transparent recovery of output on failure. Signed-off-by: Jan Sebechlebsky --- configure| 1 + doc/muxers.texi | 77 ++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 657 +++ 5 files changed, 737 insertions(+) create mode 100644 libavformat/fifo.c diff --git a/configure b/configure index 007c953..eacda09 100755 --- a/configure +++ b/configure @@ -2826,6 +2826,7 @@ dv_muxer_select="dvprofile" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" f4v_muxer_select="mov_muxer" +fifo_muxer_deps="pthreads" flac_demuxer_select="flac_parser" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" diff --git a/doc/muxers.texi b/doc/muxers.texi index c2ca0ba..e545bc7 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1408,6 +1408,83 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@section fifo + +The fifo pseudo-muxer allows to separate encoding from any other muxer +by using first-in-first-out queue and running the actual muxer in separate +thread. This is especially useful in combination with tee muxer +and output to several destinations with different reliability/writing speed/latency. + +The fifo muxer can operate in regular or fully non-blocking mode. This determines +the behaviour in case the queue fills up. In regular mode the encoding is blocked +until muxer processes some of the packets from queue, in non-blocking mode the packets +are thrown away rather than blocking the encoder (this might be useful in real-time +streaming scenario). + +@table @option + +@item fifo_format +Specify the format name. Useful if it cannot be guessed from the +output name suffix. + +@item queue_size +Specify size of the queue (number of packets) + +@item format_opts +Specify format options for the underlying muxer. Muxer options can be specified +as a list of @var{key}=@var{value} pairs separated by ':'. + +@item block_on_overflow 0|1 +If set to 0, fully non-blocking mode will be used and in case the queue +fills up, packets will be dropped. By default this option is set to 1, +so in case of queue overflow the encoder will be block until muxer +processes some of the packets. + +@item attempt_recovery 0|1 +If failure happens, attempt to recover the output. This is especially useful +when used with network output, allows to restart streaming transparently. +By default this option is turned off. + +@item max_recovery_attempts +Sets maximal number of successive unsucessfull recovery attempts after which +the output fails permanently. Unlimited if set to zero. + +@item recovery_wait_time +Waiting time before the next recovery attempt after previous unsuccessfull +recovery attempt. + +@item recovery_wait_streamtime 0|1 +If set to 0 (default), the real time is used when waiting for the recovery attempt +(i.e. the recovery will be attempted after at least recovery_wait_time seconds). +If set to 1, the time of the processed stream is taken into account instead +(i.e. the recovery will be attempted after at least recovery_wait_time seconds +of the stream is ommited). + +@item recover_any_error 0|1 +If set to 1, recovery will be attempted regardless of type of the error causing +the failure (by default, in case of certain errors the recovery is not attempted +even when attempt_recovery is on). + +@item restart_with_keyframe 0|1 +Specify whether to wait for the keyframe after recovering from +queue overflow or failure. + +@end table + +@subsection Examples + +@itemize + +@item +Stream something to rtmp server using non-blocking mode and recover automatically +in case failure happens (for example the network becomes unavailable for a moment). +@example +ffmpeg -re -i ... -c:v libx264 -c:a mp2 -f fifo -fifo_format flv -map 0:v -map 0:a + -block_on_overflow 0 -attempt_recovery 1 rtmp://example.com/live/stream_name +@end example + +@end itemize + @section tee The tee muxer can be used to write the same data to several files or any diff --git a/libavformat/Makefile b/libavformat/Makefile index c49f9de..42fb9be 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -162,6 +162,7 @@ OBJS-$(CONFIG_FFM_DEMUXER) += ffmdec.o OBJS-$(CONFIG_FFM_MUXER) += ffmenc.o OBJS-$(CONFIG_FFMETADATA_DEMUXER)+= ffmetadec.o OBJS-$(CONFIG_FFMETADATA_MUXER) += ffmetaenc.o +OBJS-$(CONFIG_FIFO_MUXER)+= fifo.o OBJS-$(CONFIG_FILMSTRIP_DEMUXER) += filmstripdec.o OBJS-$(CONFIG_FILMSTRIP_MUXER) += filmstripenc.o OBJS-$(CONFIG_FLAC_DEMUXER) += flacdec.o rawdec.o \ diff --git
[FFmpeg-devel] [GSoC][PATCH] Add FIFO muxer
From: Jan SebechlebskyHello, I'm sending the patch with implementation of discussed FIFO pseudo-muxer which is part of my GSoC project. The muxer allows to separate decoder from the actual muxer (or several muxers when combined with tee). Trasparent recovery from failure is also implemented. I'll appreciate feedback Best regards, Jan Jan Sebechlebsky (1): libavformat: Add FIFO pseudo-muxer configure| 1 + doc/muxers.texi | 77 ++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/fifo.c | 657 +++ 5 files changed, 737 insertions(+) create mode 100644 libavformat/fifo.c -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3] avformat/tee: Support arbitrary number of slaves
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- You're right. Should be fixed here. libavformat/tee.c | 26 -- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 806beaa..9d0a4cb 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -27,8 +27,6 @@ #include "avformat.h" #include "avio_internal.h" -#define MAX_SLAVES 16 - typedef enum { ON_SLAVE_FAILURE_ABORT = 1, ON_SLAVE_FAILURE_IGNORE = 2 @@ -52,7 +50,7 @@ typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; unsigned nb_alive; -TeeSlave slaves[MAX_SLAVES]; +TeeSlave *slaves; } TeeContext; static const char *const slave_delim = "|"; @@ -203,6 +201,7 @@ static void close_slaves(AVFormatContext *avf) for (i = 0; i < tee->nb_slaves; i++) { close_slave(>slaves[i]); } +av_freep(>slaves); } static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) @@ -443,24 +442,28 @@ static int tee_write_header(AVFormatContext *avf) TeeContext *tee = avf->priv_data; unsigned nb_slaves = 0, i; const char *filename = avf->filename; -char *slaves[MAX_SLAVES]; +char **slaves = NULL; int ret; while (*filename) { -if (nb_slaves == MAX_SLAVES) { -av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n", - MAX_SLAVES); -ret = AVERROR_PATCHWELCOME; +char *slave = av_get_token(, slave_delim); +if (!slave) { +ret = AVERROR(ENOMEM); goto fail; } -if (!(slaves[nb_slaves++] = av_get_token(, slave_delim))) { -ret = AVERROR(ENOMEM); +ret = av_dynarray_add_nofree(, _slaves, slave); +if (ret < 0) { +av_free(slave); goto fail; } if (strspn(filename, slave_delim)) filename++; } +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++) { @@ -483,12 +486,14 @@ static int tee_write_header(AVFormatContext *avf) av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped " "to any slave.\n", i); } +av_free(slaves); return 0; fail: for (i = 0; i < nb_slaves; i++) av_freep([i]); close_slaves(avf); +av_free(slaves); return ret; } @@ -505,6 +510,7 @@ static int tee_write_trailer(AVFormatContext *avf) ret_all = ret; } } +av_freep(>slaves); return ret_all; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 3/3] avformat/segment: Use deinit function to deinitialize muxer
From: Jan SebechlebskyThis commit moves all deinitialization of SegmentContext to seg_free_context function and registers this function as .deinit function of segment muxer. This also fixes memory leaks caused by context not being deinitialized when write_header call of segment muxer fails. Signed-off-by: Jan Sebechlebsky --- Thanks for noticing, I've removed unused declarations, this patch should be fine. libavformat/segment.c | 50 +++--- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/libavformat/segment.c b/libavformat/segment.c index df37a56..c9d0074 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -627,9 +627,28 @@ static int select_reference_stream(AVFormatContext *s) return 0; } -static void seg_free_context(SegmentContext *seg) +static void seg_free_context(AVFormatContext *s) { -ff_format_io_close(seg->avf, >list_pb); +SegmentContext *seg = s->priv_data; +SegmentListEntry *cur, *next; + +if (seg->list) +ff_format_io_close(s, >list_pb); + +av_dict_free(>format_options); +av_opt_free(seg); +av_freep(>times); +av_freep(>frames); +av_freep(>cur_entry.filename); + +cur = seg->segment_list_entries; +while (cur) { +next = cur->next; +av_freep(>filename); +av_free(cur); +cur = next; +} + avformat_free_context(seg->avf); seg->avf = NULL; } @@ -801,8 +820,6 @@ static int seg_init(AVFormatContext *s) fail: av_dict_free(); -if (ret < 0) -seg_free_context(seg); return ret; } @@ -917,9 +934,6 @@ fail: seg->segment_frame_count++; } -if (ret < 0) -seg_free_context(seg); - return ret; } @@ -927,7 +941,6 @@ static int seg_write_trailer(struct AVFormatContext *s) { SegmentContext *seg = s->priv_data; AVFormatContext *oc = seg->avf; -SegmentListEntry *cur, *next; int ret = 0; if (!oc) @@ -944,25 +957,6 @@ static int seg_write_trailer(struct AVFormatContext *s) ret = segment_end(s, 1, 1); } fail: -if (seg->list) -ff_format_io_close(s, >list_pb); - -av_dict_free(>format_options); -av_opt_free(seg); -av_freep(>times); -av_freep(>frames); -av_freep(>cur_entry.filename); - -cur = seg->segment_list_entries; -while (cur) { -next = cur->next; -av_freep(>filename); -av_free(cur); -cur = next; -} - -avformat_free_context(oc); -seg->avf = NULL; return ret; } @@ -1045,6 +1039,7 @@ AVOutputFormat ff_segment_muxer = { .write_packet = seg_write_packet, .write_trailer = seg_write_trailer, .check_bitstream = seg_check_bitstream, +.deinit = seg_free_context, .priv_class = _class, }; @@ -1064,5 +1059,6 @@ AVOutputFormat ff_stream_segment_muxer = { .write_packet = seg_write_packet, .write_trailer = seg_write_trailer, .check_bitstream = seg_check_bitstream, +.deinit = seg_free_context, .priv_class = _class, }; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/3] avformat/segment: Check write_header return value immediately
From: Jan SebechlebskyCheck write_header return value immediately after call, so in the successive statements we can assume the write_header was successful. Signed-off-by: Jan Sebechlebsky --- This is needed for the next patch in patchset. libavformat/segment.c | 9 + 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libavformat/segment.c b/libavformat/segment.c index df6f4b5..7e4e840 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -754,6 +754,11 @@ static int seg_init(AVFormatContext *s) av_dict_copy(, seg->format_options, 0); ret = avformat_write_header(oc, ); +if (ret < 0) { +ff_format_io_close(oc, >pb); +goto fail; +} + if (av_dict_count(options)) { av_log(s, AV_LOG_ERROR, "Some of the provided format options in '%s' are not recognized\n", seg->format_options_str); @@ -761,10 +766,6 @@ static int seg_init(AVFormatContext *s) goto fail; } -if (ret < 0) { -ff_format_io_close(oc, >pb); -goto fail; -} seg->segment_frame_count = 0; av_assert0(s->nb_streams == oc->nb_streams); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] Fix memory leaks in segment muxer
From: Jan SebechlebskyHello, I've observed several memory leaks in segment muxer in case the failure happens in seg_init (write_header call). I'm sending patchset to fix those. Regards, Jan Jan Sebechlebsky (3): avformat/segment: Check write_header return value immediately avformat/segment: Ensure write_trailer is called avformat/segment: Use deinit function to deinitialize muxer libavformat/segment.c | 68 --- 1 file changed, 37 insertions(+), 31 deletions(-) -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 3/3] avformat/segment: Use deinit function to deinitialize muxer
From: Jan SebechlebskyThis commit moves all deinitialization of SegmentContext to seg_free_context function and registers this function as .deinit function of segment muxer. This also fixes memory leaks caused by context not being deinitialized when write_header call of segment muxer fails. Signed-off-by: Jan Sebechlebsky --- libavformat/segment.c | 49 +++-- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/libavformat/segment.c b/libavformat/segment.c index df37a56..872233b 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -627,9 +627,28 @@ static int select_reference_stream(AVFormatContext *s) return 0; } -static void seg_free_context(SegmentContext *seg) +static void seg_free_context(AVFormatContext *s) { -ff_format_io_close(seg->avf, >list_pb); +SegmentContext *seg = s->priv_data; +SegmentListEntry *cur, *next; + +if (seg->list) +ff_format_io_close(s, >list_pb); + +av_dict_free(>format_options); +av_opt_free(seg); +av_freep(>times); +av_freep(>frames); +av_freep(>cur_entry.filename); + +cur = seg->segment_list_entries; +while (cur) { +next = cur->next; +av_freep(>filename); +av_free(cur); +cur = next; +} + avformat_free_context(seg->avf); seg->avf = NULL; } @@ -801,8 +820,6 @@ static int seg_init(AVFormatContext *s) fail: av_dict_free(); -if (ret < 0) -seg_free_context(seg); return ret; } @@ -917,9 +934,6 @@ fail: seg->segment_frame_count++; } -if (ret < 0) -seg_free_context(seg); - return ret; } @@ -944,25 +958,6 @@ static int seg_write_trailer(struct AVFormatContext *s) ret = segment_end(s, 1, 1); } fail: -if (seg->list) -ff_format_io_close(s, >list_pb); - -av_dict_free(>format_options); -av_opt_free(seg); -av_freep(>times); -av_freep(>frames); -av_freep(>cur_entry.filename); - -cur = seg->segment_list_entries; -while (cur) { -next = cur->next; -av_freep(>filename); -av_free(cur); -cur = next; -} - -avformat_free_context(oc); -seg->avf = NULL; return ret; } @@ -1045,6 +1040,7 @@ AVOutputFormat ff_segment_muxer = { .write_packet = seg_write_packet, .write_trailer = seg_write_trailer, .check_bitstream = seg_check_bitstream, +.deinit = seg_free_context, .priv_class = _class, }; @@ -1064,5 +1060,6 @@ AVOutputFormat ff_stream_segment_muxer = { .write_packet = seg_write_packet, .write_trailer = seg_write_trailer, .check_bitstream = seg_check_bitstream, +.deinit = seg_free_context, .priv_class = _class, }; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/3] avformat/segment: Ensure write_trailer is called
From: Jan SebechlebskyEnsure that write_trailer is always called after successful write_header operation so underlying muxer is deinitialized. Signed-off-by: Jan Sebechlebsky --- This is a little tricky - we have to ensure that write_trailer is called if the write_header succeeded, however if the io_open fails the AVIOContext is invalid and cause underlying muxer to crash (the muxer may assume that AVIOContext is initialized). So temporarily AVIOContext is initialized to null context just to allow write_trailer to finalize underlying muxer successfully. libavformat/segment.c | 10 +- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libavformat/segment.c b/libavformat/segment.c index 7e4e840..df37a56 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -763,6 +763,8 @@ static int seg_init(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Some of the provided format options in '%s' are not recognized\n", seg->format_options_str); ret = AVERROR(EINVAL); +av_write_trailer(oc); +ff_format_io_close(oc, >pb); goto fail; } @@ -785,8 +787,14 @@ static int seg_init(AVFormatContext *s) } else { close_null_ctxp(>pb); } -if ((ret = oc->io_open(oc, >pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0) +if ((ret = oc->io_open(oc, >pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0) { +// Some muxers rely on valid AVIOContext so we need to create one +// before performing write trailer +open_null_ctx(>pb); +av_write_trailer(oc); +close_null_ctxp(>pb); goto fail; +} if (!seg->individual_header_trailer) oc->pb->seekable = 0; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3] avformat/tee: Support arbitrary number of slaves
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- I've missed that - sorry, should be fixed in this patch. libavformat/tee.c | 26 +++--- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 806beaa..421623d 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -27,8 +27,6 @@ #include "avformat.h" #include "avio_internal.h" -#define MAX_SLAVES 16 - typedef enum { ON_SLAVE_FAILURE_ABORT = 1, ON_SLAVE_FAILURE_IGNORE = 2 @@ -52,7 +50,7 @@ typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; unsigned nb_alive; -TeeSlave slaves[MAX_SLAVES]; +TeeSlave *slaves; } TeeContext; static const char *const slave_delim = "|"; @@ -203,6 +201,7 @@ static void close_slaves(AVFormatContext *avf) for (i = 0; i < tee->nb_slaves; i++) { close_slave(>slaves[i]); } +av_freep(>slaves); } static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) @@ -443,24 +442,26 @@ static int tee_write_header(AVFormatContext *avf) TeeContext *tee = avf->priv_data; unsigned nb_slaves = 0, i; const char *filename = avf->filename; -char *slaves[MAX_SLAVES]; +char **slaves = NULL; int ret; while (*filename) { -if (nb_slaves == MAX_SLAVES) { -av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n", - MAX_SLAVES); -ret = AVERROR_PATCHWELCOME; -goto fail; -} -if (!(slaves[nb_slaves++] = av_get_token(, slave_delim))) { +char *slave = av_get_token(, slave_delim); +if (!slave) { ret = AVERROR(ENOMEM); goto fail; } +ret = av_dynarray_add_nofree(, _slaves, slave); +if (ret < 0) +goto fail; if (strspn(filename, slave_delim)) filename++; } +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++) { @@ -483,12 +484,14 @@ static int tee_write_header(AVFormatContext *avf) av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped " "to any slave.\n", i); } +av_free(slaves); return 0; fail: for (i = 0; i < nb_slaves; i++) av_freep([i]); close_slaves(avf); +av_free(slaves); return ret; } @@ -505,6 +508,7 @@ static int tee_write_trailer(AVFormatContext *avf) ret_all = ret; } } +av_freep(>slaves); return ret_all; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avformat/tee: Support arbitrary number of slaves
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavformat/tee.c | 24 +++- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 806beaa..bf7438c 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -27,8 +27,6 @@ #include "avformat.h" #include "avio_internal.h" -#define MAX_SLAVES 16 - typedef enum { ON_SLAVE_FAILURE_ABORT = 1, ON_SLAVE_FAILURE_IGNORE = 2 @@ -52,7 +50,7 @@ typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; unsigned nb_alive; -TeeSlave slaves[MAX_SLAVES]; +TeeSlave *slaves; } TeeContext; static const char *const slave_delim = "|"; @@ -203,6 +201,7 @@ static void close_slaves(AVFormatContext *avf) for (i = 0; i < tee->nb_slaves; i++) { close_slave(>slaves[i]); } +av_freep(>slaves); } static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) @@ -443,17 +442,17 @@ static int tee_write_header(AVFormatContext *avf) TeeContext *tee = avf->priv_data; unsigned nb_slaves = 0, i; const char *filename = avf->filename; -char *slaves[MAX_SLAVES]; +char **slaves = NULL; int ret; while (*filename) { -if (nb_slaves == MAX_SLAVES) { -av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n", - MAX_SLAVES); -ret = AVERROR_PATCHWELCOME; +char *slave = av_get_token(, slave_delim); +if (!slave) { +ret = AVERROR(ENOMEM); goto fail; } -if (!(slaves[nb_slaves++] = av_get_token(, slave_delim))) { +dynarray_add(, _slaves, slave); +if (!slaves) { ret = AVERROR(ENOMEM); goto fail; } @@ -461,6 +460,10 @@ static int tee_write_header(AVFormatContext *avf) filename++; } +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++) { @@ -483,12 +486,14 @@ static int tee_write_header(AVFormatContext *avf) av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped " "to any slave.\n", i); } +av_free(slaves); return 0; fail: for (i = 0; i < nb_slaves; i++) av_freep([i]); close_slaves(avf); +av_free(slaves); return ret; } @@ -505,6 +510,7 @@ static int tee_write_trailer(AVFormatContext *avf) ret_all = ret; } } +av_freep(>slaves); return ret_all; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/tee: Support arbitrary number of slaves
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavformat/tee.c | 27 +-- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 806beaa..427e999 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -27,8 +27,6 @@ #include "avformat.h" #include "avio_internal.h" -#define MAX_SLAVES 16 - typedef enum { ON_SLAVE_FAILURE_ABORT = 1, ON_SLAVE_FAILURE_IGNORE = 2 @@ -52,7 +50,7 @@ typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; unsigned nb_alive; -TeeSlave slaves[MAX_SLAVES]; +TeeSlave *slaves; } TeeContext; static const char *const slave_delim = "|"; @@ -203,6 +201,8 @@ static void close_slaves(AVFormatContext *avf) for (i = 0; i < tee->nb_slaves; i++) { close_slave(>slaves[i]); } + +av_freep(>slaves); } static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) @@ -443,17 +443,17 @@ static int tee_write_header(AVFormatContext *avf) TeeContext *tee = avf->priv_data; unsigned nb_slaves = 0, i; const char *filename = avf->filename; -char *slaves[MAX_SLAVES]; +char **slaves = NULL; int ret; while (*filename) { -if (nb_slaves == MAX_SLAVES) { -av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n", - MAX_SLAVES); -ret = AVERROR_PATCHWELCOME; +char *slave = av_get_token(, slave_delim); +if (!slave) { +ret = AVERROR(ENOMEM); goto fail; } -if (!(slaves[nb_slaves++] = av_get_token(, slave_delim))) { +dynarray_add(, _slaves, slave); +if (!slaves) { ret = AVERROR(ENOMEM); goto fail; } @@ -461,6 +461,10 @@ static int tee_write_header(AVFormatContext *avf) filename++; } +if (!(tee->slaves = calloc(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++) { @@ -483,12 +487,13 @@ static int tee_write_header(AVFormatContext *avf) av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped " "to any slave.\n", i); } +av_free(slaves); return 0; - fail: for (i = 0; i < nb_slaves; i++) av_freep([i]); close_slaves(avf); +av_free(slaves); return ret; } @@ -505,6 +510,8 @@ static int tee_write_trailer(AVFormatContext *avf) ret_all = ret; } } + +av_freep(>slaves); return ret_all; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avutil/threadmessage.h: Fix swapped comments
From: Jan SebechlebskyFix swapped descriptions of av_thread_message_queue_set_err_send and av_thread_message_queue_set_err_recv. Signed-off-by: Jan Sebechlebsky --- libavutil/threadmessage.h | 16 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libavutil/threadmessage.h b/libavutil/threadmessage.h index e256cae..8480a0a 100644 --- a/libavutil/threadmessage.h +++ b/libavutil/threadmessage.h @@ -69,10 +69,10 @@ int av_thread_message_queue_recv(AVThreadMessageQueue *mq, /** * Set the sending error code. * - * If the error code is set to non-zero, av_thread_message_queue_recv() will - * return it immediately when there are no longer available messages. - * Conventional values, such as AVERROR_EOF or AVERROR(EAGAIN), can be used - * to cause the receiving thread to stop or suspend its operation. + * If the error code is set to non-zero, av_thread_message_queue_send() will + * return it immediately. Conventional values, such as AVERROR_EOF or + * AVERROR(EAGAIN), can be used to cause the sending thread to stop or + * suspend its operation. */ void av_thread_message_queue_set_err_send(AVThreadMessageQueue *mq, int err); @@ -80,10 +80,10 @@ void av_thread_message_queue_set_err_send(AVThreadMessageQueue *mq, /** * Set the receiving error code. * - * If the error code is set to non-zero, av_thread_message_queue_send() will - * return it immediately. Conventional values, such as AVERROR_EOF or - * AVERROR(EAGAIN), can be used to cause the sending thread to stop or - * suspend its operation. + * If the error code is set to non-zero, av_thread_message_queue_recv() will + * return it immediately when there are no longer available messages. + * Conventional values, such as AVERROR_EOF or AVERROR(EAGAIN), can be used + * to cause the receiving thread to stop or suspend its operation. */ void av_thread_message_queue_set_err_recv(AVThreadMessageQueue *mq, int err); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avformat/tee: Move to new BSF API
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- I've rewritten the patch rapidly. Instead of using recursion it accumulates bitstream filtered packets in fifo buffer and dynamic array is used instead of linked list to store chain of bitstream filters. libavformat/tee.c | 301 +- 1 file changed, 255 insertions(+), 46 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 806beaa..0e394ea 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -23,6 +23,7 @@ #include "libavutil/avutil.h" #include "libavutil/avstring.h" #include "libavutil/opt.h" +#include "libavutil/fifo.h" #include "internal.h" #include "avformat.h" #include "avio_internal.h" @@ -37,8 +38,15 @@ typedef enum { #define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT typedef struct { +AVBSFContext **bsfs_ctxs; +unsigned bsfs_ctxs_nr; +} TeeBSFList; + +typedef struct { AVFormatContext *avf; -AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +TeeBSFList *bsfs; // bitstream filters per stream + +AVFifoBuffer *fifo; // fifo buffer used for bsf processing SlaveFailurePolicy on_fail; @@ -106,6 +114,46 @@ fail: return ret; } +static int initialize_bsf(AVFormatContext *avf, const char * bsf_name, + AVCodecParameters *par_in, AVRational tb_in, + AVBSFContext ** bsf_ctx) +{ +int ret = 0; +const AVBitStreamFilter *filter = av_bsf_get_by_name(bsf_name); +AVBSFContext *bsf_ctx_tmp; + +if (!filter) { +av_log(avf, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", + bsf_name); +ret = AVERROR(EINVAL); +return ret; +} + +if ((ret = av_bsf_alloc(filter, _ctx_tmp)) < 0) { +av_log(avf, AV_LOG_ERROR, "Cannot initialize bitstream filter '%s'", + bsf_name); +return ret; +} + +ret = avcodec_parameters_copy(bsf_ctx_tmp->par_in, par_in); +if (ret < 0) { +goto fail; +} + +bsf_ctx_tmp->time_base_in = tb_in; + +if ((ret = av_bsf_init(bsf_ctx_tmp)) < 0) { +goto fail; +} + +*bsf_ctx = bsf_ctx_tmp; + +return ret; +fail: +av_bsf_free(_ctx_tmp); +return ret; +} + /** * Parse list of bitstream filters and add them to the list of filters * pointed to by bsfs. @@ -113,37 +161,49 @@ fail: * The list must be specified in the form: * BSFS ::= BSF[,BSFS] */ -static int parse_bsfs(void *log_ctx, const char *bsfs_spec, - AVBitStreamFilterContext **bsfs) +static int parse_bsfs(AVFormatContext *avf, const char *bsfs_spec, + TeeBSFList * bsf_list, int stream_nr) { char *bsf_name, *buf, *dup, *saveptr; -int ret = 0; +int ret = 0, i; +AVBSFContext *bsf_ctx; +AVStream *stream = avf->streams[stream_nr]; +AVCodecParameters *last_codecpar = stream->codecpar; +AVRational last_tb = stream->time_base; -if (!(dup = buf = av_strdup(bsfs_spec))) -return AVERROR(ENOMEM); +if (!(dup = buf = av_strdup(bsfs_spec))) { +ret = AVERROR(ENOMEM); +goto fail; +} while (bsf_name = av_strtok(buf, ",", )) { -AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name); - -if (!bsf) { -av_log(log_ctx, AV_LOG_ERROR, - "Cannot initialize bitstream filter with name '%s', " - "unknown filter or internal error happened\n", - bsf_name); -ret = AVERROR_UNKNOWN; -goto end; +ret = initialize_bsf(avf, bsf_name, last_codecpar, last_tb, _ctx); +if (ret < 0) { +goto fail; } -/* append bsf context to the list of bsf contexts */ -*bsfs = bsf; -bsfs = >next; +last_tb = bsf_ctx->time_base_out; +last_codecpar = bsf_ctx->par_out; + +ret = av_dynarray_add_nofree(_list->bsfs_ctxs, _list->bsfs_ctxs_nr, bsf_ctx); +if (ret < 0) { +goto fail; +} buf = NULL; +bsf_ctx = NULL; } -end: av_free(dup); return ret; +fail: +for (i = 0; i < bsf_list->bsfs_ctxs_nr; ++i) { +av_bsf_free(_list->bsfs_ctxs[i]); +} +bsf_list->bsfs_ctxs_nr = 0; +av_free(dup); +av_bsf_free(_ctx); +return ret; } static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) @@ -163,6 +223,154 @@ static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *t return AVERROR(EINVAL); } +/* + * Applies single bitstream filter to single packet, all resulting filtered packets + * are pushed to fifo buffer + */ +static int tee_apply_bsf(AVFifoBuffer *fifo, AVBSFContext *bsf_ctx, AVPacket *pkt) +{ +int ret = 0; +ret = av_bsf_send_packet(bsf_ctx, pkt); +
[FFmpeg-devel] [PATCH] libavutil/fifo: Fix fifo grow step
From: Jan SebechlebskyFifo was reallocating always to twice of the requested size. This fixes it to reallocate to requested size, or twice of the original size - whichever is greater. Signed-off-by: Jan Sebechlebsky --- I believe the intended behaviour was as described in commit message and FFMAX(size,2*size) is a mistake. libavutil/fifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavutil/fifo.c b/libavutil/fifo.c index 986729a..1060aed 100644 --- a/libavutil/fifo.c +++ b/libavutil/fifo.c @@ -113,7 +113,7 @@ int av_fifo_grow(AVFifoBuffer *f, unsigned int size) size += av_fifo_size(f); if (old_size < size) -return av_fifo_realloc2(f, FFMAX(size, 2*size)); +return av_fifo_realloc2(f, FFMAX(size, 2*old_size)); return 0; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/tee: Move to new BSF API
From: Jan SebechlebskySigned-off-by: Jan Sebechlebsky --- libavformat/tee.c | 171 -- 1 file changed, 139 insertions(+), 32 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 806beaa..ff0918b 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -36,9 +36,14 @@ typedef enum { #define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT +typedef struct TeeBSFList { +AVBSFContext *bsf_ctx; +struct TeeBSFList *next; +} TeeBSFList; + typedef struct { AVFormatContext *avf; -AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +TeeBSFList **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; @@ -113,30 +118,61 @@ fail: * The list must be specified in the form: * BSFS ::= BSF[,BSFS] */ -static int parse_bsfs(void *log_ctx, const char *bsfs_spec, - AVBitStreamFilterContext **bsfs) +static int parse_bsfs(AVFormatContext *avf, const char *bsfs_spec, + TeeBSFList **bsfs, int stream_nr) { char *bsf_name, *buf, *dup, *saveptr; int ret = 0; +const AVBitStreamFilter *filter; +AVBSFContext *bsf_ctx; +TeeBSFList *bsf_lst; +AVStream *stream = avf->streams[stream_nr]; +AVRational last_tb = stream->time_base; if (!(dup = buf = av_strdup(bsfs_spec))) return AVERROR(ENOMEM); while (bsf_name = av_strtok(buf, ",", )) { -AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name); +filter = av_bsf_get_by_name(bsf_name); + +if (!filter) { +av_log(avf, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", + bsf_name); +ret = AVERROR(EINVAL); +goto end; +} -if (!bsf) { -av_log(log_ctx, AV_LOG_ERROR, - "Cannot initialize bitstream filter with name '%s', " - "unknown filter or internal error happened\n", +if ((ret = av_bsf_alloc(filter, _ctx)) < 0) { +av_log(avf, AV_LOG_ERROR, "Cannot initialize bitstream filter '%s'", bsf_name); -ret = AVERROR_UNKNOWN; goto end; } -/* append bsf context to the list of bsf contexts */ -*bsfs = bsf; -bsfs = >next; +ret = avcodec_parameters_copy(bsf_ctx->par_in, stream->codecpar); +if (ret < 0) { +goto fail; +} + +bsf_ctx->time_base_in = last_tb; + +if ((ret = av_bsf_init(bsf_ctx)) < 0) { +goto fail; +} + +last_tb = bsf_ctx->time_base_out; + +/* allocate new bsf list node and append to the list of bsf contexts */ +bsf_lst = av_mallocz(sizeof(TeeBSFList)); + +if (!bsf_lst) { +ret = AVERROR(ENOMEM); +goto fail; +} + +bsf_lst->bsf_ctx = bsf_ctx; + +*bsfs = bsf_lst; +bsfs = _lst->next; buf = NULL; } @@ -144,6 +180,10 @@ static int parse_bsfs(void *log_ctx, const char *bsfs_spec, end: av_free(dup); return ret; +fail: +av_free(dup); +av_bsf_free(_ctx); +return ret; } static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) @@ -163,6 +203,75 @@ static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *t return AVERROR(EINVAL); } +/** + * Apply bitstream filters and write frame(s) + */ +static int tee_process_packet(AVFormatContext *avf, TeeBSFList *bsf, AVPacket *pkt, + AVRational pkt_tb, int stream_nr) +{ +int ret_all = 0,ret; +if (!bsf) { +if (pkt) { +AVRational out_tb = avf->streams[stream_nr]->time_base; +pkt->pts = av_rescale_q(pkt->pts, pkt_tb, out_tb); +pkt->dts = av_rescale_q(pkt->dts, pkt_tb, out_tb); +pkt->duration = av_rescale_q(pkt->duration, pkt_tb, out_tb); +pkt->stream_index = stream_nr; + +ret_all = av_interleaved_write_frame(avf, pkt); +} +goto end; +} + +if ((ret_all = av_bsf_send_packet(bsf->bsf_ctx, pkt)) < 0) { +return ret_all; +} + +do { +if (!(ret = av_bsf_receive_packet(bsf->bsf_ctx, pkt))) { +ret = tee_process_packet(avf, bsf->next, pkt, + bsf->bsf_ctx->time_base_out, stream_nr); +} +} while(!ret); + +if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) { +ret_all = ret; +} + +end: +return ret_all; +} + +static void free_bsfs(TeeBSFList *bsf_list) { +TeeBSFList *next, *current = bsf_list; + +while (current) { +av_bsf_free(>bsf_ctx); +next = current->next; +av_free(current); +current = next; +} +} + +static int flush_bsfs(TeeSlave *tee_slave) +{ +AVFormatContext *avf = tee_slave->avf; +int i; +
[FFmpeg-devel] [PATCH v2 1/2] avformat/tee: Fix TeeSlave.bsfs pointer array size
From: Jan SebechlebskyTeeSlave.bsfs is array of pointers to AVBitStreamFilterContext, so element size should be really size of a pointer, not size of TeeSlave structure. Signed-off-by: Jan Sebechlebsky --- I've rewritten sizeof as suggested :) libavformat/tee.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 499ef33..6d2ce53 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -324,7 +324,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) } tee_slave->header_written = 1; -tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave)); +tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(*tee_slave->bsfs)); if (!tee_slave->bsfs) { ret = AVERROR(ENOMEM); goto end; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avcodec/mjpeg2jpeg_bsf: Check ff_bsf_get_packet success
From: Jan SebechlebskyThis fixes ticket #5487 - mjpeg2jpeg bitstream filter causes segmentation fault with header-less mjpeg. Signed-off-by: Jan Sebechlebsky --- libavcodec/mjpeg2jpeg_bsf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavcodec/mjpeg2jpeg_bsf.c b/libavcodec/mjpeg2jpeg_bsf.c index 2d4cee2..6f02bc0 100644 --- a/libavcodec/mjpeg2jpeg_bsf.c +++ b/libavcodec/mjpeg2jpeg_bsf.c @@ -86,6 +86,8 @@ static int mjpeg2jpeg_filter(AVBSFContext *ctx, AVPacket *out) uint8_t *output; ret = ff_bsf_get_packet(ctx, ); +if (ret < 0) +return ret; if (in->size < 12) { av_log(ctx, AV_LOG_ERROR, "input is truncated\n"); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avcodec/mjpeg2jpeg_bsf: Check ff_bsf_get_packet success
From: Jan SebechlebskyThis fixes ticket #5487 - mjpeg2jpeg bitstream filter causes segmentation fault with header-less mjpeg. Signed-off-by: Jan Sebechlebsky --- libavcodec/mjpeg2jpeg_bsf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libavcodec/mjpeg2jpeg_bsf.c b/libavcodec/mjpeg2jpeg_bsf.c index 2d4cee2..c4b5050 100644 --- a/libavcodec/mjpeg2jpeg_bsf.c +++ b/libavcodec/mjpeg2jpeg_bsf.c @@ -85,7 +85,9 @@ static int mjpeg2jpeg_filter(AVBSFContext *ctx, AVPacket *out) int input_skip, output_size; uint8_t *output; -ret = ff_bsf_get_packet(ctx, ); +if ((ret = ff_bsf_get_packet(ctx, )) < 0) { +return ret; +} if (in->size < 12) { av_log(ctx, AV_LOG_ERROR, "input is truncated\n"); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel