OSDN Git Service

A new OggExtractor/VorbisDecoder combo to support approximate seeking.
authorAndreas Huber <andih@google.com>
Fri, 7 May 2010 17:35:13 +0000 (10:35 -0700)
committerAndreas Huber <andih@google.com>
Fri, 7 May 2010 19:01:45 +0000 (12:01 -0700)
Change-Id: Id5d0c1c8b1adc62896bb5ed951f7b5cfda811e95
related-to-bug: 2654400

17 files changed:
include/media/stagefright/MediaDefs.h
include/media/stagefright/MetaData.h
include/media/stagefright/Utils.h
media/libstagefright/Android.mk
media/libstagefright/DataSource.cpp
media/libstagefright/MediaDefs.cpp
media/libstagefright/MediaExtractor.cpp
media/libstagefright/OMXCodec.cpp
media/libstagefright/OggExtractor.cpp [new file with mode: 0644]
media/libstagefright/StagefrightMediaScanner.cpp
media/libstagefright/Utils.cpp
media/libstagefright/VorbisExtractor.cpp [deleted file]
media/libstagefright/codecs/vorbis/Android.mk [new file with mode: 0644]
media/libstagefright/codecs/vorbis/dec/Android.mk [new file with mode: 0644]
media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp [new file with mode: 0644]
media/libstagefright/include/OggExtractor.h [moved from media/libstagefright/include/VorbisExtractor.h with 68% similarity]
media/libstagefright/include/VorbisDecoder.h [new file with mode: 0644]

index 4edfb88..207195a 100644 (file)
@@ -32,11 +32,12 @@ extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB;
 extern const char *MEDIA_MIMETYPE_AUDIO_MPEG;
 extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
 extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
+extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
 extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
 
 extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
 extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
-extern const char *MEDIA_MIMETYPE_CONTAINER_VORBIS;
+extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
 
 }  // namespace android
 
index 45cc4f6..dc2bd50 100644 (file)
@@ -37,6 +37,8 @@ enum {
     kKeyBitRate           = 'brte',  // int32_t (bps)
     kKeyESDS              = 'esds',  // raw data
     kKeyAVCC              = 'avcc',  // raw data
+    kKeyVorbisInfo        = 'vinf',  // raw data
+    kKeyVorbisBooks       = 'vboo',  // raw data
     kKeyWantsNALFragments = 'NALf',
     kKeyIsSyncFrame       = 'sync',  // int32_t (bool)
     kKeyIsCodecConfig     = 'conf',  // int32_t (bool)
index 30c7f11..498b525 100644 (file)
@@ -29,6 +29,10 @@ uint16_t U16_AT(const uint8_t *ptr);
 uint32_t U32_AT(const uint8_t *ptr);
 uint64_t U64_AT(const uint8_t *ptr);
 
+uint16_t U16LE_AT(const uint8_t *ptr);
+uint32_t U32LE_AT(const uint8_t *ptr);
+uint64_t U64LE_AT(const uint8_t *ptr);
+
 uint64_t ntoh64(uint64_t x);
 uint64_t hton64(uint64_t x);
 
index 0420a60..81f995b 100644 (file)
@@ -31,6 +31,7 @@ LOCAL_SRC_FILES +=                \
         MPEG4Extractor.cpp        \
         MPEG4Writer.cpp           \
         MediaExtractor.cpp        \
+        OggExtractor.cpp          \
         Prefetcher.cpp            \
         SampleIterator.cpp        \
         SampleTable.cpp           \
@@ -39,7 +40,6 @@ LOCAL_SRC_FILES +=                \
         StagefrightMetadataRetriever.cpp \
         TimeSource.cpp            \
         TimedEventQueue.cpp       \
-        VorbisExtractor.cpp       \
         WAVExtractor.cpp          \
         string.cpp
 
@@ -50,7 +50,7 @@ LOCAL_C_INCLUDES:= \
        $(JNI_H_INCLUDE) \
         $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
         $(TOP)/external/opencore/android \
-        $(TOP)/external/tremolo/Tremolo
+        $(TOP)/external/tremolo
 
 LOCAL_SHARED_LIBRARIES := \
         libbinder         \
@@ -70,7 +70,8 @@ LOCAL_STATIC_LIBRARIES := \
         libstagefright_amrwbdec \
         libstagefright_avcdec \
         libstagefright_m4vh263dec \
-        libstagefright_mp3dec
+        libstagefright_mp3dec \
+        libstagefright_vorbisdec
 
 LOCAL_SHARED_LIBRARIES += \
         libstagefright_amrnb_common \
index 5db3201..a66f86b 100644 (file)
@@ -18,7 +18,7 @@
 #include "include/MP3Extractor.h"
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
-#include "include/VorbisExtractor.h"
+#include "include/OggExtractor.h"
 
 #include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/DataSource.h>
@@ -93,7 +93,7 @@ void DataSource::RegisterDefaultSniffers() {
     RegisterSniffer(SniffMPEG4);
     RegisterSniffer(SniffAMR);
     RegisterSniffer(SniffWAV);
-    RegisterSniffer(SniffVorbis);
+    RegisterSniffer(SniffOgg);
 }
 
 // static
index db18ab6..4b3813b 100644 (file)
@@ -30,10 +30,11 @@ const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
 const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg";
 const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
 const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
+const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
 const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
 
 const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
 const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav";
-const char *MEDIA_MIMETYPE_CONTAINER_VORBIS = "application/ogg";
+const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
 
 }  // namespace android
