am e083d0a2: Merge "Support for Ogg Vorbis decoding in stagefright." into froyo

Merge commit 'e083d0a2f50906423ab548047d436c74648fc488' into froyo-plus-aosp

* commit 'e083d0a2f50906423ab548047d436c74648fc488':
  Support for Ogg Vorbis decoding in stagefright.
This commit is contained in:
Andreas Huber
2010-05-04 14:29:04 -07:00
committed by Android Git Automerger
8 changed files with 414 additions and 7 deletions

View File

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

View File

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

View File

@@ -39,6 +39,7 @@ LOCAL_SRC_FILES += \
StagefrightMetadataRetriever.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
VorbisExtractor.cpp \
WAVExtractor.cpp \
string.cpp

View File

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

View File

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

View File

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

View 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

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