am d13efb20: Merge "A new OggExtractor/VorbisDecoder combo to support approximate seeking." into froyo

Merge commit 'd13efb20089e44e6958cb9704864c03821f19e1c' into froyo-plus-aosp

* commit 'd13efb20089e44e6958cb9704864c03821f19e1c':
  A new OggExtractor/VorbisDecoder combo to support approximate seeking.
This commit is contained in:
Andreas Huber
2010-05-10 09:43:26 -07:00
committed by Android Git Automerger
17 changed files with 974 additions and 374 deletions

View 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_MPEG;
extern const char *MEDIA_MIMETYPE_AUDIO_AAC; extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
extern const char *MEDIA_MIMETYPE_AUDIO_QCELP; 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_AUDIO_RAW;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV; extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
extern const char *MEDIA_MIMETYPE_CONTAINER_VORBIS; extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
} // namespace android } // namespace android

View File

@ -37,6 +37,8 @@ enum {
kKeyBitRate = 'brte', // int32_t (bps) kKeyBitRate = 'brte', // int32_t (bps)
kKeyESDS = 'esds', // raw data kKeyESDS = 'esds', // raw data
kKeyAVCC = 'avcc', // raw data kKeyAVCC = 'avcc', // raw data
kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data
kKeyWantsNALFragments = 'NALf', kKeyWantsNALFragments = 'NALf',
kKeyIsSyncFrame = 'sync', // int32_t (bool) kKeyIsSyncFrame = 'sync', // int32_t (bool)
kKeyIsCodecConfig = 'conf', // int32_t (bool) kKeyIsCodecConfig = 'conf', // int32_t (bool)

View File

@ -29,6 +29,10 @@ uint16_t U16_AT(const uint8_t *ptr);
uint32_t U32_AT(const uint8_t *ptr); uint32_t U32_AT(const uint8_t *ptr);
uint64_t U64_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 ntoh64(uint64_t x);
uint64_t hton64(uint64_t x); uint64_t hton64(uint64_t x);

View File

@ -31,6 +31,7 @@ LOCAL_SRC_FILES += \
MPEG4Extractor.cpp \ MPEG4Extractor.cpp \
MPEG4Writer.cpp \ MPEG4Writer.cpp \
MediaExtractor.cpp \ MediaExtractor.cpp \
OggExtractor.cpp \
Prefetcher.cpp \ Prefetcher.cpp \
SampleIterator.cpp \ SampleIterator.cpp \
SampleTable.cpp \ SampleTable.cpp \
@ -39,7 +40,6 @@ LOCAL_SRC_FILES += \
StagefrightMetadataRetriever.cpp \ StagefrightMetadataRetriever.cpp \
TimeSource.cpp \ TimeSource.cpp \
TimedEventQueue.cpp \ TimedEventQueue.cpp \
VorbisExtractor.cpp \
WAVExtractor.cpp \ WAVExtractor.cpp \
string.cpp string.cpp
@ -50,7 +50,7 @@ LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \ $(JNI_H_INCLUDE) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
$(TOP)/external/opencore/android \ $(TOP)/external/opencore/android \
$(TOP)/external/tremolo/Tremolo $(TOP)/external/tremolo
LOCAL_SHARED_LIBRARIES := \ LOCAL_SHARED_LIBRARIES := \
libbinder \ libbinder \
@ -70,7 +70,8 @@ LOCAL_STATIC_LIBRARIES := \
libstagefright_amrwbdec \ libstagefright_amrwbdec \
libstagefright_avcdec \ libstagefright_avcdec \
libstagefright_m4vh263dec \ libstagefright_m4vh263dec \
libstagefright_mp3dec libstagefright_mp3dec \
libstagefright_vorbisdec
LOCAL_SHARED_LIBRARIES += \ LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \ libstagefright_amrnb_common \

View File

@ -18,7 +18,7 @@
#include "include/MP3Extractor.h" #include "include/MP3Extractor.h"
#include "include/MPEG4Extractor.h" #include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h" #include "include/WAVExtractor.h"
#include "include/VorbisExtractor.h" #include "include/OggExtractor.h"
#include <media/stagefright/CachingDataSource.h> #include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h> #include <media/stagefright/DataSource.h>
@ -93,7 +93,7 @@ void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffMPEG4); RegisterSniffer(SniffMPEG4);
RegisterSniffer(SniffAMR); RegisterSniffer(SniffAMR);
RegisterSniffer(SniffWAV); RegisterSniffer(SniffWAV);
RegisterSniffer(SniffVorbis); RegisterSniffer(SniffOgg);
} }
// static // static

View 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_MPEG = "audio/mpeg";
const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp"; 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_AUDIO_RAW = "audio/raw";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav"; 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 } // namespace android

View File

@ -22,7 +22,7 @@
#include "include/MP3Extractor.h" #include "include/MP3Extractor.h"
#include "include/MPEG4Extractor.h" #include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h" #include "include/WAVExtractor.h"
#include "include/VorbisExtractor.h" #include "include/OggExtractor.h"
#include <media/stagefright/DataSource.h> #include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaDefs.h>
@ -67,8 +67,8 @@ sp<MediaExtractor> MediaExtractor::Create(
return new AMRExtractor(source); return new AMRExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) { } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
return new WAVExtractor(source); return new WAVExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_VORBIS)) { } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
return new VorbisExtractor(source); return new OggExtractor(source);
} }
return NULL; return NULL;

View File

@ -25,6 +25,7 @@
#include "include/AVCDecoder.h" #include "include/AVCDecoder.h"
#include "include/M4vH263Decoder.h" #include "include/M4vH263Decoder.h"
#include "include/MP3Decoder.h" #include "include/MP3Decoder.h"
#include "include/VorbisDecoder.h"
#include "include/ESDS.h" #include "include/ESDS.h"
@ -67,6 +68,7 @@ FACTORY_CREATE(AMRWBDecoder)
FACTORY_CREATE(AACDecoder) FACTORY_CREATE(AACDecoder)
FACTORY_CREATE(AVCDecoder) FACTORY_CREATE(AVCDecoder)
FACTORY_CREATE(M4vH263Decoder) FACTORY_CREATE(M4vH263Decoder)
FACTORY_CREATE(VorbisDecoder)
FACTORY_CREATE(AMRNBEncoder) FACTORY_CREATE(AMRNBEncoder)
static sp<MediaSource> InstantiateSoftwareCodec( static sp<MediaSource> InstantiateSoftwareCodec(
@ -83,6 +85,7 @@ static sp<MediaSource> InstantiateSoftwareCodec(
FACTORY_REF(AACDecoder) FACTORY_REF(AACDecoder)
FACTORY_REF(AVCDecoder) FACTORY_REF(AVCDecoder)
FACTORY_REF(M4vH263Decoder) FACTORY_REF(M4vH263Decoder)
FACTORY_REF(VorbisDecoder)
FACTORY_REF(AMRNBEncoder) FACTORY_REF(AMRNBEncoder)
}; };
for (size_t i = 0; 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, "OMX.TI.Video.Decoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
// { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" }, // { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },
{ MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" },
}; };
static const CodecInfo kEncoderInfo[] = { static const CodecInfo kEncoderInfo[] = {

View File

@ -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

View File

@ -27,8 +27,8 @@
#include <libsonivox/eas.h> #include <libsonivox/eas.h>
// Ogg Vorbis includes // Ogg Vorbis includes
#include "ivorbiscodec.h" #include <Tremolo/ivorbiscodec.h>
#include "ivorbisfile.h" #include <Tremolo/ivorbisfile.h>
namespace android { namespace android {

View File

@ -32,6 +32,18 @@ uint64_t U64_AT(const uint8_t *ptr) {
return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4); 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. // XXX warning: these won't work on big-endian host.
uint64_t ntoh64(uint64_t x) { uint64_t ntoh64(uint64_t x) {
return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32); return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32);

View File

@ -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

View File

@ -0,0 +1,4 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@ -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)

View File

@ -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

View File

@ -14,23 +14,22 @@
* limitations under the License. * 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> #include <media/stagefright/MediaExtractor.h>
struct OggVorbis_File;
namespace android { namespace android {
class DataSource; class DataSource;
class String8; class String8;
struct VorbisDataSource; struct MyVorbisExtractor;
struct OggSource;
struct VorbisExtractor : public MediaExtractor { struct OggExtractor : public MediaExtractor {
VorbisExtractor(const sp<DataSource> &source); OggExtractor(const sp<DataSource> &source);
virtual size_t countTracks(); virtual size_t countTracks();
virtual sp<MediaSource> getTrack(size_t index); virtual sp<MediaSource> getTrack(size_t index);
@ -38,25 +37,24 @@ struct VorbisExtractor : public MediaExtractor {
virtual sp<MetaData> getMetaData(); virtual sp<MetaData> getMetaData();
uint32_t flags() const;
protected: protected:
virtual ~VorbisExtractor(); virtual ~OggExtractor();
private: private:
sp<DataSource> mDataSource; friend struct OggSource;
struct OggVorbis_File *mFile;
struct VorbisDataSource *mVorbisDataSource;
status_t mInitCheck;
sp<MetaData> mMeta;
VorbisExtractor(const VorbisExtractor &); sp<DataSource> mDataSource;
VorbisExtractor &operator=(const VorbisExtractor &); status_t mInitCheck;
MyVorbisExtractor *mImpl;
OggExtractor(const OggExtractor &);
OggExtractor &operator=(const OggExtractor &);
}; };
bool SniffVorbis( bool SniffOgg(
const sp<DataSource> &source, String8 *mimeType, float *confidence); const sp<DataSource> &source, String8 *mimeType, float *confidence);
} // namespace android } // namespace android
#endif // VORBIS_EXTRACTOR_H_ #endif // OGG_EXTRACTOR_H_

View File

@ -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_