summaryrefslogtreecommitdiffstats
path: root/media/libstagefright
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-05-07 10:35:13 -0700
committerAndreas Huber <andih@google.com>2010-05-07 12:01:45 -0700
commitee7ff20e69498ebd53dd9717a0f984188341a75e (patch)
tree2338621414d60b23ec50dd5f28fc413fb957a59e /media/libstagefright
parentf9325834de1ae004212aec2fd03445b4eebfa766 (diff)
downloadframeworks_av-ee7ff20e69498ebd53dd9717a0f984188341a75e.zip
frameworks_av-ee7ff20e69498ebd53dd9717a0f984188341a75e.tar.gz
frameworks_av-ee7ff20e69498ebd53dd9717a0f984188341a75e.tar.bz2
A new OggExtractor/VorbisDecoder combo to support approximate seeking.
Change-Id: Id5d0c1c8b1adc62896bb5ed951f7b5cfda811e95 related-to-bug: 2654400
Diffstat (limited to 'media/libstagefright')
-rw-r--r--media/libstagefright/Android.mk7
-rw-r--r--media/libstagefright/DataSource.cpp4
-rw-r--r--media/libstagefright/MediaDefs.cpp3
-rw-r--r--media/libstagefright/MediaExtractor.cpp6
-rw-r--r--media/libstagefright/OMXCodec.cpp4
-rw-r--r--media/libstagefright/OggExtractor.cpp586
-rw-r--r--media/libstagefright/StagefrightMediaScanner.cpp4
-rw-r--r--media/libstagefright/Utils.cpp12
-rw-r--r--media/libstagefright/VorbisExtractor.cpp343
-rw-r--r--media/libstagefright/codecs/vorbis/Android.mk4
-rw-r--r--media/libstagefright/codecs/vorbis/dec/Android.mk13
-rw-r--r--media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp246
-rw-r--r--media/libstagefright/include/OggExtractor.h (renamed from media/libstagefright/include/VorbisExtractor.h)32
-rw-r--r--media/libstagefright/include/VorbisDecoder.h71
14 files changed, 964 insertions, 371 deletions
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 0420a60..81f995b 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -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 \
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 5db3201..a66f86b 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -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
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index db18ab6..4b3813b 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -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
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index dfddbe0..513f49c 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -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;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 6ed384c..69da7ef 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -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
index 0000000..699b955
--- /dev/null
+++ b/media/libstagefright/OggExtractor.cpp
@@ -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
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 34fb2bc..03287dd 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -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 {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 2720f93..c563ce6 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -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
index e7b62d6..0000000
--- a/media/libstagefright/VorbisExtractor.cpp
+++ /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
index 0000000..2e43120
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/Android.mk
@@ -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
index 0000000..5c768c8
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/dec/Android.mk
@@ -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
index 0000000..5485f25
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
@@ -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
diff --git a/media/libstagefright/include/VorbisExtractor.h b/media/libstagefright/include/OggExtractor.h
index 2bb7deb..7066669 100644
--- a/media/libstagefright/include/VorbisExtractor.h
+++ b/media/libstagefright/include/OggExtractor.h
@@ -14,23 +14,22 @@
* 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
index 0000000..e9a488a
--- /dev/null
+++ b/media/libstagefright/include/VorbisDecoder.h
@@ -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_
+