index dfddbe0..513f49c 100644 (file)
@@ -22,7 +22,7 @@
 #include "include/MP3Extractor.h"
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
-#include "include/VorbisExtractor.h"
+#include "include/OggExtractor.h"
 
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaDefs.h>
@@ -67,8 +67,8 @@ sp<MediaExtractor> MediaExtractor::Create(
         return new AMRExtractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
         return new WAVExtractor(source);
-    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_VORBIS)) {
-        return new VorbisExtractor(source);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
+        return new OggExtractor(source);
     }
 
     return NULL;
index 6ed384c..69da7ef 100644 (file)
@@ -25,6 +25,7 @@
 #include "include/AVCDecoder.h"
 #include "include/M4vH263Decoder.h"
 #include "include/MP3Decoder.h"
+#include "include/VorbisDecoder.h"
 
 #include "include/ESDS.h"
 
@@ -67,6 +68,7 @@ FACTORY_CREATE(AMRWBDecoder)
 FACTORY_CREATE(AACDecoder)
 FACTORY_CREATE(AVCDecoder)
 FACTORY_CREATE(M4vH263Decoder)
+FACTORY_CREATE(VorbisDecoder)
 FACTORY_CREATE(AMRNBEncoder)
 
 static sp<MediaSource> InstantiateSoftwareCodec(
@@ -83,6 +85,7 @@ static sp<MediaSource> InstantiateSoftwareCodec(
         FACTORY_REF(AACDecoder)
         FACTORY_REF(AVCDecoder)
         FACTORY_REF(M4vH263Decoder)
+        FACTORY_REF(VorbisDecoder)
         FACTORY_REF(AMRNBEncoder)
     };
     for (size_t i = 0;
@@ -123,6 +126,7 @@ static const CodecInfo kDecoderInfo[] = {
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
 //    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },
+    { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" },
 };
 
 static const CodecInfo kEncoderInfo[] = {
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
new file mode 100644 (file)
index 0000000..699b955
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OggExtractor"
+#include <utils/Log.h>
+
+#include "include/OggExtractor.h"
+
+#include <cutils/properties.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+extern "C" {
+    #include <Tremolo/codec_internal.h>
+
+    int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb);
+    int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb);
+    int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb);
+}
+
+namespace android {
+
+struct OggSource : public MediaSource {
+    OggSource(const sp<OggExtractor> &extractor);
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+    virtual ~OggSource();
+
+private:
+    sp<OggExtractor> mExtractor;
+    bool mStarted;
+
+    OggSource(const OggSource &);
+    OggSource &operator=(const OggSource &);
+};
+
+struct MyVorbisExtractor {
+    MyVorbisExtractor(const sp<DataSource> &source);
+    virtual ~MyVorbisExtractor();
+
+    sp<MetaData> getFormat() const;
+
+    // Returns an approximate bitrate in bits per second.
+    uint64_t approxBitrate();
+
+    status_t seekToOffset(off_t offset);
+    status_t readNextPacket(MediaBuffer **buffer);
+
+    void init();
+
+private:
+    struct Page {
+        uint64_t mGranulePosition;
+        uint32_t mSerialNo;
+        uint32_t mPageNo;
+        uint8_t mFlags;
+        uint8_t mNumSegments;
+        uint8_t mLace[255];
+    };
+
+    sp<DataSource> mSource;
+    off_t mOffset;
+    Page mCurrentPage;
+    size_t mCurrentPageSize;
+    size_t mNextLaceIndex;
+
+    vorbis_info mVi;
+    vorbis_comment mVc;
+
+    sp<MetaData> mMeta;
+
+    ssize_t readPage(off_t offset, Page *page);
+    status_t findNextPage(off_t startOffset, off_t *pageOffset);
+
+    void verifyHeader(
+            MediaBuffer *buffer, uint8_t type);
+
+    MyVorbisExtractor(const MyVorbisExtractor &);
+    MyVorbisExtractor &operator=(const MyVorbisExtractor &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+OggSource::OggSource(const sp<OggExtractor> &extractor)
+    : mExtractor(extractor),
+      mStarted(false) {
+}
+
+OggSource::~OggSource() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+sp<MetaData> OggSource::getFormat() {
+    return mExtractor->mImpl->getFormat();
+}
+
+status_t OggSource::start(MetaData *params) {
+    if (mStarted) {
+        return INVALID_OPERATION;
+    }
+
+    mStarted = true;
+
+    return OK;
+}
+
+status_t OggSource::stop() {
+    mStarted = false;
+
+    return OK;
+}
+
+status_t OggSource::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    *out = NULL;
+
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
+        LOGI("seeking to offset %ld", pos);
+
+        if (mExtractor->mImpl->seekToOffset(pos) != OK) {
+            return ERROR_END_OF_STREAM;
+        }
+    }
+
+    MediaBuffer *packet;
+    status_t err = mExtractor->mImpl->readNextPacket(&packet);
+
+    if (err != OK) {
+        return err;
+    }
+
+#if 0
+    int64_t timeUs;
+    if (packet->meta_data()->findInt64(kKeyTime, &timeUs)) {
+        LOGI("found time = %lld us", timeUs);
+    } else {
+        LOGI("NO time");
+    }
+#endif
+
+    *out = packet;
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source)
+    : mSource(source),
+      mOffset(0),
+      mCurrentPageSize(0),
+      mNextLaceIndex(0) {
+    mCurrentPage.mNumSegments = 0;
+}
+
+MyVorbisExtractor::~MyVorbisExtractor() {
+}
+
+sp<MetaData> MyVorbisExtractor::getFormat() const {
+    return mMeta;
+}
+
+status_t MyVorbisExtractor::findNextPage(
+        off_t startOffset, off_t *pageOffset) {
+    *pageOffset = startOffset;
+
+    for (;;) {
+        char signature[4];
+        ssize_t n = mSource->readAt(*pageOffset, &signature, 4);
+
+        if (n < 4) {
+            *pageOffset = 0;
+
+            return (n < 0) ? n : (status_t)ERROR_END_OF_STREAM;
+        }
+
+        if (!memcmp(signature, "OggS", 4)) {
+            if (*pageOffset > startOffset) {
+                LOGV("skipped %ld bytes of junk to reach next frame",
+                     *pageOffset - startOffset);
+            }
+
+            return OK;
+        }
+
+        ++*pageOffset;
+    }
+}
+
+status_t MyVorbisExtractor::seekToOffset(off_t offset) {
+    off_t pageOffset;
+    status_t err = findNextPage(offset, &pageOffset);
+
+    if (err != OK) {
+        return err;
+    }
+
+    mOffset = pageOffset;
+
+    mCurrentPageSize = 0;
+    mCurrentPage.mNumSegments = 0;
+    mNextLaceIndex = 0;
+
+    // XXX what if new page continues packet from last???
+
+    return OK;
+}
+
+ssize_t MyVorbisExtractor::readPage(off_t offset, Page *page) {
+    uint8_t header[27];
+    if (mSource->readAt(offset, header, sizeof(header))
+            < (ssize_t)sizeof(header)) {
+        LOGE("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
+
+        return ERROR_IO;
+    }
+
+    if (memcmp(header, "OggS", 4)) {
+        return ERROR_MALFORMED;
+    }
+
+    if (header[4] != 0) {
+        // Wrong version.
+
+        return ERROR_UNSUPPORTED;
+    }
+
+    page->mFlags = header[5];
+
+    if (page->mFlags & ~7) {
+        // Only bits 0-2 are defined in version 0.
+        return ERROR_MALFORMED;
+    }
+
+    page->mGranulePosition = U64LE_AT(&header[6]);
+
+#if 0
+    printf("granulePosition = %llu (0x%llx)\n",
+           page->mGranulePosition, page->mGranulePosition);
+#endif
+
+    page->mSerialNo = U32LE_AT(&header[14]);
+    page->mPageNo = U32LE_AT(&header[18]);
+
+    page->mNumSegments = header[26];
+    if (mSource->readAt(
+                offset + sizeof(header), page->mLace, page->mNumSegments)
+            < (ssize_t)page->mNumSegments) {
+        return ERROR_IO;
+    }
+
+    size_t totalSize = 0;;
+    for (size_t i = 0; i < page->mNumSegments; ++i) {
+        totalSize += page->mLace[i];
+    }
+
+    String8 tmp;
+    for (size_t i = 0; i < page->mNumSegments; ++i) {
+        char x[32];
+        sprintf(x, "%s%u", i > 0 ? ", " : "", (unsigned)page->mLace[i]);
+
+        tmp.append(x);
+    }
+
+    LOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string());
+
+    return sizeof(header) + page->mNumSegments + totalSize;
+}
+
+status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) {
+    *out = NULL;
+
+    MediaBuffer *buffer = NULL;
+    int64_t timeUs = -1;
+
+    for (;;) {
+        size_t i;
+        size_t packetSize = 0;
+        bool gotFullPacket = false;
+        for (i = mNextLaceIndex; i < mCurrentPage.mNumSegments; ++i) {
+            uint8_t lace = mCurrentPage.mLace[i];
+
+            packetSize += lace;
+
+            if (lace < 255) {
+                gotFullPacket = true;
+                ++i;
+                break;
+            }
+        }
+
+        if (mNextLaceIndex < mCurrentPage.mNumSegments) {
+            off_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments;
+            for (size_t j = 0; j < mNextLaceIndex; ++j) {
+                dataOffset += mCurrentPage.mLace[j];
+            }
+
+            size_t fullSize = packetSize;
+            if (buffer != NULL) {
+                fullSize += buffer->range_length();
+            }
+            MediaBuffer *tmp = new MediaBuffer(fullSize);
+            if (buffer != NULL) {
+                memcpy(tmp->data(), buffer->data(), buffer->range_length());
+                tmp->set_range(0, buffer->range_length());
+                buffer->release();
+            } else {
+                // XXX Not only is this not technically the correct time for
+                // this packet, we also stamp every packet in this page
+                // with the same time. This needs fixing later.
+                timeUs = mCurrentPage.mGranulePosition * 1000000ll / mVi.rate;
+                tmp->set_range(0, 0);
+            }
+            buffer = tmp;
+
+            ssize_t n = mSource->readAt(
+                    dataOffset,
+                    (uint8_t *)buffer->data() + buffer->range_length(),
+                    packetSize);
+
+            if (n < (ssize_t)packetSize) {
+                LOGE("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
+                return ERROR_IO;
+            }
+
+            buffer->set_range(0, fullSize);
+
+            mNextLaceIndex = i;
+
+            if (gotFullPacket) {
+                // We've just read the entire packet.
+
+                if (timeUs >= 0) {
+                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
+                }
+
+                *out = buffer;
+
+                return OK;
+            }
+
+            // fall through, the buffer now contains the start of the packet.
+        }
+
+        CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments);
+
+        mOffset += mCurrentPageSize;
+        ssize_t n = readPage(mOffset, &mCurrentPage);
+
+        if (n <= 0) {
+            if (buffer) {
+                buffer->release();
+                buffer = NULL;
+            }
+
+            LOGE("readPage returned %ld", n);
+
+            return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
+        }
+
+        mCurrentPageSize = n;
+        mNextLaceIndex = 0;
+
+        if (buffer != NULL) {
+            if ((mCurrentPage.mFlags & 1) == 0) {
+                // This page does not continue the packet, i.e. the packet
+                // is already complete.
+
+                if (timeUs >= 0) {
+                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
+                }
+
+                *out = buffer;
+
+                return OK;
+            }
+        }
+    }
+}
+
+void MyVorbisExtractor::init() {
+    vorbis_info_init(&mVi);
+
+    vorbis_comment mVc;
+
+    mMeta = new MetaData;
+    mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
+
+    MediaBuffer *packet;
+    CHECK_EQ(readNextPacket(&packet), OK);
+    LOGV("read packet of size %d\n", packet->range_length());
+    verifyHeader(packet, 1);
+    packet->release();
+    packet = NULL;
+
+    CHECK_EQ(readNextPacket(&packet), OK);
+    LOGV("read packet of size %d\n", packet->range_length());
+    verifyHeader(packet, 3);
+    packet->release();
+    packet = NULL;
+
+    CHECK_EQ(readNextPacket(&packet), OK);
+    LOGV("read packet of size %d\n", packet->range_length());
+    verifyHeader(packet, 5);
+    packet->release();
+    packet = NULL;
+}
+
+void MyVorbisExtractor::verifyHeader(
+        MediaBuffer *buffer, uint8_t type) {
+    const uint8_t *data =
+        (const uint8_t *)buffer->data() + buffer->range_offset();
+
+    size_t size = buffer->range_length();
+
+    CHECK(size >= 7);
+
+    CHECK_EQ(data[0], type);
+    CHECK(!memcmp(&data[1], "vorbis", 6));
+
+    ogg_buffer buf;
+    buf.data = (uint8_t *)data;
+    buf.size = size;
+    buf.refcount = 1;
+    buf.ptr.owner = NULL;
+
+    ogg_reference ref;
+    ref.buffer = &buf;
+    ref.begin = 0;
+    ref.length = size;
+    ref.next = NULL;
+
+    oggpack_buffer bits;
+    oggpack_readinit(&bits, &ref);
+
+    CHECK_EQ(oggpack_read(&bits, 8), type);
+    for (size_t i = 0; i < 6; ++i) {
+        oggpack_read(&bits, 8);  // skip 'vorbis'
+    }
+
+    switch (type) {
+        case 1:
+        {
+            CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits));
+
+            mMeta->setData(kKeyVorbisInfo, 0, data, size);
+            mMeta->setInt32(kKeySampleRate, mVi.rate);
+            mMeta->setInt32(kKeyChannelCount, mVi.channels);
+
+            LOGV("lower-bitrate = %ld", mVi.bitrate_lower);
+            LOGV("upper-bitrate = %ld", mVi.bitrate_upper);
+            LOGV("nominal-bitrate = %ld", mVi.bitrate_nominal);
+            LOGV("window-bitrate = %ld", mVi.bitrate_window);
+
+            off_t size;
+            if (mSource->getSize(&size) == OK) {
+                uint64_t bps = approxBitrate();
+
+                mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
+            }
+            break;
+        }
+
+        case 3:
+        {
+            CHECK_EQ(0, _vorbis_unpack_comment(&mVc, &bits));
+            break;
+        }
+
+        case 5:
+        {
+            CHECK_EQ(0, _vorbis_unpack_books(&mVi, &bits));
+
+            mMeta->setData(kKeyVorbisBooks, 0, data, size);
+            break;
+        }
+    }
+}
+
+uint64_t MyVorbisExtractor::approxBitrate() {
+    if (mVi.bitrate_nominal != 0) {
+        return mVi.bitrate_nominal;
+    }
+
+    return (mVi.bitrate_lower + mVi.bitrate_upper) / 2;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+OggExtractor::OggExtractor(const sp<DataSource> &source)
+    : mDataSource(source),
+      mInitCheck(NO_INIT),
+      mImpl(NULL) {
+    mImpl = new MyVorbisExtractor(mDataSource);
+    CHECK_EQ(mImpl->seekToOffset(0), OK);
+    mImpl->init();
+
+    mInitCheck = OK;
+}
+
+OggExtractor::~OggExtractor() {
+    delete mImpl;
+    mImpl = NULL;
+}
+
+size_t OggExtractor::countTracks() {
+    return mInitCheck != OK ? 0 : 1;
+}
+
+sp<MediaSource> OggExtractor::getTrack(size_t index) {
+    if (index >= 1) {
+        return NULL;
+    }
+
+    return new OggSource(this);
+}
+
+sp<MetaData> OggExtractor::getTrackMetaData(
+        size_t index, uint32_t flags) {
+    if (index >= 1) {
+        return NULL;
+    }
+
+    return mImpl->getFormat();
+}
+
+sp<MetaData> OggExtractor::getMetaData() {
+    sp<MetaData> meta = new MetaData;
+
+    if (mInitCheck != OK) {
+        return meta;
+    }
+
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
+
+    return meta;
+}
+
+bool SniffOgg(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+    char tmp[4];
+    if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
+        return false;
+    }
+
+    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG);
+    *confidence = 0.2f;
+
+    return true;
+}
+
+}  // namespace android
index 34fb2bc..03287dd 100644 (file)
@@ -27,8 +27,8 @@
 #include <libsonivox/eas.h>
 
 // Ogg Vorbis includes
-#include "ivorbiscodec.h"
-#include "ivorbisfile.h"
+#include <Tremolo/ivorbiscodec.h>
+#include <Tremolo/ivorbisfile.h>
 
 namespace android {
 
index 2720f93..c563ce6 100644 (file)
@@ -32,6 +32,18 @@ uint64_t U64_AT(const uint8_t *ptr) {
     return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4);
 }
 
+uint16_t U16LE_AT(const uint8_t *ptr) {
+    return ptr[0] | (ptr[1] << 8);
+}
+
+uint32_t U32LE_AT(const uint8_t *ptr) {
+    return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0];
+}
+
+uint64_t U64LE_AT(const uint8_t *ptr) {
+    return ((uint64_t)U32LE_AT(ptr + 4)) << 32 | U32LE_AT(ptr);
+}
+
 // XXX warning: these won't work on big-endian host.
 uint64_t ntoh64(uint64_t x) {
     return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32);
diff --git a/media/libstagefright/VorbisExtractor.cpp b/media/libstagefright/VorbisExtractor.cpp
deleted file mode 100644 (file)
index e7b62d6..0000000
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "VorbisExtractor"
-#include <utils/Log.h>
-
-#include "include/VorbisExtractor.h"
-
-#include <cutils/properties.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <utils/String8.h>
-
-#include <ivorbisfile.h>
-
-namespace android {
-
-struct VorbisDataSource {
-    sp<DataSource> mDataSource;
-    off_t mOffset;
-    bool mSeekDisabled;
-};
-
-static bool ShouldDisableSeek(const sp<DataSource> &source) {
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("media.vorbis.always-allow-seek", value, NULL)
-            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
-        return false;
-    }
-
-    // This is a workaround for an application streaming data through
-    // a local HTTP proxy that doesn't really conform to the HTTP/1.1
-    // specs. We have to disable seek functionality in this case.
-
-    return source->flags() & DataSource::kStreamedFromLocalHost;
-}
-
-static size_t VorbisRead(
-        void *ptr, size_t size, size_t nmemb, void *datasource) {
-    VorbisDataSource *vds = (VorbisDataSource *)datasource;
-
-    ssize_t n = vds->mDataSource->readAt(vds->mOffset, ptr, size * nmemb);
-
-    if (n < 0) {
-        return n;
-    }
-
-    vds->mOffset += n;
-
-    return n / size;
-}
-
-static int VorbisSeek(
-        void *datasource, ogg_int64_t offset, int whence) {
-    VorbisDataSource *vds = (VorbisDataSource *)datasource;
-
-    if (vds->mSeekDisabled) {
-        errno = ESPIPE;
-        return -1;
-    }
-
-    switch (whence) {
-        case SEEK_SET:
-            vds->mOffset = offset;
-            break;
-        case SEEK_END:
-        {
-            off_t size;
-            if (vds->mDataSource->getSize(&size) != OK) {
-                errno = ESPIPE;
-                return -1;
-            }
-
-            vds->mOffset = offset + size;
-            break;
-        }
-
-        case SEEK_CUR:
-        {
-            vds->mOffset += offset;
-            break;
-        }
-
-        default:
-        {
-            errno = EINVAL;
-            return -1;
-        }
-    }
-
-    return 0;
-}
-
-static int VorbisClose(void *datasource) {
-    return 0;
-}
-
-static long VorbisTell(void *datasource) {
-    VorbisDataSource *vds = (VorbisDataSource *)datasource;
-
-    return vds->mOffset;
-}
-
-static const ov_callbacks gVorbisCallbacks = {
-    &VorbisRead,
-    &VorbisSeek,
-    &VorbisClose,
-    &VorbisTell
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-struct VorbisSource : public MediaSource {
-    VorbisSource(const sp<VorbisExtractor> &extractor,
-                 const sp<MetaData> &meta, OggVorbis_File *file);
-
-    virtual sp<MetaData> getFormat();
-
-    virtual status_t start(MetaData *params = NULL);
-    virtual status_t stop();
-
-    virtual status_t read(
-            MediaBuffer **buffer, const ReadOptions *options = NULL);
-
-protected:
-    virtual ~VorbisSource();
-
-private:
-    enum {
-        kMaxBufferSize = 8192
-    };
-
-    sp<VorbisExtractor> mExtractor;
-    sp<MetaData> mMeta;
-    OggVorbis_File *mFile;
-    MediaBufferGroup *mGroup;
-
-    VorbisSource(const VorbisSource &);
-    VorbisSource &operator=(const VorbisSource &);
-};
-
-VorbisSource::VorbisSource(
-        const sp<VorbisExtractor> &extractor,
-        const sp<MetaData> &meta, OggVorbis_File *file)
-    : mExtractor(extractor),
-      mMeta(meta),
-      mFile(file),
-      mGroup(NULL) {
-}
-
-VorbisSource::~VorbisSource() {
-    if (mGroup) {
-        stop();
-    }
-}
-
-sp<MetaData> VorbisSource::getFormat() {
-    return mMeta;
-}
-
-status_t VorbisSource::start(MetaData *params) {
-    if (mGroup != NULL) {
-        return INVALID_OPERATION;
-    }
-
-    mGroup = new MediaBufferGroup;
-    mGroup->add_buffer(new MediaBuffer(kMaxBufferSize));
-
-    return OK;
-}
-
-status_t VorbisSource::stop() {
-    delete mGroup;
-    mGroup = NULL;
-
-    return OK;
-}
-
-status_t VorbisSource::read(
-        MediaBuffer **out, const ReadOptions *options) {
-    *out = NULL;
-
-    int64_t seekTimeUs;
-    if (options && options->getSeekTo(&seekTimeUs)) {
-        ov_time_seek(mFile, seekTimeUs / 1000ll);
-    }
-
-    MediaBuffer *buffer;
-    CHECK_EQ(OK, mGroup->acquire_buffer(&buffer));
-
-    ogg_int64_t positionMs = ov_time_tell(mFile);
-
-    int bitstream;
-    long n = ov_read(mFile, buffer->data(), buffer->size(), &bitstream);
-
-    if (n <= 0) {
-        LOGE("ov_read returned %ld", n);
-
-        buffer->release();
-        buffer = NULL;
-
-        return n < 0 ? ERROR_MALFORMED : ERROR_END_OF_STREAM;
-    }
-
-    buffer->set_range(0, n);
-    buffer->meta_data()->setInt64(kKeyTime, positionMs * 1000ll);
-
-    *out = buffer;
-
-    return OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-VorbisExtractor::VorbisExtractor(const sp<DataSource> &source)
-    : mDataSource(source),
-      mFile(new OggVorbis_File),
-      mVorbisDataSource(new VorbisDataSource),
-      mInitCheck(NO_INIT) {
-    mVorbisDataSource->mDataSource = mDataSource;
-    mVorbisDataSource->mOffset = 0;
-    mVorbisDataSource->mSeekDisabled = ShouldDisableSeek(mDataSource);
-
-    int res = ov_open_callbacks(
-            mVorbisDataSource, mFile, NULL, 0, gVorbisCallbacks);
-
-    if (res != 0) {
-        return;
-    }
-
-    mMeta = new MetaData;
-    mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
-
-    vorbis_info *vi = ov_info(mFile, -1);
-    mMeta->setInt32(kKeySampleRate, vi->rate);
-    mMeta->setInt32(kKeyChannelCount, vi->channels);
-
-    ogg_int64_t durationMs = ov_time_total(mFile, -1);
-    mMeta->setInt64(kKeyDuration, durationMs * 1000ll);
-
-    LOGI("Successfully initialized.");
-
-    mInitCheck = OK;
-}
-
-VorbisExtractor::~VorbisExtractor() {
-    CHECK_EQ(0, ov_clear(mFile));
-
-    delete mVorbisDataSource;
-    mVorbisDataSource = NULL;
-
-    delete mFile;
-    mFile = NULL;
-}
-
-size_t VorbisExtractor::countTracks() {
-    return mInitCheck != OK ? 0 : 1;
-}
-
-sp<MediaSource> VorbisExtractor::getTrack(size_t index) {
-    if (index >= 1) {
-        return NULL;
-    }
-
-    return new VorbisSource(this, mMeta, mFile);
-}
-
-sp<MetaData> VorbisExtractor::getTrackMetaData(
-        size_t index, uint32_t flags) {
-    if (index >= 1) {
-        return NULL;
-    }
-
-    return mMeta;
-}
-
-sp<MetaData> VorbisExtractor::getMetaData() {
-    sp<MetaData> meta = new MetaData;
-
-    if (mInitCheck != OK) {
-        return meta;
-    }
-
-    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_VORBIS);
-
-    return meta;
-}
-
-bool SniffVorbis(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
-    OggVorbis_File file;
-
-    VorbisDataSource vds;
-    vds.mDataSource = source;
-    vds.mOffset = 0;
-    vds.mSeekDisabled = ShouldDisableSeek(source);
-
-    int res = ov_test_callbacks(&vds, &file, NULL, 0, gVorbisCallbacks);
-
-    CHECK_EQ(0, ov_clear(&file));
-
-    if (res != 0) {
-        return false;
-    }
-
-    *mimeType = MEDIA_MIMETYPE_CONTAINER_VORBIS;
-    *confidence = 0.4f;
-
-    LOGV("This looks like an Ogg file.");
-
-    return true;
-}
-
-uint32_t VorbisExtractor::flags() const {
-    if (ShouldDisableSeek(mDataSource)) {
-        LOGI("This is streamed from local host, seek disabled");
-        return CAN_PAUSE;
-    } else {
-        return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE;
-    }
-}
-
-}  // namespace android
diff --git a/media/libstagefright/codecs/vorbis/Android.mk b/media/libstagefright/codecs/vorbis/Android.mk
new file mode 100644 (file)
index 0000000..2e43120
--- /dev/null
@@ -0,0 +1,4 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk
new file mode 100644 (file)
index 0000000..5c768c8
--- /dev/null
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        VorbisDecoder.cpp \
+
+LOCAL_C_INCLUDES := \
+        frameworks/base/media/libstagefright/include \
+        external/tremolo
+
+LOCAL_MODULE := libstagefright_vorbisdec
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
new file mode 100644 (file)
index 0000000..5485f25
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VorbisDecoder.h"
+
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+extern "C" {
+    #include <Tremolo/codec_internal.h>
+
+    int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb);
+    int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb);
+    int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb);
+}
+
+namespace android {
+
+VorbisDecoder::VorbisDecoder(const sp<MediaSource> &source)
+    : mSource(source),
+      mStarted(false),
+      mBufferGroup(NULL),
+      mAnchorTimeUs(0),
+      mNumFramesOutput(0),
+      mState(NULL),
+      mVi(NULL) {
+    sp<MetaData> srcFormat = mSource->getFormat();
+    CHECK(srcFormat->findInt32(kKeyChannelCount, &mNumChannels));
+    CHECK(srcFormat->findInt32(kKeySampleRate, &mSampleRate));
+}
+
+VorbisDecoder::~VorbisDecoder() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+static void makeBitReader(
+        const void *data, size_t size,
+        ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) {
+    buf->data = (uint8_t *)data;
+    buf->size = size;
+    buf->refcount = 1;
+    buf->ptr.owner = NULL;
+
+    ref->buffer = buf;
+    ref->begin = 0;
+    ref->length = size;
+    ref->next = NULL;
+
+    oggpack_readinit(bits, ref);
+}
+
+status_t VorbisDecoder::start(MetaData *params) {
+    CHECK(!mStarted);
+
+    mBufferGroup = new MediaBufferGroup;
+    mBufferGroup->add_buffer(
+            new MediaBuffer(kMaxNumSamplesPerBuffer * sizeof(int16_t)));
+
+    mSource->start();
+
+    sp<MetaData> meta = mSource->getFormat();
+
+    mVi = new vorbis_info;
+    vorbis_info_init(mVi);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    CHECK(meta->findData(kKeyVorbisInfo, &type, &data, &size));
+
+    ogg_buffer buf;
+    ogg_reference ref;
+    oggpack_buffer bits;
+    makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits);
+    CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits));
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));
+
+    makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits);
+    CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits));
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    mState = new vorbis_dsp_state;
+    CHECK_EQ(0, vorbis_dsp_init(mState, mVi));
+
+    mAnchorTimeUs = 0;
+    mNumFramesOutput = 0;
+    mStarted = true;
+
+    return OK;
+}
+
+status_t VorbisDecoder::stop() {
+    CHECK(mStarted);
+
+    vorbis_dsp_clear(mState);
+    delete mState;
+    mState = NULL;
+
+    vorbis_info_clear(mVi);
+    delete mVi;
+    mVi = NULL;
+
+    delete mBufferGroup;
+    mBufferGroup = NULL;
+
+    mSource->stop();
+
+    mStarted = false;
+
+    return OK;
+}
+
+sp<MetaData> VorbisDecoder::getFormat() {
+    sp<MetaData> srcFormat = mSource->getFormat();
+
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+    meta->setInt32(kKeyChannelCount, mNumChannels);
+    meta->setInt32(kKeySampleRate, mSampleRate);
+
+    int64_t durationUs;
+    if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
+        meta->setInt64(kKeyDuration, durationUs);
+    }
+
+    meta->setCString(kKeyDecoderComponent, "VorbisDecoder");
+
+    return meta;
+}
+
+int VorbisDecoder::decodePacket(MediaBuffer *packet, MediaBuffer *out) {
+    ogg_buffer buf;
+    buf.data = (uint8_t *)packet->data() + packet->range_offset();
+    buf.size = packet->range_length();
+    buf.refcount = 1;
+    buf.ptr.owner = NULL;
+
+    ogg_reference ref;
+    ref.buffer = &buf;
+    ref.begin = 0;
+    ref.length = packet->range_length();
+    ref.next = NULL;
+
+    ogg_packet pack;
+    pack.packet = &ref;
+    pack.bytes = packet->range_length();
+    pack.b_o_s = 0;
+    pack.e_o_s = 0;
+    pack.granulepos = 0;
+    pack.packetno = 0;
+
+    int err = vorbis_dsp_synthesis(mState, &pack, 1);
+    if (err != 0) {
+        LOGW("vorbis_dsp_synthesis returned %d", err);
+        return 0;
+    }
+
+    int numFrames = vorbis_dsp_pcmout(
+            mState, (int16_t *)out->data(), kMaxNumSamplesPerBuffer);
+
+    if (numFrames < 0) {
+        LOGE("vorbis_dsp_pcmout returned %d", numFrames);
+        return 0;
+    }
+
+    out->set_range(0, numFrames * sizeof(int16_t) * mNumChannels);
+
+    return numFrames;
+}
+
+status_t VorbisDecoder::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    status_t err;
+
+    *out = NULL;
+
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        CHECK(seekTimeUs >= 0);
+
+        mNumFramesOutput = 0;
+    } else {
+        seekTimeUs = -1;
+    }
+
+    MediaBuffer *inputBuffer;
+    err = mSource->read(&inputBuffer, options);
+
+    if (err != OK) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    int64_t timeUs;
+    if (inputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
+        mAnchorTimeUs = timeUs;
+        mNumFramesOutput = 0;
+    } else {
+        // We must have a new timestamp after seeking.
+        CHECK(seekTimeUs < 0);
+    }
+
+    MediaBuffer *outputBuffer;
+    CHECK_EQ(mBufferGroup->acquire_buffer(&outputBuffer), OK);
+
+    int numFrames = decodePacket(inputBuffer, outputBuffer);
+
+    inputBuffer->release();
+    inputBuffer = NULL;
+
+    outputBuffer->meta_data()->setInt64(
+            kKeyTime,
+            mAnchorTimeUs
+                + (mNumFramesOutput * 1000000ll) / mSampleRate);
+
+    mNumFramesOutput += numFrames;
+
+    *out = outputBuffer;
+
+    return OK;
+}
+
+}  // namespace android
similarity index 68%
rename from media/libstagefright/include/VorbisExtractor.h
rename to media/libstagefright/include/OggExtractor.h
index 2bb7deb..7066669 100644 (file)
  * limitations under the License.
  */
 
-#ifndef VORBIS_EXTRACTOR_H_
+#ifndef OGG_EXTRACTOR_H_
 
-#define VORBIS_EXTRACTOR_H_
+#define OGG_EXTRACTOR_H_
 
 #include <media/stagefright/MediaExtractor.h>
 
-struct OggVorbis_File;
-
 namespace android {
 
 class DataSource;
 class String8;
 
-struct VorbisDataSource;
+struct MyVorbisExtractor;
+struct OggSource;
 
-struct VorbisExtractor : public MediaExtractor {
-    VorbisExtractor(const sp<DataSource> &source);
+struct OggExtractor : public MediaExtractor {
+    OggExtractor(const sp<DataSource> &source);
 
     virtual size_t countTracks();
     virtual sp<MediaSource> getTrack(size_t index);
@@ -38,25 +37,24 @@ struct VorbisExtractor : public MediaExtractor {
 
     virtual sp<MetaData> getMetaData();
 
-    uint32_t flags() const;
-
 protected:
-    virtual ~VorbisExtractor();
+    virtual ~OggExtractor();
 
 private:
+    friend struct OggSource;
+
     sp<DataSource> mDataSource;
-    struct OggVorbis_File *mFile;
-    struct VorbisDataSource *mVorbisDataSource;
     status_t mInitCheck;
-    sp<MetaData> mMeta;
 
-    VorbisExtractor(const VorbisExtractor &);
-    VorbisExtractor &operator=(const VorbisExtractor &);
+    MyVorbisExtractor *mImpl;
+
+    OggExtractor(const OggExtractor &);
+    OggExtractor &operator=(const OggExtractor &);
 };
 
-bool SniffVorbis(
+bool SniffOgg(
         const sp<DataSource> &source, String8 *mimeType, float *confidence);
 
 }  // namespace android
 
-#endif  // VORBIS_EXTRACTOR_H_
+#endif  // OGG_EXTRACTOR_H_
diff --git a/media/libstagefright/include/VorbisDecoder.h b/media/libstagefright/include/VorbisDecoder.h
new file mode 100644 (file)
index 0000000..e9a488a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VORBIS_DECODER_H_
+
+#define VORBIS_DECODER_H_
+
+#include <media/stagefright/MediaSource.h>
+
+struct vorbis_dsp_state;
+struct vorbis_info;
+
+namespace android {
+
+struct MediaBufferGroup;
+
+struct VorbisDecoder : public MediaSource {
+    VorbisDecoder(const sp<MediaSource> &source);
+
+    virtual status_t start(MetaData *params);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+    virtual ~VorbisDecoder();
+
+private:
+    enum {
+        kMaxNumSamplesPerBuffer = 8192 * 2
+    };
+
+    sp<MediaSource> mSource;
+    bool mStarted;
+
+    MediaBufferGroup *mBufferGroup;
+
+    int32_t mNumChannels;
+    int32_t mSampleRate;
+    int64_t mAnchorTimeUs;
+    int64_t mNumFramesOutput;
+
+    vorbis_dsp_state *mState;
+    vorbis_info *mVi;
+
+    int decodePacket(MediaBuffer *packet, MediaBuffer *out);
+
+    VorbisDecoder(const VorbisDecoder &);
+    VorbisDecoder &operator=(const VorbisDecoder &);
+};
+
+}  // namespace android
+
+#endif  // VORBIS_DECODER_H_
+