[FFmpeg-devel] [PATCH] RFC: Automatic bitstream filtering
Rodger Combs
rodger.combs at gmail.com
Wed Oct 7 03:07:08 CEST 2015
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
More information about the ffmpeg-devel
mailing list