[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