lavf/mux: add avformat_init_output

This allows a consumer to run the muxer's init function without actually
writing the header, which is useful in chained muxers that support
automatic bitstream filtering.
This commit is contained in:
Rodger Combs 2016-06-24 22:02:50 -05:00
parent 8a24e03684
commit a246fef163
No known key found for this signature in database
GPG Key ID: E3E54DCDCD3CB843
5 changed files with 100 additions and 17 deletions

View File

@ -15,6 +15,9 @@ libavutil: 2015-08-28
API changes, most recent first: API changes, most recent first:
2016-10-24 - xxxxxxx - lavf 57.54.100 - avformat.h
Add avformat_init_output() and AVSTREAM_INIT_IN_ macros
2016-10-22 - xxxxxxx - lavu 55.33.100 - avassert.h 2016-10-22 - xxxxxxx - lavu 55.33.100 - avassert.h
Add av_assert0_fpu() / av_assert2_fpu() Add av_assert0_fpu() / av_assert2_fpu()

View File

@ -618,6 +618,8 @@ typedef struct AVOutputFormat {
* AVStream parameters that need to be set before packets are sent. * AVStream parameters that need to be set before packets are sent.
* This method must not write output. * This method must not write output.
* *
* Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure
*
* Any allocations made here must be freed in deinit(). * Any allocations made here must be freed in deinit().
*/ */
int (*init)(struct AVFormatContext *); int (*init)(struct AVFormatContext *);
@ -2374,6 +2376,10 @@ void avformat_close_input(AVFormatContext **s);
* @addtogroup lavf_encoding * @addtogroup lavf_encoding
* @{ * @{
*/ */
#define AVSTREAM_INIT_IN_WRITE_HEADER 0 ///< stream parameters initialized in avformat_write_header
#define AVSTREAM_INIT_IN_INIT_OUTPUT 1 ///< stream parameters initialized in avformat_init_output
/** /**
* Allocate the stream private data and write the stream header to * Allocate the stream private data and write the stream header to
* an output media file. * an output media file.
@ -2385,13 +2391,37 @@ void avformat_close_input(AVFormatContext **s);
* On return this parameter will be destroyed and replaced with a dict containing * On return this parameter will be destroyed and replaced with a dict containing
* options that were not found. May be NULL. * options that were not found. May be NULL.
* *
* @return 0 on success, negative AVERROR on failure. * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec had not already been fully initialized in avformat_init,
* AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec had already been fully initialized in avformat_init,
* negative AVERROR on failure.
* *
* @see av_opt_find, av_dict_set, avio_open, av_oformat_next. * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_init_output.
*/ */
av_warn_unused_result av_warn_unused_result
int avformat_write_header(AVFormatContext *s, AVDictionary **options); int avformat_write_header(AVFormatContext *s, AVDictionary **options);
/**
* Allocate the stream private data and initialize the codec, but do not write the header.
* May optionally be used before avformat_write_header to initialize stream parameters
* before actually writing the header.
* If using this function, do not pass the same options to avformat_write_header.
*
* @param s Media file handle, must be allocated with avformat_alloc_context().
* Its oformat field must be set to the desired output format;
* Its pb field must be set to an already opened AVIOContext.
* @param options An AVDictionary filled with AVFormatContext and muxer-private options.
* On return this parameter will be destroyed and replaced with a dict containing
* options that were not found. May be NULL.
*
* @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec requires avformat_write_header to fully initialize,
* AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec has been fully initialized,
* negative AVERROR on failure.
*
* @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_write_header.
*/
av_warn_unused_result
int avformat_init_output(AVFormatContext *s, AVDictionary **options);
/** /**
* Write a packet to an output media file. * Write a packet to an output media file.
* *

View File

@ -130,6 +130,16 @@ struct AVFormatInternal {
* Timestamp of the end of the shortest stream. * Timestamp of the end of the shortest stream.
*/ */
int64_t shortest_end; int64_t shortest_end;
/**
* Whether or not avformat_init_output has already been called
*/
int initialized;
/**
* Whether or not avformat_init_output fully initialized streams
*/
int streams_initialized;
}; };
struct AVStreamInternal { struct AVStreamInternal {

View File

@ -423,10 +423,13 @@ FF_ENABLE_DEPRECATION_WARNINGS
*options = tmp; *options = tmp;
} }
if (s->oformat->init && (ret = s->oformat->init(s)) < 0) { if (s->oformat->init) {
if (s->oformat->deinit) if ((ret = s->oformat->init(s)) < 0) {
s->oformat->deinit(s); if (s->oformat->deinit)
goto fail; s->oformat->deinit(s);
return ret;
}
return ret == 0;
} }
return 0; return 0;
@ -493,31 +496,64 @@ static int write_header_internal(AVFormatContext *s)
return 0; return 0;
} }
int avformat_write_header(AVFormatContext *s, AVDictionary **options) int avformat_init_output(AVFormatContext *s, AVDictionary **options)
{ {
int ret = 0; int ret = 0;
if ((ret = init_muxer(s, options)) < 0) if ((ret = init_muxer(s, options)) < 0)
return ret; return ret;
s->internal->initialized = 1;
s->internal->streams_initialized = ret;
if (s->oformat->init && ret) {
if ((ret = init_pts(s)) < 0)
return ret;
if (s->avoid_negative_ts < 0) {
av_assert2(s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO);
if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {
s->avoid_negative_ts = 0;
} else
s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;
}
return AVSTREAM_INIT_IN_INIT_OUTPUT;
}
return AVSTREAM_INIT_IN_WRITE_HEADER;
}
int avformat_write_header(AVFormatContext *s, AVDictionary **options)
{
int ret = 0;
int already_initialized = s->internal->initialized;
int streams_already_initialized = s->internal->streams_initialized;
if (!already_initialized)
if ((ret = avformat_init_output(s, options)) < 0)
return ret;
if (!(s->oformat->check_bitstream && s->flags & AVFMT_FLAG_AUTO_BSF)) { if (!(s->oformat->check_bitstream && s->flags & AVFMT_FLAG_AUTO_BSF)) {
ret = write_header_internal(s); ret = write_header_internal(s);
if (ret < 0) if (ret < 0)
goto fail; goto fail;
} }
if ((ret = init_pts(s)) < 0) if (!s->internal->streams_initialized) {
goto fail; if ((ret = init_pts(s)) < 0)
goto fail;
if (s->avoid_negative_ts < 0) { if (s->avoid_negative_ts < 0) {
av_assert2(s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO); av_assert2(s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO);
if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) { if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {
s->avoid_negative_ts = 0; s->avoid_negative_ts = 0;
} else } else
s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE; s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;
}
} }
return 0; return streams_already_initialized;
fail: fail:
if (s->oformat->deinit) if (s->oformat->deinit)
@ -1292,6 +1328,10 @@ fail:
if (s->oformat->deinit) if (s->oformat->deinit)
s->oformat->deinit(s); s->oformat->deinit(s);
s->internal->header_written =
s->internal->initialized =
s->internal->streams_initialized = 0;
if (s->pb) if (s->pb)
avio_flush(s->pb); avio_flush(s->pb);
if (ret == 0) if (ret == 0)

View File

@ -32,7 +32,7 @@
// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
// Also please add any ticket numbers that you believe might be affected here // Also please add any ticket numbers that you believe might be affected here
#define LIBAVFORMAT_VERSION_MAJOR 57 #define LIBAVFORMAT_VERSION_MAJOR 57
#define LIBAVFORMAT_VERSION_MINOR 53 #define LIBAVFORMAT_VERSION_MINOR 54
#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \