This solves the problem discussed in https://ffmpeg.org/pipermail/ffmpeg-devel/2015-September/179238.html by allowing AVCodec::write_header to be delayed until after packets have been run through required bitstream filters in order to generate global extradata.
It also provides a mechanism by which a muxer can add a bitstream filter to a stream automatically, rather than prompting the user to do so. I'd like to split this into 4 commits: - Moving av_apply_bitstream_filters from ffmpeg.c to its own API function - Other lavf API changes - Use of the new API in matroskaenc - The minor style tweak in matroskaenc There are a few other changes I think should be made before this is applied: - Adding BSF arguments to AVBitStreamFilterContext rather than passing them manually on each packet - Providing an API to add a bitstream filter to an AVStream, rather than doing so manually, and using it in check_bitstream - Using said API in ffmpeg_opt.c and removing ffmpeg.c's own BSF handling - Adding check_bitstream to other muxers that currently prompt the user to add bitstream filters (such as aac_adtstoasc and h264_mpeg4toannexb). It could also be used by e.g. movenc for generating the EAC3-specific atom. --- ffmpeg.c | 45 ++++-------------------------------------- libavformat/avformat.h | 16 +++++++++++++++ libavformat/matroskaenc.c | 22 ++++++++++++++++++++- libavformat/mux.c | 25 +++++++++++++++++++++++- libavformat/utils.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+), 43 deletions(-) diff --git a/ffmpeg.c b/ffmpeg.c index e31a2c6..e6cd0e8 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -682,47 +682,10 @@ static void write_frame(AVFormatContext *s, AVPacket *pkt, OutputStream *ost) if (bsfc) av_packet_split_side_data(pkt); - while (bsfc) { - AVPacket new_pkt = *pkt; - AVDictionaryEntry *bsf_arg = av_dict_get(ost->bsf_args, - bsfc->filter->name, - NULL, 0); - int a = av_bitstream_filter_filter(bsfc, avctx, - bsf_arg ? bsf_arg->value : NULL, - &new_pkt.data, &new_pkt.size, - pkt->data, pkt->size, - pkt->flags & AV_PKT_FLAG_KEY); - if(a == 0 && new_pkt.data != pkt->data) { - uint8_t *t = av_malloc(new_pkt.size + AV_INPUT_BUFFER_PADDING_SIZE); //the new should be a subset of the old so cannot overflow - if(t) { - memcpy(t, new_pkt.data, new_pkt.size); - memset(t + new_pkt.size, 0, AV_INPUT_BUFFER_PADDING_SIZE); - new_pkt.data = t; - new_pkt.buf = NULL; - a = 1; - } else - a = AVERROR(ENOMEM); - } - if (a > 0) { - pkt->side_data = NULL; - pkt->side_data_elems = 0; - av_free_packet(pkt); - new_pkt.buf = av_buffer_create(new_pkt.data, new_pkt.size, - av_buffer_default_free, NULL, 0); - if (!new_pkt.buf) - exit_program(1); - } else if (a < 0) { - new_pkt = *pkt; - av_log(NULL, AV_LOG_ERROR, "Failed to open bitstream filter %s for stream %d with codec %s", - bsfc->filter->name, pkt->stream_index, - avctx->codec ? avctx->codec->name : "copy"); - print_error("", a); - if (exit_on_error) - exit_program(1); - } - *pkt = new_pkt; - - bsfc = bsfc->next; + if (ret = av_apply_bitstream_filters(s, pkt, bsfc, ost->bsf_args) < 0) { + print_error("", ret); + if (exit_on_error) + exit_program(1); } if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) { diff --git a/libavformat/avformat.h b/libavformat/avformat.h index e2a27d4..62807c4 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -598,6 +598,10 @@ typedef struct AVOutputFormat { */ int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); enum AVCodecID data_codec; /**< default data codec */ + /** + * Check if a packet requires a bitstream filter. If so, + */ + int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt); } AVOutputFormat; /** * @} @@ -1167,6 +1171,14 @@ typedef struct AVStream { AVRational display_aspect_ratio; struct FFFrac *priv_pts; + + /** + * bitstream filter to run on stream + * - encoding: Set by muxer + * - decoding: unused + */ + int bitstream_checked; + AVBitStreamFilterContext *bsfc; } AVStream; AVRational av_stream_get_r_frame_rate(const AVStream *s); @@ -1782,6 +1794,8 @@ typedef struct AVFormatContext { * Demuxing: Set by user. */ int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options); + + int header_written; } AVFormatContext; int av_format_get_probe_score(const AVFormatContext *s); @@ -2728,6 +2742,8 @@ int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, int avformat_queue_attached_pictures(AVFormatContext *s); +int av_apply_bitstream_filters(AVFormatContext *s, AVPacket *pkt, + AVBitStreamFilterContext *bsfc, AVDictionary *bsf_args); /** * @} diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 1fb39fe..b26d864 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -2106,6 +2106,23 @@ static int mkv_query_codec(enum AVCodecID codec_id, int std_compliance) return 0; } +static int mkv_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) +{ + AVStream *st = s->streams[pkt->stream_index]; + if (st->codec->codec_id == AV_CODEC_ID_AAC) { + if (pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { + if (!(st->bsfc = av_bitstream_filter_init("aac_adtstoasc"))) { + av_log(s, AV_LOG_FATAL, "Unknown bitstream filter aac_adtstoasc\n"); + return AVERROR(EINVAL); + } + } + } + + st->bitstream_checked = 1; + + return 0; +} + static const AVCodecTag additional_audio_tags[] = { { AV_CODEC_ID_ALAC, 0XFFFFFFFF }, { AV_CODEC_ID_EAC3, 0XFFFFFFFF }, @@ -2179,6 +2196,7 @@ AVOutputFormat ff_matroska_muxer = { }, .subtitle_codec = AV_CODEC_ID_ASS, .query_codec = mkv_query_codec, + .check_bitstream = mkv_check_bitstream, .priv_class = &matroska_class, }; #endif @@ -2198,11 +2216,12 @@ AVOutputFormat ff_webm_muxer = { .extensions = "webm", .priv_data_size = sizeof(MatroskaMuxContext), .audio_codec = CONFIG_LIBOPUS_ENCODER ? AV_CODEC_ID_OPUS : AV_CODEC_ID_VORBIS, - .video_codec = CONFIG_LIBVPX_VP9_ENCODER? AV_CODEC_ID_VP9 : AV_CODEC_ID_VP8, + .video_codec = CONFIG_LIBVPX_VP9_ENCODER ? AV_CODEC_ID_VP9 : AV_CODEC_ID_VP8, .subtitle_codec = AV_CODEC_ID_WEBVTT, .write_header = mkv_write_header, .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, + .check_bitstream = mkv_check_bitstream, .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, .priv_class = &webm_class, @@ -2228,6 +2247,7 @@ AVOutputFormat ff_matroska_audio_muxer = { .write_header = mkv_write_header, .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, + .check_bitstream = mkv_check_bitstream, .flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, .codec_tag = (const AVCodecTag* const []){ diff --git a/libavformat/mux.c b/libavformat/mux.c index c9ef490..e9de72f 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -451,7 +451,7 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) if ((ret = init_muxer(s, options)) < 0) return ret; - if (s->oformat->write_header) { + if (s->oformat->write_header && !s->oformat->check_bitstream) { ret = s->oformat->write_header(s); if (ret >= 0 && s->pb && s->pb->error < 0) ret = s->pb->error; @@ -459,6 +459,7 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) return ret; if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS) avio_flush(s->pb); + s->header_written = 1; } if ((ret = init_pts(s)) < 0) @@ -951,6 +952,16 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) ret = AVERROR(EINVAL); goto fail; } + + if (s->oformat->check_bitstream) { + if (!st->bitstream_checked) { + if ((ret = s->oformat->check_bitstream(s, pkt)) < 0) + goto fail; + } + } + + if ((ret = av_apply_bitstream_filters(s, pkt, st->bsfc, NULL)) < 0) + goto fail; } else { av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n"); flush = 1; @@ -967,10 +978,22 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) if (ret <= 0) //FIXME cleanup needed for ret<0 ? return ret; + if (!s->header_written && s->oformat->write_header) { + ret = s->oformat->write_header(s); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; + if (ret < 0) + goto fail2; + if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS) + avio_flush(s->pb); + s->header_written = 1; + } + ret = write_packet(s, &opkt); if (ret >= 0) s->streams[opkt.stream_index]->nb_frames++; +fail2: av_free_packet(&opkt); if (ret < 0) diff --git a/libavformat/utils.c b/libavformat/utils.c index 689473e..c98b8e0 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -4596,3 +4596,53 @@ uint8_t *ff_stream_new_side_data(AVStream *st, enum AVPacketSideDataType type, sd->size = size; return data; } + +int av_apply_bitstream_filters(AVFormatContext *s, AVPacket *pkt, + AVBitStreamFilterContext *bsfc, AVDictionary *bsf_args) +{ + int ret = 0; + AVStream *st = s->streams[pkt->stream_index]; + while (bsfc) { + AVPacket new_pkt = *pkt; + AVDictionaryEntry *bsf_arg = av_dict_get(bsf_args, bsfc->filter->name, NULL, 0); + int a = av_bitstream_filter_filter(bsfc, st->codec, + bsf_arg ? bsf_arg->value : NULL, + &new_pkt.data, &new_pkt.size, + pkt->data, pkt->size, + pkt->flags & AV_PKT_FLAG_KEY); + if(a == 0 && new_pkt.data != pkt->data) { + uint8_t *t = av_malloc(new_pkt.size + AV_INPUT_BUFFER_PADDING_SIZE); //the new should be a subset of the old so cannot overflow + if (t) { + memcpy(t, new_pkt.data, new_pkt.size); + memset(t + new_pkt.size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + new_pkt.data = t; + new_pkt.buf = NULL; + a = 1; + } else { + a = AVERROR(ENOMEM); + } + } + if (a > 0) { + new_pkt.buf = av_buffer_create(new_pkt.data, new_pkt.size, + av_buffer_default_free, NULL, 0); + if (new_pkt.buf) { + pkt->side_data = NULL; + pkt->side_data_elems = 0; + av_free_packet(pkt); + } else { + a = AVERROR(ENOMEM); + } + } + if (a < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open bitstream filter %s for stream %d with codec %s", + bsfc->filter->name, pkt->stream_index, + st->codec->codec ? st->codec->codec->name : "copy"); + ret = a; + break; + } + *pkt = new_pkt; + + bsfc = bsfc->next; + } + return ret; +} -- 2.6.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel