diff options
author | Andreas Huber <andih@google.com> | 2010-05-07 10:35:13 -0700 |
---|---|---|
committer | Andreas Huber <andih@google.com> | 2010-05-07 12:01:45 -0700 |
commit | ee7ff20e69498ebd53dd9717a0f984188341a75e (patch) | |
tree | 2338621414d60b23ec50dd5f28fc413fb957a59e /media | |
parent | f9325834de1ae004212aec2fd03445b4eebfa766 (diff) | |
download | frameworks_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')
-rw-r--r-- | media/libstagefright/Android.mk | 7 | ||||
-rw-r--r-- | media/libstagefright/DataSource.cpp | 4 | ||||
-rw-r--r-- | media/libstagefright/MediaDefs.cpp | 3 | ||||
-rw-r--r-- | media/libstagefright/MediaExtractor.cpp | 6 | ||||
-rw-r--r-- | media/libstagefright/OMXCodec.cpp | 4 | ||||
-rw-r--r-- | media/libstagefright/OggExtractor.cpp | 586 | ||||
-rw-r--r-- | media/libstagefright/StagefrightMediaScanner.cpp | 4 | ||||
-rw-r--r-- | media/libstagefright/Utils.cpp | 12 | ||||
-rw-r--r-- | media/libstagefright/VorbisExtractor.cpp | 343 | ||||
-rw-r--r-- | media/libstagefright/codecs/vorbis/Android.mk | 4 | ||||
-rw-r--r-- | media/libstagefright/codecs/vorbis/dec/Android.mk | 13 | ||||
-rw-r--r-- | media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp | 246 | ||||
-rw-r--r-- | media/libstagefright/include/OggExtractor.h (renamed from media/libstagefright/include/VorbisExtractor.h) | 32 | ||||
-rw-r--r-- | media/libstagefright/include/VorbisDecoder.h | 71 |
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_ + |