[FFmpeg-cvslog] avcodec: add a Vorbis parser to get packet duration

Justin Ruggles git at videolan.org
Sun Mar 4 04:47:42 CET 2012


ffmpeg | branch: master | Justin Ruggles <justin.ruggles at gmail.com> | Sun Jan 15 13:38:03 2012 -0500| [5602a464c9f9e3c0922f5cfeccaf2fa1c40b2401] | committer: Justin Ruggles

avcodec: add a Vorbis parser to get packet duration

This also allows for removing some of the Vorbis-related hacks.

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=5602a464c9f9e3c0922f5cfeccaf2fa1c40b2401
---

 libavcodec/Makefile          |    1 +
 libavcodec/allcodecs.c       |    1 +
 libavcodec/version.h         |    2 +-
 libavcodec/vorbis_parser.c   |  270 ++++++++++++++++++++++++++++++++++++++++++
 libavcodec/vorbis_parser.h   |   68 +++++++++++
 libavcodec/vorbisdec.c       |    1 -
 libavformat/oggparsevorbis.c |    1 +
 libavformat/rtpdec.c         |    3 +
 libavformat/utils.c          |    6 +-
 9 files changed, 346 insertions(+), 7 deletions(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 33a1974..e456790 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -644,6 +644,7 @@ OBJS-$(CONFIG_RV40_PARSER)             += rv34_parser.o
 OBJS-$(CONFIG_VC1_PARSER)              += vc1_parser.o vc1.o vc1data.o \
                                           msmpeg4.o msmpeg4data.o mpeg4video.o \
                                           h263.o mpegvideo.o error_resilience.o
+OBJS-$(CONFIG_VORBIS_PARSER)           += vorbis_parser.o xiph.o
 OBJS-$(CONFIG_VP3_PARSER)              += vp3_parser.o
 OBJS-$(CONFIG_VP8_PARSER)              += vp8_parser.o
 
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 2844cfc..166f1fd 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -416,6 +416,7 @@ void avcodec_register_all(void)
     REGISTER_PARSER  (RV30, rv30);
     REGISTER_PARSER  (RV40, rv40);
     REGISTER_PARSER  (VC1, vc1);
+    REGISTER_PARSER  (VORBIS, vorbis);
     REGISTER_PARSER  (VP3, vp3);
     REGISTER_PARSER  (VP8, vp8);
 
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 7790f24..f3b40ca 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -21,7 +21,7 @@
 #define AVCODEC_VERSION_H
 
 #define LIBAVCODEC_VERSION_MAJOR 54
-#define LIBAVCODEC_VERSION_MINOR  5
+#define LIBAVCODEC_VERSION_MINOR  6
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavcodec/vorbis_parser.c b/libavcodec/vorbis_parser.c
new file mode 100644
index 0000000..f8d92e0
--- /dev/null
+++ b/libavcodec/vorbis_parser.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Vorbis audio parser
+ *
+ * Determines the duration for each packet.
+ */
+
+#include "get_bits.h"
+#include "parser.h"
+#include "xiph.h"
+#include "vorbis_parser.h"
+
+static int parse_id_header(AVCodecContext *avctx, VorbisParseContext *s,
+                           const uint8_t *buf, int buf_size)
+{
+    /* Id header should be 30 bytes */
+    if (buf_size < 30) {
+        av_log(avctx, AV_LOG_ERROR, "Id header is too short\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    /* make sure this is the Id header */
+    if (buf[0] != 1) {
+        av_log(avctx, AV_LOG_ERROR, "Wrong packet type in Id header\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    /* check for header signature */
+    if (memcmp(&buf[1], "vorbis", 6)) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid packet signature in Id header\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (!(buf[29] & 0x1)) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid framing bit in Id header\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    s->blocksize[0] = 1 << (buf[28] & 0xF);
+    s->blocksize[1] = 1 << (buf[28] >>  4);
+
+    return 0;
+}
+
+static int parse_setup_header(AVCodecContext *avctx, VorbisParseContext *s,
+                              const uint8_t *buf, int buf_size)
+{
+    GetBitContext gb, gb0;
+    uint8_t *rev_buf;
+    int i, ret = 0;
+    int got_framing_bit, mode_count, got_mode_header, last_mode_count = 0;
+
+    /* avoid overread */
+    if (buf_size < 7) {
+        av_log(avctx, AV_LOG_ERROR, "Setup header is too short\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    /* make sure this is the Setup header */
+    if (buf[0] != 5) {
+        av_log(avctx, AV_LOG_ERROR, "Wrong packet type in Setup header\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    /* check for header signature */
+    if (memcmp(&buf[1], "vorbis", 6)) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid packet signature in Setup header\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    /* reverse bytes so we can easily read backwards with get_bits() */
+    if (!(rev_buf = av_malloc(buf_size))) {
+        av_log(avctx, AV_LOG_ERROR, "Out of memory\n");
+        return AVERROR(ENOMEM);
+    }
+    for (i = 0; i < buf_size; i++)
+        rev_buf[i] = buf[buf_size - 1 - i];
+    init_get_bits(&gb, rev_buf, buf_size * 8);
+
+    got_framing_bit = 0;
+    while (get_bits_left(&gb) > 97) {
+        if (get_bits1(&gb)) {
+            got_framing_bit = get_bits_count(&gb);
+            break;
+        }
+    }
+    if (!got_framing_bit) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid Setup header\n");
+        ret = AVERROR_INVALIDDATA;
+        goto bad_header;
+    }
+
+    /* Now we search backwards to find possible valid mode counts. This is not
+     * fool-proof because we could have false positive matches and read too
+     * far, but there isn't really any way to be sure without parsing through
+     * all the many variable-sized fields before the modes. This approach seems
+     * to work well in testing, and it is similar to how it is handled in
+     * liboggz. */
+    mode_count = 0;
+    got_mode_header = 0;
+    while (get_bits_left(&gb) >= 97) {
+        if (get_bits(&gb, 8) > 63 || get_bits(&gb, 16) || get_bits(&gb, 16))
+            break;
+        skip_bits(&gb, 1);
+        mode_count++;
+        if (mode_count > 64)
+            break;
+        gb0 = gb;
+        if (get_bits(&gb0, 6) + 1 == mode_count) {
+            got_mode_header = 1;
+            last_mode_count = mode_count;
+        }
+    }
+    if (!got_mode_header) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid Setup header\n");
+        ret = AVERROR_INVALIDDATA;
+        goto bad_header;
+    }
+    /* All samples I've seen use <= 2 modes, so ask for a sample if we find
+     * more than that, as it is most likely a false positive. If we get any
+     * we may need to approach this the long way and parse the whole Setup
+     * header, but I hope very much that it never comes to that. */
+    if (last_mode_count > 2) {
+        av_log_ask_for_sample(avctx, "%d modes found. This is either a false "
+                              "positive or a sample from an unknown encoder.\n",
+                              last_mode_count);
+    }
+    /* We're limiting the mode count to 63 so that we know that the previous
+     * block flag will be in the first packet byte. */
+    if (last_mode_count > 63) {
+        av_log(avctx, AV_LOG_ERROR, "Unsupported mode count: %d\n",
+               last_mode_count);
+        ret = AVERROR_INVALIDDATA;
+        goto bad_header;
+    }
+    s->mode_count = mode_count = last_mode_count;
+    /* Determine the number of bits required to code the mode and turn that
+     * into a bitmask to directly access the mode from the first frame byte. */
+    s->mode_mask = ((1 << (av_log2(mode_count - 1) + 1)) - 1) << 1;
+    /* The previous window flag is the next bit after the mode */
+    s->prev_mask = (s->mode_mask | 0x1) + 1;
+
+    init_get_bits(&gb, rev_buf, buf_size * 8);
+    skip_bits_long(&gb, got_framing_bit);
+    for (i = mode_count - 1; i >= 0; i--) {
+        skip_bits_long(&gb, 40);
+        s->mode_blocksize[i] = s->blocksize[get_bits1(&gb)];
+    }
+
+bad_header:
+    av_free(rev_buf);
+    return ret;
+}
+
+int avpriv_vorbis_parse_extradata(AVCodecContext *avctx, VorbisParseContext *s)
+{
+    uint8_t *header_start[3];
+    int header_len[3];
+    int ret;
+
+    s->avctx = avctx;
+    s->extradata_parsed = 1;
+
+    if ((ret = avpriv_split_xiph_headers(avctx->extradata,
+                                         avctx->extradata_size, 30,
+                                         header_start, header_len)) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Extradata corrupt.\n");
+        return ret;
+    }
+
+    if ((ret = parse_id_header(avctx, s, header_start[0], header_len[0])) < 0)
+        return ret;
+
+    if ((ret = parse_setup_header(avctx, s, header_start[2], header_len[2])) < 0)
+        return ret;
+
+    s->valid_extradata = 1;
+    s->previous_blocksize = s->mode_blocksize[0];
+
+    return 0;
+}
+
+int avpriv_vorbis_parse_frame(VorbisParseContext *s, const uint8_t *buf,
+                              int buf_size)
+{
+    int duration = 0;
+
+    if (s->valid_extradata && buf_size > 0) {
+        int mode, current_blocksize;
+        int previous_blocksize = s->previous_blocksize;
+
+        if (buf[0] & 1) {
+            av_log(s->avctx, AV_LOG_ERROR, "Invalid packet\n");
+            return AVERROR_INVALIDDATA;
+        }
+        if (s->mode_count == 1)
+            mode = 0;
+        else
+            mode = (buf[0] & s->mode_mask) >> 1;
+        if (mode >= s->mode_count) {
+            av_log(s->avctx, AV_LOG_ERROR, "Invalid mode in packet\n");
+            return AVERROR_INVALIDDATA;
+        }
+        if (mode) {
+            int flag = !!(buf[0] & s->prev_mask);
+            previous_blocksize = s->blocksize[flag];
+        }
+        current_blocksize     = s->mode_blocksize[mode];
+        duration              = (previous_blocksize + current_blocksize) >> 2;
+        s->previous_blocksize = current_blocksize;
+    }
+
+    return duration;
+}
+
+void avpriv_vorbis_parse_reset(VorbisParseContext *s)
+{
+    if (s->valid_extradata)
+        s->previous_blocksize = s->mode_blocksize[0];
+}
+
+#if CONFIG_VORBIS_PARSER
+static int vorbis_parse(AVCodecParserContext *s1, AVCodecContext *avctx,
+                        const uint8_t **poutbuf, int *poutbuf_size,
+                        const uint8_t *buf, int buf_size)
+{
+    VorbisParseContext *s = s1->priv_data;
+    int duration;
+
+    if (!s->extradata_parsed && avctx->extradata && avctx->extradata_size)
+        if (avpriv_vorbis_parse_extradata(avctx, s))
+            goto end;
+
+    if ((duration = avpriv_vorbis_parse_frame(s, buf, buf_size)) >= 0)
+        s1->duration = duration;
+
+end:
+    /* always return the full packet. this parser isn't doing any splitting or
+       combining, only packet analysis */
+    *poutbuf      = buf;
+    *poutbuf_size = buf_size;
+    return buf_size;
+}
+
+AVCodecParser ff_vorbis_parser = {
+    .codec_ids      = { CODEC_ID_VORBIS },
+    .priv_data_size = sizeof(VorbisParseContext),
+    .parser_parse   = vorbis_parse,
+};
+#endif /* CONFIG_VORBIS_PARSER */
diff --git a/libavcodec/vorbis_parser.h b/libavcodec/vorbis_parser.h
new file mode 100644
index 0000000..480a918
--- /dev/null
+++ b/libavcodec/vorbis_parser.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Vorbis audio parser
+ *
+ * Determines the duration for each packet.
+ */
+
+#ifndef AVCODEC_VORBIS_PARSER_H
+#define AVCODEC_VORBIS_PARSER_H
+
+#include "avcodec.h"
+
+typedef struct VorbisParseContext {
+    AVCodecContext *avctx;      ///< codec context
+    int extradata_parsed;       ///< we have attempted to parse extradata
+    int valid_extradata;        ///< extradata is valid, so we can calculate duration
+    int blocksize[2];           ///< short and long window sizes
+    int previous_blocksize;     ///< previous window size
+    int mode_blocksize[64];     ///< window size mapping for each mode
+    int mode_count;             ///< number of modes
+    int mode_mask;              ///< bitmask used to get the mode in each packet
+    int prev_mask;              ///< bitmask used to get the previous mode flag in each packet
+} VorbisParseContext;
+
+/**
+ * Initialize the Vorbis parser using headers in the extradata.
+ *
+ * @param avctx codec context
+ * @param s     Vorbis parser context
+ */
+int avpriv_vorbis_parse_extradata(AVCodecContext *avctx, VorbisParseContext *s);
+
+/**
+ * Get the duration for a Vorbis packet.
+ *
+ * avpriv_vorbis_parse_extradata() must have been successfully called prior to
+ * this in order for a correct duration to be returned.
+ *
+ * @param s        Vorbis parser context
+ * @param buf      buffer containing a Vorbis frame
+ * @param buf_size size of the buffer
+ */
+int avpriv_vorbis_parse_frame(VorbisParseContext *s, const uint8_t *buf,
+                              int buf_size);
+
+void avpriv_vorbis_parse_reset(VorbisParseContext *s);
+
+#endif /* AVCODEC_VORBIS_PARSER_H */
diff --git a/libavcodec/vorbisdec.c b/libavcodec/vorbisdec.c
index c83d49d..0d491c8 100644
--- a/libavcodec/vorbisdec.c
+++ b/libavcodec/vorbisdec.c
@@ -1031,7 +1031,6 @@ static av_cold int vorbis_decode_init(AVCodecContext *avccontext)
 
     avccontext->channels    = vc->audio_channels;
     avccontext->sample_rate = vc->audio_samplerate;
-    avccontext->frame_size  = FFMIN(vc->blocksize[0], vc->blocksize[1]) >> 2;
 
     avcodec_get_frame_defaults(&vc->frame);
     avccontext->coded_frame = &vc->frame;
diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c
index ba9b348..b9d9f57 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -251,6 +251,7 @@ vorbis_header (AVFormatContext * s, int idx)
 
         st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
         st->codec->codec_id = CODEC_ID_VORBIS;
+        st->need_parsing = AVSTREAM_PARSE_HEADERS;
 
         if (srate > 0) {
             st->codec->sample_rate = srate;
diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c
index a8c5c3f..61653f7 100644
--- a/libavformat/rtpdec.c
+++ b/libavformat/rtpdec.c
@@ -396,6 +396,9 @@ RTPDemuxContext *ff_rtp_parse_open(AVFormatContext *s1, AVStream *st, URLContext
         case CODEC_ID_H264:
             st->need_parsing = AVSTREAM_PARSE_FULL;
             break;
+        case CODEC_ID_VORBIS:
+            st->need_parsing = AVSTREAM_PARSE_HEADERS;
+            break;
         case CODEC_ID_ADPCM_G722:
             /* According to RFC 3551, the stream clock rate is 8000
              * even if the sample rate is 16000. */
diff --git a/libavformat/utils.c b/libavformat/utils.c
index cf4392b..123bc8b 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -701,9 +701,6 @@ static int get_audio_frame_size(AVCodecContext *enc, int size)
 {
     int frame_size;
 
-    if(enc->codec_id == CODEC_ID_VORBIS)
-        return -1;
-
     if (enc->frame_size <= 1) {
         int bits_per_sample = av_get_bits_per_sample(enc->codec_id);
 
@@ -1995,8 +1992,7 @@ static int has_codec_parameters(AVCodecContext *avctx)
     case AVMEDIA_TYPE_AUDIO:
         val = avctx->sample_rate && avctx->channels && avctx->sample_fmt != AV_SAMPLE_FMT_NONE;
         if (!avctx->frame_size &&
-            (avctx->codec_id == CODEC_ID_VORBIS ||
-             avctx->codec_id == CODEC_ID_AAC ||
+            (avctx->codec_id == CODEC_ID_AAC ||
              avctx->codec_id == CODEC_ID_MP1 ||
              avctx->codec_id == CODEC_ID_MP2 ||
              avctx->codec_id == CODEC_ID_MP3 ||



More information about the ffmpeg-cvslog mailing list