Merge "Support for Ogg Vorbis decoding in stagefright." into froyo
This commit is contained in:
committed by
Android (Google) Code Review
commit
e083d0a2f5
@@ -36,6 +36,7 @@ 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;
|
||||
|
||||
} // namespace android
|
||||
|
||||
|
||||
@@ -678,6 +678,26 @@ static player_type getDefaultPlayerType() {
|
||||
return PV_PLAYER;
|
||||
}
|
||||
|
||||
// By default we use the VORBIS_PLAYER for vorbis playback (duh!),
|
||||
// but if the magic property is set we will use our new experimental
|
||||
// stagefright code instead.
|
||||
static player_type OverrideStagefrightForVorbis(player_type player) {
|
||||
if (player != VORBIS_PLAYER) {
|
||||
return player;
|
||||
}
|
||||
|
||||
#if BUILD_WITH_FULL_STAGEFRIGHT
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
if (property_get("media.stagefright.enable-vorbis", value, NULL)
|
||||
&& (!strcmp(value, "1") || !strcmp(value, "true"))) {
|
||||
return STAGEFRIGHT_PLAYER;
|
||||
}
|
||||
#endif
|
||||
|
||||
return VORBIS_PLAYER;
|
||||
}
|
||||
|
||||
|
||||
player_type getPlayerType(int fd, int64_t offset, int64_t length)
|
||||
{
|
||||
char buf[20];
|
||||
@@ -689,7 +709,7 @@ player_type getPlayerType(int fd, int64_t offset, int64_t length)
|
||||
|
||||
// Ogg vorbis?
|
||||
if (ident == 0x5367674f) // 'OggS'
|
||||
return VORBIS_PLAYER;
|
||||
return OverrideStagefrightForVorbis(VORBIS_PLAYER);
|
||||
|
||||
#ifndef NO_OPENCORE
|
||||
if (ident == 0x75b22630) {
|
||||
@@ -725,6 +745,13 @@ player_type getPlayerType(const char* url)
|
||||
return TEST_PLAYER;
|
||||
}
|
||||
|
||||
bool useStagefrightForHTTP = false;
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
if (property_get("media.stagefright.enable-http", value, NULL)
|
||||
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
|
||||
useStagefrightForHTTP = true;
|
||||
}
|
||||
|
||||
// use MidiFile for MIDI extensions
|
||||
int lenURL = strlen(url);
|
||||
for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
|
||||
@@ -732,17 +759,18 @@ player_type getPlayerType(const char* url)
|
||||
int start = lenURL - len;
|
||||
if (start > 0) {
|
||||
if (!strncmp(url + start, FILE_EXTS[i].extension, len)) {
|
||||
return FILE_EXTS[i].playertype;
|
||||
if (FILE_EXTS[i].playertype == VORBIS_PLAYER
|
||||
&& !strncasecmp(url, "http://", 7)
|
||||
&& useStagefrightForHTTP) {
|
||||
return STAGEFRIGHT_PLAYER;
|
||||
}
|
||||
return OverrideStagefrightForVorbis(FILE_EXTS[i].playertype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strncasecmp(url, "http://", 7)) {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
if (!property_get("media.stagefright.enable-http", value, NULL)
|
||||
|| (strcmp(value, "1") && strcasecmp(value, "true"))) {
|
||||
// For now, we're going to use PV for http-based playback
|
||||
// by default until we can clear up a few more issues.
|
||||
if (!useStagefrightForHTTP) {
|
||||
return PV_PLAYER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ LOCAL_SRC_FILES += \
|
||||
StagefrightMetadataRetriever.cpp \
|
||||
TimeSource.cpp \
|
||||
TimedEventQueue.cpp \
|
||||
VorbisExtractor.cpp \
|
||||
WAVExtractor.cpp \
|
||||
string.cpp
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "include/MP3Extractor.h"
|
||||
#include "include/MPEG4Extractor.h"
|
||||
#include "include/WAVExtractor.h"
|
||||
#include "include/VorbisExtractor.h"
|
||||
|
||||
#include <media/stagefright/CachingDataSource.h>
|
||||
#include <media/stagefright/DataSource.h>
|
||||
@@ -92,6 +93,7 @@ void DataSource::RegisterDefaultSniffers() {
|
||||
RegisterSniffer(SniffMPEG4);
|
||||
RegisterSniffer(SniffAMR);
|
||||
RegisterSniffer(SniffWAV);
|
||||
RegisterSniffer(SniffVorbis);
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -34,5 +34,6 @@ 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";
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "include/MP3Extractor.h"
|
||||
#include "include/MPEG4Extractor.h"
|
||||
#include "include/WAVExtractor.h"
|
||||
#include "include/VorbisExtractor.h"
|
||||
|
||||
#include <media/stagefright/DataSource.h>
|
||||
#include <media/stagefright/MediaDefs.h>
|
||||
@@ -62,6 +63,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);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
311
media/libstagefright/VorbisExtractor.cpp
Normal file
311
media/libstagefright/VorbisExtractor.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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 <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;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
60
media/libstagefright/include/VorbisExtractor.h
Normal file
60
media/libstagefright/include/VorbisExtractor.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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_EXTRACTOR_H_
|
||||
|
||||
#define VORBIS_EXTRACTOR_H_
|
||||
|
||||
#include <media/stagefright/MediaExtractor.h>
|
||||
|
||||
struct OggVorbis_File;
|
||||
|
||||
namespace android {
|
||||
|
||||
class DataSource;
|
||||
class String8;
|
||||
|
||||
struct VorbisDataSource;
|
||||
|
||||
struct VorbisExtractor : public MediaExtractor {
|
||||
VorbisExtractor(const sp<DataSource> &source);
|
||||
|
||||
virtual size_t countTracks();
|
||||
virtual sp<MediaSource> getTrack(size_t index);
|
||||
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
|
||||
|
||||
virtual sp<MetaData> getMetaData();
|
||||
|
||||
protected:
|
||||
virtual ~VorbisExtractor();
|
||||
|
||||
private:
|
||||
sp<DataSource> mDataSource;
|
||||
struct OggVorbis_File *mFile;
|
||||
struct VorbisDataSource *mVorbisDataSource;
|
||||
status_t mInitCheck;
|
||||
sp<MetaData> mMeta;
|
||||
|
||||
VorbisExtractor(const VorbisExtractor &);
|
||||
VorbisExtractor &operator=(const VorbisExtractor &);
|
||||
};
|
||||
|
||||
bool SniffVorbis(
|
||||
const sp<DataSource> &source, String8 *mimeType, float *confidence);
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // VORBIS_EXTRACTOR_H_
|
||||
Reference in New Issue
Block a user