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_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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 \
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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[] = {
|
||||||
|
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>
|
#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 {
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
* 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_
|
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