[FFmpeg-devel] [PATCH v2] libavformat/tee: Add fifo support for tee
From: Jan Sebechlebsky Signed-off-by: Jan Sebechlebsky --- Thanks for noticing, I've fixed the patch (also some minor formatting issues I've noticed). doc/muxers.texi | 20 + libavformat/tee.c | 87 ++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index dbe53f5..7b4e165 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1473,6 +1473,7 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@anchor{fifo} @section fifo The fifo pseudo-muxer allows the separation of encoding and muxing by using @@ -1580,6 +1581,18 @@ with the tee muxer; encoding can be a very expensive process. It is not useful when using the libavformat API directly because it is then possible to feed the same packets to several muxers directly. +@table @option + +@item use_fifo @var{bool} +If set to 1, slave outputs will be processed in separate thread using @ref{fifo} +muxer. This allows to compensate for different speed/latency/reliability of +outputs and setup transparent recovery. By default this feature is turned off. + +@item fifo_options +Options to pass to fifo pseudo-muxer instances. See @ref{fifo}. + +@end table + The slave outputs are specified in the file name given to the muxer, separated by '|'. If any of the slave name contains the '|' separator, leading or trailing spaces or any special character, it must be @@ -1601,6 +1614,13 @@ output name suffix. Specify a list of bitstream filters to apply to the specified output. +@item use_fifo @var{bool} +This allows to override tee muxer use_fifo option for individual slave muxer. + +@item fifo_options +This allows to override tee muxer fifo_options for individual slave muxer. +See @ref{fifo}. + It is possible to specify to which streams a given bitstream filter applies, by appending a stream specifier to the option separated by @code{/}. @var{spec} must be a stream specifier (see @ref{Format diff --git a/libavformat/tee.c b/libavformat/tee.c index d59ad4d..c668e95 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -40,6 +40,8 @@ typedef struct { AVBSFContext **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; +int use_fifo; +AVDictionary *fifo_options; /** map from input to output streams indexes, * disabled output streams are set to -1 */ @@ -52,15 +54,28 @@ typedef struct TeeContext { unsigned nb_slaves; unsigned nb_alive; TeeSlave *slaves; +int use_fifo; +AVDictionary *fifo_options; +char *fifo_options_str; } TeeContext; static const char *const slave_delim = "|"; static const char *const slave_bsfs_spec_sep = "/"; static const char *const slave_select_sep = ","; +#define OFFSET(x) offsetof(TeeContext, x) +static const AVOption options[] = { +{"use_fifo", "Use fifo pseudo-muxer to separate actual muxers from encoder", + OFFSET(use_fifo), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, +{"fifo_options", "fifo pseudo-muxer options", OFFSET(fifo_options_str), + AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, +{NULL} +}; + static const AVClass tee_muxer_class = { .class_name = "Tee muxer", .item_name = av_default_item_name, +.option = options, .version= LIBAVUTIL_VERSION_INT, }; @@ -81,6 +96,27 @@ static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *t return AVERROR(EINVAL); } +static int parse_slave_fifo_options(const char *use_fifo, +const char *fifo_options, TeeSlave *tee_slave) +{ +int ret = 0; + +if (use_fifo) { +if (av_match_name(use_fifo, "true,y,yes,enable,enabled,on,1")) { +tee_slave->use_fifo = 1; +} else if (av_match_name(use_fifo, "false,n,no,disable,disabled,off,0")) { +tee_slave->use_fifo = 0; +} else { +return AVERROR(EINVAL); +} +} + +if (fifo_options) +ret = av_dict_parse_string(&tee_slave->fifo_options, fifo_options, "=", ":", 0); + +return ret; +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -125,6 +161,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionaryEntry *entry; char *filename; char *format = NULL, *select = NULL, *on_fail = NULL; +char *use_fifo = NULL, *fifo_options_str = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -145,6 +182,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); STEAL_OPTION("onfail", on_fail); +STEAL_OPTION("use_fifo", use_fifo); +STEAL_OPTION("fifo_options", fifo_options_str); ret = parse_slave_failure_policy_optio
[FFmpeg-devel] [PATCH v2] avformat/tee: Add FATE tests for tee
From: Jan Sebechlebsky This 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 file mode 100644 index 000..0b42d11 --- /dev/null ++
[FFmpeg-devel] [PATCH v2 1/2] libavformat/tee: Add fifo support for tee
From: Jan Sebechlebsky Signed-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(&tee_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); STEAL_OPTION("select", select); STEAL_OPTIO
[FFmpeg-devel] [PATCH 0/2] Fifo support for tee
From: Jan Sebechlebsky Hello, 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 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(&tee->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(&options, &tee_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 v2 1/7] avformat/utils: Add ff_stream_encode_params_copy()
From: Jan Sebechlebsky Signed-off-by: Jan Sebechlebsky --- Chanages from the last version of patch: -> dst->metadata dictionary is freed before entries are copied from src->metadata libavformat/internal.h | 9 libavformat/utils.c| 57 ++ 2 files changed, 66 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..f2b0e6e 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -3951,6 +3951,63 @@ 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; + +av_dict_free(&dst->metadata); +ret = av_dict_copy(&dst->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(&dst->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(&dst->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 v2 3/7] avformat/tee: Rescale ts using av_packet_rescale_ts
From: Jan Sebechlebsky This 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(&pkt2, tb, tb2); pkt2.stream_index = s2; if ((ret = av_apply_bitstream_filters(avf2->streams[s2]->codec, &pkt2, -- 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 Sebechlebsky mpeg4_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(&in); +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 2/2] avcodec/mpeg4_unpack_bframes_bsf: Check av_packet_from_data() return value
From: Jan Sebechlebsky Signed-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(&in); +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/bsf: Check side data when setting BSF EOF flag.
From: Jan Sebechlebsky Set 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/avcodec.h: Change av_bsf_send_packet() comment
From: Jan Sebechlebsky Specify 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 v2] avcodec/bsf: Set EOF flag only if pkt == NULL
From: Jan Sebechlebsky Set 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 0/2][GSoC] Add fifo pseudo-muxer
From: Jan Sebechlebsky Hello, 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 v3 1/2] libavformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky The 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 flv -map 0:v -map 0:a + -block_on_overflow 0 -attemp
[FFmpeg-devel] [PATCH 2/2] MAINTAINERS: Add myself as maintainer of fifo muxer
From: Jan Sebechlebsky Signed-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 2/2] avcodec/bsf: Forbid packet without payload in av_bsf_send_packet
From: Jan Sebechlebsky Signed-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 Sebechlebsky Set 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 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) { +/* filtering error */ +
[FFmpeg-devel] [PATCH 2/5] avcodec/bsf: Add ff_bsf_get_packet_ref() function
From: Jan Sebechlebsky Use 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 5/5] avformat/tee: Use BSF list API
From: Jan Sebechlebsky Signed-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, ",", &saveptr)) { -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 = &bsf->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(&tee_slave->bsfs[i]); } av_freep(&tee_slave->stream_map); av_freep(&tee_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, &tee_slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, &tee_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(&options, 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(&tee_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 ? "," : ""); -bsf = bsf-
[FFmpeg-devel] [PATCH 4/5] avcodec/bsf: Add custom item name func for BSF list
From: Jan Sebechlebsky Add 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(&lst->bsf_lst[i]); av_freep(&lst->bsf_lst); +av_freep(&lst->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(&bp, 16, 128); + +av_bprintf(&bp, "bsf_list("); +for (i = 0; i < lst->ctx_nr; i++) +av_bprintf(&bp, i ? ",%s" : "%s", lst->bsf_lst[i]->filter->name); +av_bprintf(&bp, ")"); + +av_bprint_finalize(&bp, &lst->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 1/5] doc/bsfs: Fix bsf options divider in documentation
From: Jan Sebechlebsky The 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 09/11] avformat/mux: Restore stream ts in av_write_packet on EAGAIN
From: Jan Sebechlebsky compute_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 03/11] avformat/fifo: Add fate test
From: Jan Sebechlebsky Signed-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(&avf->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 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan Sebechlebsky Add 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(&ctx->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(&avf2->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(&msg.pkt); @@ -552,15 +573,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, &msg, - 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, &msg, 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(&fifo->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))); +} +} + if (fifo->format_opti
[FFmpeg-devel] [PATCH 07/11] avformat/avformat.h: Add comments regarding AVFMT_FLAG_NONBLOCK.
From: Jan Sebechlebsky Add 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 06/11] avformat: add av_abort_output() function
From: Jan Sebechlebsky Signed-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 04/11] avformat/muxers: Add non-blocking mode support for av_write_trailer
From: Jan Sebechlebsky This 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 v4 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 rtmp://example.com/live/stream_name +@end example + +@end itemize
[FFmpeg-devel] [PATCH 02/11] MAINTAINERS: Add myself as maintainer of fifo muxer
From: Jan Sebechlebsky Signed-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 05/11] avformat/mux: Refactor muxer deinit from av_write_trailer
From: Jan Sebechlebsky Move 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(&s->streams[i]->priv_data); +av_freep(&s->streams[i]->index_entries); +} + +if (s->oformat->priv_class) +av_opt_free(s->priv_data); + +av_freep(&s->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(&s->streams[i]->priv_data); -av_freep(&s->streams[i]->index_entries); -} -if (s->oformat->priv_class) -av_opt_free(s->priv_data); -av_freep(&s->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 08/11] avformat/mux: Restore original ts in write_packet on error
From: Jan Sebechlebsky Restore 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] [GSoC] fifo muxer
From: Jan Sebechlebsky Hello, 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 11/11] avformat/fifo: Add test for nonblocking mode
From: Jan Sebechlebsky Signed-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(&pkt); + +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, 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, &pkt, 100); +av_packet_unref(&pkt); +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(&pkt); + +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, 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, &pkt, 100); +av_packet_unref(&pkt); +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 --- a/tests/ref/fate/fifo-muxer-ts
[FFmpeg-devel] [PATCH v5 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 example + +@end itemize + @section tee The tee muxer c
[FFmpeg-devel] [PATCH v2 06/11] avformat: add avformat_write_abort() function
From: Jan Sebechlebsky Signed-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 v2 07/11] avformat/avformat.h: Add comments regarding AVFMT_FLAG_NONBLOCK.
From: Jan Sebechlebsky Add comments regarding AVFMG_FLAG_NONBLOCK usage with muxers. Signed-off-by: Jan Sebechlebsky --- There are no changes in this patch, it is just rebased version because of change in previous patch. libavformat/avformat.h | 9 - 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 3f6a6eb..e2c4c41 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. * -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan Sebechlebsky Add 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(&ctx->termination_requested)) +return 1; + +return ff_check_interrupt(&ctx->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(&fifo_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(&fifo->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(&avf2->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(&msg.pkt); @@ -552,15 +581,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, &msg, - 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, &msg, 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(&fifo->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(&fifo->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; +avpriv_a
[FFmpeg-devel] [PATCH v2 11/11] avformat/fifo: Add test for nonblocking mode
From: Jan Sebechlebsky Signed-off-by: Jan Sebechlebsky --- Changes from last version: - av_abort_output was renamed to avformat_write_abort 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..9801810 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(&pkt); + +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, 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, &pkt, 100); +av_packet_unref(&pkt); +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(&pkt); + +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, 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, &pkt, 100); +av_packet_unref(&pkt); +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}}, + {NULL} }; diff --git a/tests/ref/fate/fifo-muxer-t
[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; + +lst->bsfs[i]->time_b
[FFmpeg-devel] [PATCH v2 4/5] avcodec/bsf: Add custom item name func for BSF list
From: Jan Sebechlebsky Add 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(&lst->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(&bp, 16, 128); + +av_bprintf(&bp, "bsf_list("); +for (i = 0; i < lst->nb_bsfs; i++) +av_bprintf(&bp, i ? ",%s" : "%s", lst->bsfs[i]->filter->name); +av_bprintf(&bp, ")"); + +av_bprint_finalize(&bp, &lst->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 5/5] avformat/tee: Use BSF list API
From: Jan Sebechlebsky Signed-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(&tee_slave->bsfs[i]); } av_freep(&tee_slave->stream_map); av_freep(&tee_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, &tee_slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, &tee_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(&options, 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(&tee_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(&pkt2, tb, tb2); pkt2.stream_index = s2; -if ((ret = av_apply_bitstream_filters(avf2->streams[s2]->codec, &pkt2, - tee->slaves[i].bsfs[s2])) < 0 || -(ret = av_interleaved_write_frame(avf2, &pkt2)) < 0) { +ret = av_bsf_send_packet(tee->slaves[i].bsfs[s2], &pkt2); +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], &pkt2); +if (ret < 0) +
[FFmpeg-devel] [PATCH v6 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 recovering from +queue overflow or failure. This option
[FFmpeg-devel] [PATCH v2 03/11] avformat/fifo: Add fate test
From: Jan Sebechlebsky Signed-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(&avf->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]); +} +printf("\n"); +} +#define OFFSET(x) offsetof(FailingMuxerCon
[FFmpeg-devel] [PATCH v3 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan Sebechlebsky Add 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(&ctx->termination_requested)) +return 1; + +return ff_check_interrupt(&ctx->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(&fifo_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(&fifo->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(&avf2->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(&msg.pkt); @@ -545,15 +574,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, &msg, - 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, &msg, 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(&fifo->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(&fifo->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(&fifo->termination_requested, 1); +ret = pthread_join( fifo->writer_thread, NULL); +i
[FFmpeg-devel] [PATCH v3 5/5] avformat/tee: Use BSF list API
From: Jan Sebechlebsky Signed-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, ",", &saveptr)) { -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 = &bsf->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(&tee_slave->bsfs[i]); } av_freep(&tee_slave->stream_map); av_freep(&tee_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, &tee_slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, &tee_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(&options, 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(&tee_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) { -av_log(l
[FFmpeg-devel] [PATCH v7 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 example + +@end itemize + +@anchor tee @section tee
[FFmpeg-devel] [PATCH v3 03/11] avformat/fifo: Add fate test
From: Jan Sebechlebsky Signed-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(&avf->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 v2 04/11] avformat/muxers: Add non-blocking mode support for av_write_trailer
From: Jan Sebechlebsky This 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, &pkt, 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, &pkt); 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 v2 05/11] avformat/mux: Refactor muxer deinit from av_write_trailer
From: Jan Sebechlebsky Move 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(&s->streams[i]->priv_data); +av_freep(&s->streams[i]->index_entries); +} + +if (s->oformat->priv_class) +av_opt_free(s->priv_data); + +av_freep(&s->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(&s->streams[i]->priv_data); -av_freep(&s->streams[i]->index_entries); -} -if (s->oformat->priv_class) -av_opt_free(s->priv_data); -av_freep(&s->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 v3 06/11] avformat: add avformat_write_abort() function
From: Jan Sebechlebsky Signed-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 v3 07/11] avformat/mux: Comment and assert AVFMT_FLAG_NONBLOCK
From: Jan Sebechlebsky Add 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 v4 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan Sebechlebsky Add 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(&ctx->termination_requested)) +return 1; + +return ff_check_interrupt(&ctx->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(&fifo_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(&fifo->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(&avf2->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(&msg.pkt); @@ -545,15 +574,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, &msg, - 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, &msg, 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(&fifo->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(&fifo->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(&fifo->termination_requested, 1); +
[FFmpeg-devel] [PATCH v3 11/11] avformat/fifo: Add test for nonblocking mode
From: Jan Sebechlebsky Signed-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(&pkt); + +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, 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, &pkt, 100); +av_packet_unref(&pkt); +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(&pkt); + +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, 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, &pkt, 100); +av_packet_unref(&pkt); +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}}, + {NULL} }; diff --git
[FFmpeg-devel] [PATCH v4 03/11] avformat/fifo: Add fate test
From: Jan Sebechlebsky Signed-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(&avf->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]); +} +printf(
[FFmpeg-devel] [PATCH v5 03/11] avformat/fifo: Add fate test
From: Jan Sebechlebsky Signed-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(&avf->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]); +} +printf("\n"); +} +#define OFFSET(x) offsetof(F
[FFmpeg-devel] [PATCH v8 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 failure (network outage) and attempt to recover +streami
[FFmpeg-devel] [PATCH v5 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan Sebechlebsky Add 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(&ctx->termination_requested)) +return 1; + +return ff_check_interrupt(&ctx->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(&fifo_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(&fifo->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(&avf2->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(&msg.pkt); @@ -542,15 +571,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, &msg, - 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, &msg, 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(&fifo->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(&fifo->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(&fifo->termination_requested, 1); +ret = pthread_join( fifo->writer_thread, NULL); +
[FFmpeg-devel] [PATCH v3 4/5] avcodec/bsf: Add custom item name func for BSF list
From: Jan Sebechlebsky Add 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(&bsf->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(&lst->bsfs[i]); av_freep(&lst->bsfs); +av_freep(&lst->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(&bp, 16, 128); + +av_bprintf(&bp, "bsf_list("); +for (i = 0; i < lst->nb_bsfs; i++) +av_bprintf(&bp, i ? ",%s" : "%s", lst->bsfs[i]->filter->name); +av_bprintf(&bp, ")"); + +av_bprint_finalize(&bp, &lst->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 v3 2/5] avcodec/bsf: Add list BSF API
From: Jan Sebechlebsky --- Changes since the last version of the patch: - removed av_freep(&bsf->item_name) from bsf_list_close since it belongs to next commit libavcodec/avcodec.h | 85 libavcodec/bsf.c | 278 +++ 2 files changed, 363 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..664565f 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,279 @@ 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; + +lst->bsfs[i]->time_base_in = tb; + +ret = av_bsf_init(lst->bsfs[i]); +if (ret < 0) +goto fail; + +cod_par = lst->bsfs[i]->par_out; +tb = lst->bsfs[i]->time_base_out; +} + +bsf->time_base_out = tb; +ret = avcodec_parameters_copy(bsf->par_out, cod_par); +
[FFmpeg-devel] [PATCH] doc/APIchanges: Document addition of list BSF API in lavc
From: Jan Sebechlebsky Signed-off-by: Jan Sebechlebsky --- doc/APIchanges | 7 +++ 1 file changed, 7 insertions(+) diff --git a/doc/APIchanges b/doc/APIchanges index 209ab41..4868d58 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -15,6 +15,13 @@ libavutil: 2015-08-28 API changes, most recent first: +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 v9 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 (false) by default. + +@end table + +@subsection Examples + +
[FFmpeg-devel] [PATCH v6 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan Sebechlebsky Add 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(&ctx->termination_requested)) +return 1; + +return ff_check_interrupt(&ctx->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(&fifo_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(&fifo->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(&avf2->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(&msg.pkt); @@ -548,15 +577,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, &msg, - 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, &msg, 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(&fifo->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(&fifo->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(&fifo->termination_requested, 1); +re
[FFmpeg-devel] [PATCH] avformat: Document thread-safety requirement for interrupt callback
From: Jan Sebechlebsky Muxing 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 v10 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 indefinitely. +@example +ffmpeg -re -i ... -c:v libx264 -c:a aa
[FFmpeg-devel] [PATCH v2] doc/APIchanges: Document addition of list BSF API in lavc
From: Jan Sebechlebsky Signed-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 v11 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 -fifo_format flv -map 0:v -map 0:a + -drop_pkts_on_o
[FFmpeg-devel] [PATCH v12 01/11] avformat: Add fifo pseudo-muxer
From: Jan Sebechlebsky Signed-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 (network outage) and attempt to recover +streaming every
[FFmpeg-devel] [PATCH v7 10/11] avformat/fifo: Add AVFMT_FLAG_NONBLOCK support
From: Jan Sebechlebsky Add 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(&ctx->termination_requested)) +return 1; + +return ff_check_interrupt(&ctx->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(&fifo_thread_ctx); +/* This must be only return path from fifo_consumer_thread function, + * so the thread_finised_flag is set. */ +avpriv_atomic_int_set(&fifo->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(&avf2->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(&msg.pkt); @@ -547,15 +576,21 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } -ret = av_thread_message_queue_send(fifo->queue, &msg, - 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, &msg, 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(&fifo->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(&fifo->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(&fifo->termination_requested, 1); +
[FFmpeg-devel] [PATCH v4 5/5] avformat/tee: Use BSF list API
From: Jan Sebechlebsky Signed-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, ",", &saveptr)) { -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 = &bsf->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(&tee_slave->bsfs[i]); } av_freep(&tee_slave->stream_map); av_freep(&tee_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, &tee_slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, &tee_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(&options, 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(&tee_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, avcodec_get_name(st
[FFmpeg-devel] [PATCH] libavcodec/bsfs: Fix bsf option setting
From: Jan Sebechlebsky AV_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 v5 5/5] avformat/tee: Use BSF list API
From: Jan Sebechlebsky Signed-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, ",", &saveptr)) { -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 = &bsf->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(&tee_slave->bsfs[i]); } av_freep(&tee_slave->stream_map); av_freep(&tee_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, &tee_slave->bsfs[i]); +ret = av_bsf_list_parse_str(entry->value, &tee_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(&options, 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(&tee_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 = slave->bsfs[i
[FFmpeg-devel] [PATCH 2/2] avformat/tee: Add FATE tests for tee
From: Jan Sebechlebsky This 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: audio +#codec_id 0: aac +#sample_rate 0: 48000 +#channel_l
[FFmpeg-devel] [PATCH 1/2] avformat/tee: Copy interrupt callback and flags to slave
From: Jan Sebechlebsky Copy 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] libavformat/tee: Add fifo support for tee
From: Jan Sebechlebsky Signed-off-by: Jan Sebechlebsky --- This commit makes use of fifo muxer together with tee muxer easier, fifo muxer does not have to be explicitly specified for each slave. For the most simple use case it is sufficient to turn fifo muxer on for all slaves by switching on use_fifo option. Same options can be passed to all fifo muxer instances of slaves by assigning them to fifo_options of tee. Both use_fifo option and individual fifo_options can be overriden per slave if needed. doc/muxers.texi | 20 + libavformat/tee.c | 89 ++- 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 9ec2e31..b88b83c 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1473,6 +1473,7 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@anchor{fifo} @section fifo The fifo pseudo-muxer allows the separation of encoding and muxing by using @@ -1580,6 +1581,18 @@ with the tee muxer; encoding can be a very expensive process. It is not useful when using the libavformat API directly because it is then possible to feed the same packets to several muxers directly. +@table @option + +@item use_fifo @var{bool} +If set to 1, slave outputs will be processed in separate thread using @ref{fifo} +muxer. This allows to compensate for different speed/latency/reliability of +outputs and setup transparent recovery. By default this feature is turned off. + +@item fifo_options +Options to pass to fifo pseudo-muxer instances. See @ref{fifo}. + +@end table + The slave outputs are specified in the file name given to the muxer, separated by '|'. If any of the slave name contains the '|' separator, leading or trailing spaces or any special character, it must be @@ -1601,6 +1614,13 @@ output name suffix. Specify a list of bitstream filters to apply to the specified output. +@item use_fifo @var{bool} +This allows to override tee muxer use_fifo option for individual slave muxer. + +@item fifo_options +This allows to override tee muxer fifo_options for individual slave muxer. +See @ref{fifo}. + It is possible to specify to which streams a given bitstream filter applies, by appending a stream specifier to the option separated by @code{/}. @var{spec} must be a stream specifier (see @ref{Format diff --git a/libavformat/tee.c b/libavformat/tee.c index d59ad4d..764135d 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -40,6 +40,8 @@ typedef struct { AVBSFContext **bsfs; ///< bitstream filters per stream SlaveFailurePolicy on_fail; +int use_fifo; +AVDictionary *fifo_options; /** map from input to output streams indexes, * disabled output streams are set to -1 */ @@ -52,15 +54,28 @@ typedef struct TeeContext { unsigned nb_slaves; unsigned nb_alive; TeeSlave *slaves; +int use_fifo; +AVDictionary *fifo_options; +char *fifo_options_str; } TeeContext; static const char *const slave_delim = "|"; static const char *const slave_bsfs_spec_sep = "/"; static const char *const slave_select_sep = ","; +#define OFFSET(x) offsetof(TeeContext, x) +static const AVOption options[] = { +{"use_fifo", "Use fifo pseudo-muxer to separate actual muxers from encoder", + OFFSET(use_fifo), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, +{"fifo_options", "fifo pseudo-muxer options", OFFSET(fifo_options_str), + AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, +{NULL} +}; + static const AVClass tee_muxer_class = { .class_name = "Tee muxer", .item_name = av_default_item_name, +.option = options, .version= LIBAVUTIL_VERSION_INT, }; @@ -81,6 +96,27 @@ static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *t return AVERROR(EINVAL); } +static int parse_slave_fifo_options(const char * use_fifo, +const char * fifo_options, TeeSlave * tee_slave) +{ +int ret = 0; + +if (use_fifo) { +if (av_match_name(use_fifo, "true,y,yes,enable,enabled,on,1")) { +tee_slave->use_fifo = 1; +} else if (av_match_name(use_fifo, "false,n,no,disable,disabled,off,0")) { +tee_slave->use_fifo = 0; +} else { +return AVERROR(EINVAL); +} +} + +if (fifo_options) +ret = av_dict_parse_string(&tee_slave->fifo_options, fifo_options, "=", ":", 0); + +return ret; +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -125,6 +161,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionaryEntry *entry; char *filename; char *format = NULL, *select = NULL, *on_fail = NULL; +char *use_fifo = NULL, *fifo_options_str = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int str
[FFmpeg-devel] [PATCH v3 1/3] Refactor close_slaves function in tee muxer
From: Jan Sebechlebsky Closing single slave operation is pulled out into separate function close_slave(TeeSlave*). Both close_slave and close_slaves function are moved before open_slave function. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 58 ++- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 1390705..50c81c4 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -135,6 +135,38 @@ end: return ret; } +static void close_slave(TeeSlave* tee_slave) +{ +AVFormatContext *avf; +unsigned i; + +avf = tee_slave->avf; +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; +} +} +av_freep(&tee_slave->stream_map); +av_freep(&tee_slave->bsfs); + +ff_format_io_close(avf,&avf->pb); +avformat_free_context(avf); +tee_slave->avf = NULL; +} + +static void close_slaves(AVFormatContext *avf) +{ +TeeContext *tee = avf->priv_data; +unsigned i; + +for (i = 0; i < tee->nb_slaves; i++) { +close_slave(&tee->slaves[i]); +} +} + static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) { int i, ret; @@ -311,32 +343,6 @@ end: return ret; } -static void close_slaves(AVFormatContext *avf) -{ -TeeContext *tee = avf->priv_data; -AVFormatContext *avf2; -unsigned i, j; - -for (i = 0; i < tee->nb_slaves; i++) { -avf2 = tee->slaves[i].avf; - -for (j = 0; j < avf2->nb_streams; j++) { -AVBitStreamFilterContext *bsf_next, *bsf = tee->slaves[i].bsfs[j]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} -av_freep(&tee->slaves[i].stream_map); -av_freep(&tee->slaves[i].bsfs); - -ff_format_io_close(avf2, &avf2->pb); -avformat_free_context(avf2); -tee->slaves[i].avf = NULL; -} -} - static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) { int i; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 2/3] Fix leak and crash in tee muxer when open_slave fails
From: Jan Sebechlebsky Calling close_slave in case error is to be returned from open_slave will free allocated resources. Since failure can happen before bsfs array is initialized, close_slave must check that bsfs is not NULL before accessing tee_slave->bsfs[i] element. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 19 +++ 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 50c81c4..f5dc443 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -141,12 +141,14 @@ static void close_slave(TeeSlave* tee_slave) unsigned i; avf = tee_slave->avf; -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; +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; +} } } av_freep(&tee_slave->stream_map); @@ -197,6 +199,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) goto end; +tee_slave->avf = avf2; av_dict_copy(&avf2->metadata, avf->metadata, 0); avf2->opaque = avf->opaque; avf2->io_open = avf->io_open; @@ -276,7 +279,6 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) goto end; } -tee_slave->avf = avf2; tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave)); if (!tee_slave->bsfs) { ret = AVERROR(ENOMEM); @@ -291,7 +293,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_log(avf, AV_LOG_ERROR, "Specifier separator in '%s' is '%c', but only characters '%s' " "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep); -return AVERROR(EINVAL); +ret = AVERROR(EINVAL); +goto end; } spec++; /* consume separator */ } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 1/3] Refactor close_slaves function in tee muxer
From: Jan Sebechlebsky Closing single slave operation is pulled out into separate function close_slave(TeeSlave*). Both close_slave and close_slaves function are moved before open_slave function. Signed-off-by: Jan Sebechlebsky --- I've missed bad spacing in close_slave argument in previous version of patch, this should be all right. libavformat/tee.c | 58 ++- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 1390705..bb043fe 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -135,6 +135,38 @@ end: return ret; } +static void close_slave(TeeSlave *tee_slave) +{ +AVFormatContext *avf; +unsigned i; + +avf = tee_slave->avf; +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; +} +} +av_freep(&tee_slave->stream_map); +av_freep(&tee_slave->bsfs); + +ff_format_io_close(avf,&avf->pb); +avformat_free_context(avf); +tee_slave->avf = NULL; +} + +static void close_slaves(AVFormatContext *avf) +{ +TeeContext *tee = avf->priv_data; +unsigned i; + +for (i = 0; i < tee->nb_slaves; i++) { +close_slave(&tee->slaves[i]); +} +} + static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) { int i, ret; @@ -311,32 +343,6 @@ end: return ret; } -static void close_slaves(AVFormatContext *avf) -{ -TeeContext *tee = avf->priv_data; -AVFormatContext *avf2; -unsigned i, j; - -for (i = 0; i < tee->nb_slaves; i++) { -avf2 = tee->slaves[i].avf; - -for (j = 0; j < avf2->nb_streams; j++) { -AVBitStreamFilterContext *bsf_next, *bsf = tee->slaves[i].bsfs[j]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} -av_freep(&tee->slaves[i].stream_map); -av_freep(&tee->slaves[i].bsfs); - -ff_format_io_close(avf2, &avf2->pb); -avformat_free_context(avf2); -tee->slaves[i].avf = NULL; -} -} - static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) { int i; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 3/3] Tee muxer improvement (handling slave failure)
From: Jan Sebechlebsky Adds per slave option 'onfail' to the tee muxer allowing an output to fail,so other slave outputs can continue. Signed-off-by: Jan Sebechlebsky --- doc/muxers.texi | 14 libavformat/tee.c | 96 +-- 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index c36c72c..5a40c31 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1367,6 +1367,12 @@ Select the streams that should be mapped to the slave output, specified by a stream specifier. If not specified, this defaults to all the input streams. You may use multiple stream specifiers separated by commas (@code{,}) e.g.: @code{a:0,v} + +@item onfail +Specify behaviour on output failure. This can be set to either @code{abort} (which is +default) or @code{ignore}. @code{abort} will cause whole process to fail in case of failure +on this slave output. @code{ignore} will ignore failure on this output, so other outputs +will continue without being affected. @end table @subsection Examples @@ -1381,6 +1387,14 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a @end example @item +As above, but continue streaming even if output to local file fails +(for example local drive fills up): +@example +ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a + "[onfail=ignore]archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/" +@end example + +@item Use @command{ffmpeg} to encode the input, and send the output to three different destinations. The @code{dump_extra} bitstream filter is used to add extradata information to all the output video diff --git a/libavformat/tee.c b/libavformat/tee.c index dfa5f46..3b0bada 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,20 @@ #define MAX_SLAVES 16 +typedef enum { +ON_SLAVE_FAILURE_ABORT = 1, +ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +SlaveFailurePolicy on_fail; +unsigned char is_alive; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -41,6 +51,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; +unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -135,6 +146,18 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt) +{ +if (!opt) { +return DEFAULT_SLAVE_FAILURE_POLICY; +} else if (!av_strcasecmp("abort", opt)) { +return ON_SLAVE_FAILURE_ABORT; +} else if (!av_strcasecmp("ignore", opt)) { +return ON_SLAVE_FAILURE_IGNORE; +} +return 0; +} + static void close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -165,7 +188,8 @@ static void close_slaves(AVFormatContext *avf) unsigned i; for (i = 0; i < tee->nb_slaves; i++) { -close_slave(&tee->slaves[i]); +if (tee->slaves[i].is_alive) +close_slave(&tee->slaves[i]); } } @@ -175,7 +199,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; -char *format = NULL, *select = NULL; +char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -195,6 +219,17 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); +STEAL_OPTION("onfail", on_fail); + +tee_slave->on_fail = parse_slave_failure_policy_option(on_fail); +if (!tee_slave->on_fail) { +av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); +ret = AVERROR(EINVAL); +/* Set failure behaviour to abort, so invalid option error will not be ignored */ +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +goto end; +} ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -339,8 +374,11 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) } end: +if (ret < 0) +close_slave(tee_slave); av_free(format); av_free(select); +av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -370,6 +408,31 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, + int err_n, unsigned char needs_closing) +{ +TeeContext *tee = avf->priv_data; +TeeSlave *tee_slave = &tee->sl
[FFmpeg-devel] [PATCH v4 2/3] avformat/tee: Fix leaks in tee muxer when open_slave fails
From: Jan Sebechlebsky Calling close_slave in case error is to be returned from open_slave will free allocated resources. Since failure can happen before bsfs array is initialized, close_slave must check that bsfs is not NULL before accessing tee_slave->bsfs[i] element. Signed-off-by: Jan Sebechlebsky --- I've fixed the spaces (same as previous commit) and commit message title. libavformat/tee.c | 19 +++ 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 5303def..d823805 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -141,12 +141,14 @@ static void close_slave(TeeSlave *tee_slave) unsigned i; avf = tee_slave->avf; -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; +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; +} } } av_freep(&tee_slave->stream_map); @@ -197,6 +199,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) goto end; +tee_slave->avf = avf2; av_dict_copy(&avf2->metadata, avf->metadata, 0); avf2->opaque = avf->opaque; avf2->io_open = avf->io_open; @@ -276,7 +279,6 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) goto end; } -tee_slave->avf = avf2; tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave)); if (!tee_slave->bsfs) { ret = AVERROR(ENOMEM); @@ -291,7 +293,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_log(avf, AV_LOG_ERROR, "Specifier separator in '%s' is '%c', but only characters '%s' " "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep); -return AVERROR(EINVAL); +ret = AVERROR(EINVAL); +goto end; } spec++; /* consume separator */ } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v4 3/3] avformat/tee: Handling slave failure in tee muxer
From: Jan Sebechlebsky Adds per slave option 'onfail' to the tee muxer allowing an output to fail,so other slave outputs can continue. Signed-off-by: Jan Sebechlebsky --- I've just added topic to commit message title as Marton Balint suggested. doc/muxers.texi | 14 libavformat/tee.c | 96 +-- 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 2aafbad..2d63083 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1372,6 +1372,12 @@ Select the streams that should be mapped to the slave output, specified by a stream specifier. If not specified, this defaults to all the input streams. You may use multiple stream specifiers separated by commas (@code{,}) e.g.: @code{a:0,v} + +@item onfail +Specify behaviour on output failure. This can be set to either @code{abort} (which is +default) or @code{ignore}. @code{abort} will cause whole process to fail in case of failure +on this slave output. @code{ignore} will ignore failure on this output, so other outputs +will continue without being affected. @end table @subsection Examples @@ -1386,6 +1392,14 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a @end example @item +As above, but continue streaming even if output to local file fails +(for example local drive fills up): +@example +ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a + "[onfail=ignore]archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/" +@end example + +@item Use @command{ffmpeg} to encode the input, and send the output to three different destinations. The @code{dump_extra} bitstream filter is used to add extradata information to all the output video diff --git a/libavformat/tee.c b/libavformat/tee.c index d823805..50aaf9f 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,20 @@ #define MAX_SLAVES 16 +typedef enum { +ON_SLAVE_FAILURE_ABORT = 1, +ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +SlaveFailurePolicy on_fail; +unsigned char is_alive; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -41,6 +51,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; +unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -135,6 +146,18 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt) +{ +if (!opt) { +return DEFAULT_SLAVE_FAILURE_POLICY; +} else if (!av_strcasecmp("abort", opt)) { +return ON_SLAVE_FAILURE_ABORT; +} else if (!av_strcasecmp("ignore", opt)) { +return ON_SLAVE_FAILURE_IGNORE; +} +return 0; +} + static void close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -165,7 +188,8 @@ static void close_slaves(AVFormatContext *avf) unsigned i; for (i = 0; i < tee->nb_slaves; i++) { -close_slave(&tee->slaves[i]); +if (tee->slaves[i].is_alive) +close_slave(&tee->slaves[i]); } } @@ -175,7 +199,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; -char *format = NULL, *select = NULL; +char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -195,6 +219,17 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); +STEAL_OPTION("onfail", on_fail); + +tee_slave->on_fail = parse_slave_failure_policy_option(on_fail); +if (!tee_slave->on_fail) { +av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); +ret = AVERROR(EINVAL); +/* Set failure behaviour to abort, so invalid option error will not be ignored */ +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +goto end; +} ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -339,8 +374,11 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) } end: +if (ret < 0) +close_slave(tee_slave); av_free(format); av_free(select); +av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -370,6 +408,31 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, + int err_n, unsigned char needs_closing) +
[FFmpeg-devel] [PATCH v4 1/3] avformat/tee: Refactor close_slaves function in tee muxer
From: Jan Sebechlebsky Closing single slave operation is pulled out into separate function close_slave(TeeSlave*). Both close_slave and close_slaves function are moved before open_slave function. Signed-off-by: Jan Sebechlebsky --- I've fixed missing spaces and added topic to commit message title. libavformat/tee.c | 58 ++- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 1390705..5303def 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -135,6 +135,38 @@ end: return ret; } +static void close_slave(TeeSlave *tee_slave) +{ +AVFormatContext *avf; +unsigned i; + +avf = tee_slave->avf; +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; +} +} +av_freep(&tee_slave->stream_map); +av_freep(&tee_slave->bsfs); + +ff_format_io_close(avf,&avf->pb); +avformat_free_context(avf); +tee_slave->avf = NULL; +} + +static void close_slaves(AVFormatContext *avf) +{ +TeeContext *tee = avf->priv_data; +unsigned i; + +for (i = 0; i < tee->nb_slaves; i++) { +close_slave(&tee->slaves[i]); +} +} + static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) { int i, ret; @@ -311,32 +343,6 @@ end: return ret; } -static void close_slaves(AVFormatContext *avf) -{ -TeeContext *tee = avf->priv_data; -AVFormatContext *avf2; -unsigned i, j; - -for (i = 0; i < tee->nb_slaves; i++) { -avf2 = tee->slaves[i].avf; - -for (j = 0; j < avf2->nb_streams; j++) { -AVBitStreamFilterContext *bsf_next, *bsf = tee->slaves[i].bsfs[j]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} -av_freep(&tee->slaves[i].stream_map); -av_freep(&tee->slaves[i].bsfs); - -ff_format_io_close(avf2, &avf2->pb); -avformat_free_context(avf2); -tee->slaves[i].avf = NULL; -} -} - static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) { int i; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v5 1/3] avformat/tee: Refactor close_slaves function in tee muxer
From: Jan Sebechlebsky Closing single slave operation is pulled out into separate function close_slave(TeeSlave*). Both close_slave and close_slaves function are moved before open_slave function. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 58 ++- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index bb344e6..ab6cd32 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -135,6 +135,38 @@ end: return ret; } +static void close_slave(TeeSlave *tee_slave) +{ +AVFormatContext *avf; +unsigned i; + +avf = tee_slave->avf; +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; +} +} +av_freep(&tee_slave->stream_map); +av_freep(&tee_slave->bsfs); + +ff_format_io_close(avf, &avf->pb); +avformat_free_context(avf); +tee_slave->avf = NULL; +} + +static void close_slaves(AVFormatContext *avf) +{ +TeeContext *tee = avf->priv_data; +unsigned i; + +for (i = 0; i < tee->nb_slaves; i++) { +close_slave(&tee->slaves[i]); +} +} + static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) { int i, ret; @@ -311,32 +343,6 @@ end: return ret; } -static void close_slaves(AVFormatContext *avf) -{ -TeeContext *tee = avf->priv_data; -AVFormatContext *avf2; -unsigned i, j; - -for (i = 0; i < tee->nb_slaves; i++) { -avf2 = tee->slaves[i].avf; - -for (j = 0; j < avf2->nb_streams; j++) { -AVBitStreamFilterContext *bsf_next, *bsf = tee->slaves[i].bsfs[j]; -while (bsf) { -bsf_next = bsf->next; -av_bitstream_filter_close(bsf); -bsf = bsf_next; -} -} -av_freep(&tee->slaves[i].stream_map); -av_freep(&tee->slaves[i].bsfs); - -ff_format_io_close(avf2, &avf2->pb); -avformat_free_context(avf2); -tee->slaves[i].avf = NULL; -} -} - static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) { int i; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v5 2/3] avformat/tee: Fix leaks in tee muxer when open_slave fails
From: Jan Sebechlebsky Calling close_slave in case error is to be returned from open_slave will free allocated resources. Since failure can happen before bsfs array is initialized, close_slave must check that bsfs is not NULL before accessing tee_slave->bsfs[i] element. Slave muxer expects write_trailer to be called if it's write_header suceeded (so resources allocated in write_header are freed). Therefore if failure happens after successfull write_header call, we must ensure that write_trailer of that particular slave is called. Signed-off-by: Jan Sebechlebsky --- libavformat/tee.c | 44 +--- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index ab6cd32..222826a 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -141,12 +141,17 @@ static void close_slave(TeeSlave *tee_slave) unsigned i; avf = tee_slave->avf; -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; +if (!avf) +return; + +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; +} } } av_freep(&tee_slave->stream_map); @@ -197,6 +202,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) goto end; +tee_slave->avf = avf2; av_dict_copy(&avf2->metadata, avf->metadata, 0); avf2->opaque = avf->opaque; avf2->io_open = avf->io_open; @@ -276,11 +282,10 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) goto end; } -tee_slave->avf = avf2; tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave)); if (!tee_slave->bsfs) { ret = AVERROR(ENOMEM); -goto end; +goto fail; } entry = NULL; @@ -291,7 +296,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_log(avf, AV_LOG_ERROR, "Specifier separator in '%s' is '%c', but only characters '%s' " "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep); -return AVERROR(EINVAL); +ret = AVERROR(EINVAL); +goto fail; } spec++; /* consume separator */ } @@ -302,7 +308,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_log(avf, AV_LOG_ERROR, "Invalid stream specifier '%s' in bsfs option '%s' for slave " "output '%s'\n", spec, entry->key, filename); -goto end; +goto fail; } if (ret > 0) { @@ -319,7 +325,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_log(avf, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s' associated to " "stream %d of slave output '%s'\n", entry->value, i, filename); -goto end; +goto fail; } } } @@ -332,7 +338,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key); ret = AVERROR_OPTION_NOT_FOUND; -goto end; +goto fail; } end: @@ -341,6 +347,9 @@ end: av_dict_free(&options); av_freep(&tmp_select); return ret; +fail: +av_write_trailer(avf2); +goto end; } static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) @@ -390,15 +399,18 @@ static int tee_write_header(AVFormatContext *avf) filename++; } +tee->nb_slaves = 0; + for (i = 0; i < nb_slaves; i++) { -if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) +if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) { +close_slave(&tee->slaves[i]); goto fail; +} log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE); av_freep(&slaves[i]); +tee->nb_slaves++; } -tee->nb_slaves = nb_slaves; - for (i = 0; i < avf->nb_streams; i++) { int j, mapped = 0; for (j = 0; j < tee->nb_slaves; j++) @@ -412,6 +424,8 @@ static int tee_write_header(AVFormatContext *avf) fail: for (i = 0; i < nb
[FFmpeg-devel] [PATCH v5 3/3] avformat/tee: Handling slave failure in tee muxer
From: Jan Sebechlebsky Adds per slave option 'onfail' to the tee muxer allowing an output to fail,so other slave outputs can continue. Signed-off-by: Jan Sebechlebsky --- doc/muxers.texi | 14 libavformat/tee.c | 102 +- 2 files changed, 100 insertions(+), 16 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 2aafbad..2d63083 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1372,6 +1372,12 @@ Select the streams that should be mapped to the slave output, specified by a stream specifier. If not specified, this defaults to all the input streams. You may use multiple stream specifiers separated by commas (@code{,}) e.g.: @code{a:0,v} + +@item onfail +Specify behaviour on output failure. This can be set to either @code{abort} (which is +default) or @code{ignore}. @code{abort} will cause whole process to fail in case of failure +on this slave output. @code{ignore} will ignore failure on this output, so other outputs +will continue without being affected. @end table @subsection Examples @@ -1386,6 +1392,14 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a @end example @item +As above, but continue streaming even if output to local file fails +(for example local drive fills up): +@example +ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a + "[onfail=ignore]archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/" +@end example + +@item Use @command{ffmpeg} to encode the input, and send the output to three different destinations. The @code{dump_extra} bitstream filter is used to add extradata information to all the output video diff --git a/libavformat/tee.c b/libavformat/tee.c index 222826a..5f8c121 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,19 @@ #define MAX_SLAVES 16 +typedef enum { +ON_SLAVE_FAILURE_ABORT = 1, +ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +SlaveFailurePolicy on_fail; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -41,6 +50,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; +unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -135,6 +145,18 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt) +{ +if (!opt) { +return DEFAULT_SLAVE_FAILURE_POLICY; +} else if (!av_strcasecmp("abort", opt)) { +return ON_SLAVE_FAILURE_ABORT; +} else if (!av_strcasecmp("ignore", opt)) { +return ON_SLAVE_FAILURE_IGNORE; +} +return AVERROR(EINVAL); +} + static void close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -178,7 +200,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; -char *format = NULL, *select = NULL; +char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -198,6 +220,17 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); +STEAL_OPTION("onfail", on_fail); + +tee_slave->on_fail = parse_slave_failure_policy_option(on_fail); +if (tee_slave->on_fail < 0) { +av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); +ret = AVERROR(EINVAL); +/* Set failure behaviour to abort, so invalid option error will not be ignored */ +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +goto end; +} ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -344,6 +377,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) end: av_free(format); av_free(select); +av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -376,6 +410,32 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, + int err_n, unsigned char write_trailer) +{ +TeeContext *tee = avf->priv_data; +TeeSlave *tee_slave = &tee->slaves[slave_idx]; + +tee->nb_alive--; + +if (write_trailer) +av_write_trailer(tee_slave->avf); + +close_slave(tee_slave); + +if (!tee->nb_alive) { +av_log(avf, AV_LOG_ERROR, "All tee outputs failed.\n"); +return err_n; +} else if (tee_slave->on_fail == ON_SLAVE_FAILURE_ABORT) { +av_
[FFmpeg-devel] [GSOC] Handling slave failure in tee muxer
Hello, I'm sorry for delay. I made a few modifications to the patchset from the last version apart from issues pointed by Marton: - I discovered another source of leaks - slave muxers often allocate memory in write_header call and free them in write_trailer. write_trailer is called if write_header succeeded before. However in the tee when i-th slave failed to open, write_trailer was never called on already succesfully initializated slaves. I've changed the code in second patch so if failure happens inside open_slave function after write_header call, write_trailer is called before open_slave function returns. write_trailer is also called on all already initialized (alive) slaves. If failure happens later (in write_packet), if we are not handling slave failure this is not a problem since master write_trailer will be called anyway. If we are handling slave failure (third patch), the slave is closed immediately after failure happens, so I write_trailer is called inside tee_process_slave_failure in that case. - I removed is_alive field from TeeSlave structure, since avf field can be used instead. I've tested several scenarios with valgrind, I believe there should be no more leaks in tee after this patchset is applied. Marton, Nicolas - What do you think? Jan S. ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v6 3/3] avformat/tee: Handling slave failure in tee muxer
From: Jan Sebechlebsky Adds per slave option 'onfail' to the tee muxer allowing an output to fail,so other slave outputs can continue. Signed-off-by: Jan Sebechlebsky --- Thanks for pointing on the problem with testing enum for negative value. I've fixed that, so parse_slave_failure_policy_option returns only error value and sets value of enum by using pointer - I think it also looks nicer to have it this way. doc/muxers.texi | 14 libavformat/tee.c | 104 +- 2 files changed, 102 insertions(+), 16 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 042efce..c62d4b5 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1453,6 +1453,12 @@ Select the streams that should be mapped to the slave output, specified by a stream specifier. If not specified, this defaults to all the input streams. You may use multiple stream specifiers separated by commas (@code{,}) e.g.: @code{a:0,v} + +@item onfail +Specify behaviour on output failure. This can be set to either @code{abort} (which is +default) or @code{ignore}. @code{abort} will cause whole process to fail in case of failure +on this slave output. @code{ignore} will ignore failure on this output, so other outputs +will continue without being affected. @end table @subsection Examples @@ -1467,6 +1473,14 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a @end example @item +As above, but continue streaming even if output to local file fails +(for example local drive fills up): +@example +ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a + "[onfail=ignore]archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/" +@end example + +@item Use @command{ffmpeg} to encode the input, and send the output to three different destinations. The @code{dump_extra} bitstream filter is used to add extradata information to all the output video diff --git a/libavformat/tee.c b/libavformat/tee.c index 222826a..a6892c2 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,19 @@ #define MAX_SLAVES 16 +typedef enum { +ON_SLAVE_FAILURE_ABORT = 1, +ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +SlaveFailurePolicy on_fail; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -41,6 +50,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; +unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -135,6 +145,23 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) +{ +if (!opt) { +tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY; +return 0; +} else if (!av_strcasecmp("abort", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return 0; +} else if (!av_strcasecmp("ignore", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_IGNORE; +return 0; +} +/* Set failure behaviour to abort, so invalid option error will not be ignored */ +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return AVERROR(EINVAL); +} + static void close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -178,7 +205,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; -char *format = NULL, *select = NULL; +char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -198,6 +225,14 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); +STEAL_OPTION("onfail", on_fail); + +ret = parse_slave_failure_policy_option(on_fail, tee_slave); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); +goto end; +} ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -344,6 +379,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) end: av_free(format); av_free(select); +av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -376,6 +412,32 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, + int err_n, unsigned char write_trailer) +{ +TeeContext *tee = avf->priv_data; +TeeSlave *tee_slave = &tee->slaves[slave_idx]; + +
[FFmpeg-devel] [PATCH v8 3/3][GSOC] avformat/tee: Handling slave failure in tee muxer
From: Jan Sebechlebsky Adds per slave option 'onfail' to the tee muxer allowing an output to fail,so other slave outputs can continue. Signed-off-by: Jan Sebechlebsky --- Sorry I've missed those. It is fixed in this patch. Regards, Jan S. doc/muxers.texi | 14 + libavformat/tee.c | 94 --- 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 042efce..c62d4b5 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1453,6 +1453,12 @@ Select the streams that should be mapped to the slave output, specified by a stream specifier. If not specified, this defaults to all the input streams. You may use multiple stream specifiers separated by commas (@code{,}) e.g.: @code{a:0,v} + +@item onfail +Specify behaviour on output failure. This can be set to either @code{abort} (which is +default) or @code{ignore}. @code{abort} will cause whole process to fail in case of failure +on this slave output. @code{ignore} will ignore failure on this output, so other outputs +will continue without being affected. @end table @subsection Examples @@ -1467,6 +1473,14 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a @end example @item +As above, but continue streaming even if output to local file fails +(for example local drive fills up): +@example +ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a + "[onfail=ignore]archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/" +@end example + +@item Use @command{ffmpeg} to encode the input, and send the output to three different destinations. The @code{dump_extra} bitstream filter is used to add extradata information to all the output video diff --git a/libavformat/tee.c b/libavformat/tee.c index 753f7ea..01c8c02 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,19 @@ #define MAX_SLAVES 16 +typedef enum { +ON_SLAVE_FAILURE_ABORT = 1, +ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +SlaveFailurePolicy on_fail; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -42,6 +51,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; +unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -136,6 +146,23 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) +{ +if (!opt) { +tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY; +return 0; +} else if (!av_strcasecmp("abort", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return 0; +} else if (!av_strcasecmp("ignore", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_IGNORE; +return 0; +} +/* Set failure behaviour to abort, so invalid option error will not be ignored */ +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return AVERROR(EINVAL); +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -184,7 +211,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; -char *format = NULL, *select = NULL; +char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -204,6 +231,14 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); +STEAL_OPTION("onfail", on_fail); + +ret = parse_slave_failure_policy_option(on_fail, tee_slave); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); +goto end; +} ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -351,6 +386,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) end: av_free(format); av_free(select); +av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -380,6 +416,28 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, int err_n) +{ +TeeContext *tee = avf->priv_data; +TeeSlave *tee_slave = &tee->slaves[slave_idx]; + +tee->nb_alive--; + +close_slave(tee_slave); + +if (!tee->nb_alive) { +av_log(avf, AV_LOG_ERROR, "All tee outputs failed.\n"); +return err_n; +} else if (tee_slave->on_fail == ON_SLAVE_FAILURE_ABORT) { +av_lo
[FFmpeg-devel] [PATCH v9 3/3][GSOC] avformat/tee: Handling slave failure in tee muxer
From: Jan Sebechlebsky Adds per slave option 'onfail' to the tee muxer allowing an output to fail,so other slave outputs can continue. Signed-off-by: Jan Sebechlebsky --- This is embarassing, sorry for that! You're right in both, I've tried to merge my old file with the changes in previous patch and it obviosly didn't end well. Next time I'll try to edit it manually to notice such mistakes. Hopefully, this patch will be final. Regards, Jan S. doc/muxers.texi | 14 + libavformat/tee.c | 92 --- 2 files changed, 95 insertions(+), 11 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 042efce..c62d4b5 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1453,6 +1453,12 @@ Select the streams that should be mapped to the slave output, specified by a stream specifier. If not specified, this defaults to all the input streams. You may use multiple stream specifiers separated by commas (@code{,}) e.g.: @code{a:0,v} + +@item onfail +Specify behaviour on output failure. This can be set to either @code{abort} (which is +default) or @code{ignore}. @code{abort} will cause whole process to fail in case of failure +on this slave output. @code{ignore} will ignore failure on this output, so other outputs +will continue without being affected. @end table @subsection Examples @@ -1467,6 +1473,14 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a @end example @item +As above, but continue streaming even if output to local file fails +(for example local drive fills up): +@example +ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a + "[onfail=ignore]archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/" +@end example + +@item Use @command{ffmpeg} to encode the input, and send the output to three different destinations. The @code{dump_extra} bitstream filter is used to add extradata information to all the output video diff --git a/libavformat/tee.c b/libavformat/tee.c index 753f7ea..ca74cb1 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,19 @@ #define MAX_SLAVES 16 +typedef enum { +ON_SLAVE_FAILURE_ABORT = 1, +ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +SlaveFailurePolicy on_fail; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -42,6 +51,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; +unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -136,6 +146,23 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) +{ +if (!opt) { +tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY; +return 0; +} else if (!av_strcasecmp("abort", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return 0; +} else if (!av_strcasecmp("ignore", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_IGNORE; +return 0; +} +/* Set failure behaviour to abort, so invalid option error will not be ignored */ +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return AVERROR(EINVAL); +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -184,7 +211,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; -char *format = NULL, *select = NULL; +char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -204,6 +231,14 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); +STEAL_OPTION("onfail", on_fail); + +ret = parse_slave_failure_policy_option(on_fail, tee_slave); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); +goto end; +} ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -351,6 +386,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) end: av_free(format); av_free(select); +av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -380,6 +416,28 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, int err_n) +{ +TeeContext *tee = avf->priv_data; +TeeSlave *tee_slave = &tee->slaves[slave_idx]; + +tee->nb_alive--; + +clos
[FFmpeg-devel] [PATCH v10 3/3][GSOC] avformat/tee: Handling slave failure in tee muxer
From: Jan Sebechlebsky Adds per slave option 'onfail' to the tee muxer allowing an output to fail,so other slave outputs can continue. Signed-off-by: Jan Sebechlebsky --- doc/muxers.texi | 14 libavformat/tee.c | 95 --- 2 files changed, 98 insertions(+), 11 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 042efce..c62d4b5 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1453,6 +1453,12 @@ Select the streams that should be mapped to the slave output, specified by a stream specifier. If not specified, this defaults to all the input streams. You may use multiple stream specifiers separated by commas (@code{,}) e.g.: @code{a:0,v} + +@item onfail +Specify behaviour on output failure. This can be set to either @code{abort} (which is +default) or @code{ignore}. @code{abort} will cause whole process to fail in case of failure +on this slave output. @code{ignore} will ignore failure on this output, so other outputs +will continue without being affected. @end table @subsection Examples @@ -1467,6 +1473,14 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a @end example @item +As above, but continue streaming even if output to local file fails +(for example local drive fills up): +@example +ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a + "[onfail=ignore]archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/" +@end example + +@item Use @command{ffmpeg} to encode the input, and send the output to three different destinations. The @code{dump_extra} bitstream filter is used to add extradata information to all the output video diff --git a/libavformat/tee.c b/libavformat/tee.c index 753f7ea..d7613f6 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,19 @@ #define MAX_SLAVES 16 +typedef enum { +ON_SLAVE_FAILURE_ABORT = 1, +ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +SlaveFailurePolicy on_fail; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -42,6 +51,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; +unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -136,6 +146,23 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) +{ +if (!opt) { +tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY; +return 0; +} else if (!av_strcasecmp("abort", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return 0; +} else if (!av_strcasecmp("ignore", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_IGNORE; +return 0; +} +/* Set failure behaviour to abort, so invalid option error will not be ignored */ +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return AVERROR(EINVAL); +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -184,7 +211,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; -char *format = NULL, *select = NULL; +char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -204,6 +231,14 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); +STEAL_OPTION("onfail", on_fail); + +ret = parse_slave_failure_policy_option(on_fail, tee_slave); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); +goto end; +} ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -351,6 +386,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) end: av_free(format); av_free(select); +av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -380,6 +416,32 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, int err_n) +{ +char errbuf[128]; +const char *errbuf_ptr = errbuf; +TeeContext *tee = avf->priv_data; +TeeSlave *tee_slave = &tee->slaves[slave_idx]; + +tee->nb_alive--; + +close_slave(tee_slave); + +if (!tee->nb_alive) { +av_log(avf, AV_LOG_ERROR, "All tee outputs failed.\n"); +return err_n; +} else if (tee_slave->on_fail == ON_SLAVE_FAILURE_ABORT) { +av_log(avf, AV_LOG
Re: [FFmpeg-devel] [PATCH v9 3/3][GSOC] avformat/tee: Handling slave failure in tee muxer
Understood :) I've modified the patch so the error is printed in that case. I'm sending both yet unapplied patches (So you don't have to search in mailing list for the most recent version of second one). Can you apply them, if it's OK now? I guess I can then create patch to replace calls to deprecated API. Regards, Jan ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v10 2/3][GSOC] avformat/tee: Fix leaks in tee muxer when open_slave fails
From: Jan Sebechlebsky In open_slave failure can happen before bsfs array is initialized, close_slave must check that bsfs is not NULL before accessing tee_slave->bsfs[i] element. Slave muxer expects write_trailer to be called if it's write_header suceeded (so resources allocated in write_header are freed). Therefore if failure happens after successfull write_header call, we must ensure that write_trailer of that particular slave is called. Some cleanups are made by Marton Balint. Signed-off-by: Jan Sebechlebsky Signed-off-by: Marton Balint --- libavformat/tee.c | 42 +- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index ab6cd32..753f7ea 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -36,6 +36,7 @@ typedef struct { /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; +int header_written; } TeeSlave; typedef struct TeeContext { @@ -135,18 +136,27 @@ end: return ret; } -static void close_slave(TeeSlave *tee_slave) +static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; unsigned i; +int ret = 0; avf = tee_slave->avf; -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; +if (!avf) +return 0; + +if (tee_slave->header_written) +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; +} } } av_freep(&tee_slave->stream_map); @@ -155,6 +165,7 @@ static void close_slave(TeeSlave *tee_slave) ff_format_io_close(avf, &avf->pb); avformat_free_context(avf); tee_slave->avf = NULL; +return ret; } static void close_slaves(AVFormatContext *avf) @@ -197,6 +208,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) goto end; +tee_slave->avf = avf2; av_dict_copy(&avf2->metadata, avf->metadata, 0); avf2->opaque = avf->opaque; avf2->io_open = avf->io_open; @@ -275,8 +287,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) slave, av_err2str(ret)); goto end; } +tee_slave->header_written = 1; -tee_slave->avf = avf2; tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave)); if (!tee_slave->bsfs) { ret = AVERROR(ENOMEM); @@ -291,7 +303,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_log(avf, AV_LOG_ERROR, "Specifier separator in '%s' is '%c', but only characters '%s' " "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep); -return AVERROR(EINVAL); +ret = AVERROR(EINVAL); +goto end; } spec++; /* consume separator */ } @@ -390,6 +403,8 @@ static int tee_write_header(AVFormatContext *avf) filename++; } +tee->nb_slaves = nb_slaves; + for (i = 0; i < nb_slaves; i++) { if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) goto fail; @@ -397,8 +412,6 @@ static int tee_write_header(AVFormatContext *avf) av_freep(&slaves[i]); } -tee->nb_slaves = nb_slaves; - for (i = 0; i < avf->nb_streams; i++) { int j, mapped = 0; for (j = 0; j < tee->nb_slaves; j++) @@ -419,19 +432,14 @@ fail: static int tee_write_trailer(AVFormatContext *avf) { TeeContext *tee = avf->priv_data; -AVFormatContext *avf2; int ret_all = 0, ret; unsigned i; for (i = 0; i < tee->nb_slaves; i++) { -avf2 = tee->slaves[i].avf; -if ((ret = av_write_trailer(avf2)) < 0) +if ((ret = close_slave(&tee->slaves[i])) < 0) if (!ret_all) ret_all = ret; -if (!(avf2->oformat->flags & AVFMT_NOFILE)) -ff_format_io_close(avf2, &avf2->pb); } -close_slaves(avf); return ret_all; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v11 3/3][GSOC] avformat/tee: Handling slave failure in tee muxer
From: Jan Sebechlebsky Adds per slave option 'onfail' to the tee muxer allowing an output to fail,so other slave outputs can continue. Signed-off-by: Jan Sebechlebsky --- Changes from last version: -> Use av_err2str in tee_process_slave_failure instead of combination of av_strerror and strerror doc/muxers.texi | 14 + libavformat/tee.c | 91 --- 2 files changed, 94 insertions(+), 11 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 042efce..c62d4b5 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1453,6 +1453,12 @@ Select the streams that should be mapped to the slave output, specified by a stream specifier. If not specified, this defaults to all the input streams. You may use multiple stream specifiers separated by commas (@code{,}) e.g.: @code{a:0,v} + +@item onfail +Specify behaviour on output failure. This can be set to either @code{abort} (which is +default) or @code{ignore}. @code{abort} will cause whole process to fail in case of failure +on this slave output. @code{ignore} will ignore failure on this output, so other outputs +will continue without being affected. @end table @subsection Examples @@ -1467,6 +1473,14 @@ ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a @end example @item +As above, but continue streaming even if output to local file fails +(for example local drive fills up): +@example +ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a + "[onfail=ignore]archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/" +@end example + +@item Use @command{ffmpeg} to encode the input, and send the output to three different destinations. The @code{dump_extra} bitstream filter is used to add extradata information to all the output video diff --git a/libavformat/tee.c b/libavformat/tee.c index 753f7ea..499ef33 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,19 @@ #define MAX_SLAVES 16 +typedef enum { +ON_SLAVE_FAILURE_ABORT = 1, +ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream +SlaveFailurePolicy on_fail; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -42,6 +51,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; +unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -136,6 +146,23 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) +{ +if (!opt) { +tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY; +return 0; +} else if (!av_strcasecmp("abort", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return 0; +} else if (!av_strcasecmp("ignore", opt)) { +tee_slave->on_fail = ON_SLAVE_FAILURE_IGNORE; +return 0; +} +/* Set failure behaviour to abort, so invalid option error will not be ignored */ +tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; +return AVERROR(EINVAL); +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -184,7 +211,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; -char *format = NULL, *select = NULL; +char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -204,6 +231,14 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); +STEAL_OPTION("onfail", on_fail); + +ret = parse_slave_failure_policy_option(on_fail, tee_slave); +if (ret < 0) { +av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); +goto end; +} ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -351,6 +386,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) end: av_free(format); av_free(select); +av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -380,6 +416,28 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, int err_n) +{ +TeeContext *tee = avf->priv_data; +TeeSlave *tee_slave = &tee->slaves[slave_idx]; + +tee->nb_alive--; + +close_slave(tee_slave); + +if (!tee->nb_alive) { +av_log(avf, AV_LOG_ERROR, "All tee outputs failed.\n"); +return err_n; +} else i
[FFmpeg-devel] [PATCH 1/2] avformat/tee: Fix TeeSlave.bsfs pointer array size
From: Jan Sebechlebsky TeeSlave.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 --- libavformat/tee.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 499ef33..879d5b8 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(AVBitStreamFilterContext*)); 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 2/2] avformat/tee: Use ref instead copy in write_packet
From: Jan Sebechlebsky Replace av_copy_packet and deprecated av_dup_packet by creating reference using av_packet_ref. Signed-off-by: Jan Sebechlebsky --- This should be effectively the same as calling av_packet_clone, but without dynamic memory allocation (reuses local AVPacket pkt). libavformat/tee.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libavformat/tee.c b/libavformat/tee.c index 879d5b8..1891f9b 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -527,8 +527,8 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) if (s2 < 0) continue; -if ((ret = av_copy_packet(&pkt2, pkt)) < 0 || -(ret = av_dup_packet(&pkt2))< 0) +memset(&pkt2, 0, sizeof(AVPacket)); +if ((ret = av_packet_ref(&pkt2, pkt)) < 0) if (!ret_all) { ret_all = ret; continue; -- 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 Sebechlebsky This 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, &in); +if ((ret = ff_bsf_get_packet(ctx, &in)) < 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 v2] avcodec/mjpeg2jpeg_bsf: Check ff_bsf_get_packet success
From: Jan Sebechlebsky This 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, &in); +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 v2 1/2] avformat/tee: Fix TeeSlave.bsfs pointer array size
From: Jan Sebechlebsky TeeSlave.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] avformat/tee: Move to new BSF API
From: Jan Sebechlebsky Signed-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, ",", &saveptr)) { -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, &bsf_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 = &bsf->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 = &bsf_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(&bsf_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(¤t->bsf_ctx); +next = current->next; +av_free(current); +current = next; +} +} + +static int flush_bsfs(TeeSlave *tee_slave) +{ +AVFormatContext *avf = tee_slave->avf; +int i; +int ret, retAll = 0; + +
[FFmpeg-devel] [PATCH] libavutil/fifo: Fix fifo grow step
From: Jan Sebechlebsky Fifo 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