summaryrefslogtreecommitdiffstats
path: root/media/libstagefright
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-05-04 11:46:42 -0700
committerAndreas Huber <andih@google.com>2010-05-04 11:58:16 -0700
commit3cf4e43cdd391502690c07e08e805aa8ff5db0c0 (patch)
tree3792432de7d3cf66218e1ed518bfbb32d7d9d4b7 /media/libstagefright
parent0b5ba9eeed56a80fed3735f5cd4951477fda79f0 (diff)
downloadframeworks_av-3cf4e43cdd391502690c07e08e805aa8ff5db0c0.zip
frameworks_av-3cf4e43cdd391502690c07e08e805aa8ff5db0c0.tar.gz
frameworks_av-3cf4e43cdd391502690c07e08e805aa8ff5db0c0.tar.bz2
Support for Ogg Vorbis decoding in stagefright.
Set the magic property media.stagefright.enable-vorbis to true to use the new implementation instead of the standalon vorbis player for file-based playback. HTTP streaming of vorbis content will always go through stagefright. Change-Id: Ie3843a99fadb22372f89540d0f8d65196e0c2af8 related-to-bug: 2654400
Diffstat (limited to 'media/libstagefright')
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/DataSource.cpp2
-rw-r--r--media/libstagefright/MediaDefs.cpp1
-rw-r--r--media/libstagefright/MediaExtractor.cpp3
-rw-r--r--media/libstagefright/VorbisExtractor.cpp311
-rw-r--r--media/libstagefright/include/VorbisExtractor.h60
6 files changed, 378 insertions, 0 deletions
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 2a65766..8191cc7 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -39,6 +39,7 @@ LOCAL_SRC_FILES += \
StagefrightMetadataRetriever.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
+ VorbisExtractor.cpp \
WAVExtractor.cpp \
string.cpp
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 86e4bfe..5db3201 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.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
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 3a89170..db18ab6 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -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
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 738e18a..832db04 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -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;
diff --git a/media/libstagefright/VorbisExtractor.cpp b/media/libstagefright/VorbisExtractor.cpp
new file mode 100644
index 0000000..96b05c0
--- /dev/null
+++ b/media/libstagefright/VorbisExtractor.cpp
@@ -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
diff --git a/media/libstagefright/include/VorbisExtractor.h b/media/libstagefright/include/VorbisExtractor.h
new file mode 100644
index 0000000..8e38a93
--- /dev/null
+++ b/media/libstagefright/include/VorbisExtractor.h
@@ -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_