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:
@ -32,11 +32,12 @@ extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB;
|
||||
extern const char *MEDIA_MIMETYPE_AUDIO_MPEG;
|
||||
extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
|
||||
extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
|
||||
extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
|
||||
extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
|
||||
|
||||
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
|
||||
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
|
||||
extern const char *MEDIA_MIMETYPE_CONTAINER_VORBIS;
|
||||
extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
|
||||
|
||||
} // namespace android
|
||||
|
||||
|
@ -37,6 +37,8 @@ enum {
|
||||
kKeyBitRate = 'brte', // int32_t (bps)
|
||||
kKeyESDS = 'esds', // raw data
|
||||
kKeyAVCC = 'avcc', // raw data
|
||||
kKeyVorbisInfo = 'vinf', // raw data
|
||||
kKeyVorbisBooks = 'vboo', // raw data
|
||||
kKeyWantsNALFragments = 'NALf',
|
||||
kKeyIsSyncFrame = 'sync', // int32_t (bool)
|
||||
kKeyIsCodecConfig = 'conf', // int32_t (bool)
|
||||
|
@ -29,6 +29,10 @@ uint16_t U16_AT(const uint8_t *ptr);
|
||||
uint32_t U32_AT(const uint8_t *ptr);
|
||||
uint64_t U64_AT(const uint8_t *ptr);
|
||||
|
||||
uint16_t U16LE_AT(const uint8_t *ptr);
|
||||
uint32_t U32LE_AT(const uint8_t *ptr);
|
||||
uint64_t U64LE_AT(const uint8_t *ptr);
|
||||
|
||||
uint64_t ntoh64(uint64_t x);
|
||||
uint64_t hton64(uint64_t x);
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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[] = {
|
||||
|
586
media/libstagefright/OggExtractor.cpp
Normal file
586
media/libstagefright/OggExtractor.cpp
Normal 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
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
4
media/libstagefright/codecs/vorbis/Android.mk
Normal file
4
media/libstagefright/codecs/vorbis/Android.mk
Normal file
@ -0,0 +1,4 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
13
media/libstagefright/codecs/vorbis/dec/Android.mk
Normal file
13
media/libstagefright/codecs/vorbis/dec/Android.mk
Normal 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)
|
246
media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
Normal file
246
media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
Normal 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
|
@ -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:
|
||||
sp<DataSource> mDataSource;
|
||||
struct OggVorbis_File *mFile;
|
||||
struct VorbisDataSource *mVorbisDataSource;
|
||||
status_t mInitCheck;
|
||||
sp<MetaData> mMeta;
|
||||
friend struct OggSource;
|
||||
|
||||
VorbisExtractor(const VorbisExtractor &);
|
||||
VorbisExtractor &operator=(const VorbisExtractor &);
|
||||
sp<DataSource> mDataSource;
|
||||
status_t mInitCheck;
|
||||
|
||||
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_
|
71
media/libstagefright/include/VorbisDecoder.h
Normal file
71
media/libstagefright/include/VorbisDecoder.h
Normal 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_
|
||||
|
Reference in New Issue
Block a user