summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/codecs/vorbis/dec
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/codecs/vorbis/dec')
-rw-r--r--media/libstagefright/codecs/vorbis/dec/Android.mk27
-rw-r--r--media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp445
-rw-r--r--media/libstagefright/codecs/vorbis/dec/SoftVorbis.h78
3 files changed, 549 insertions, 1 deletions
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk
index 5c768c8..06f0079 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.mk
+++ b/media/libstagefright/codecs/vorbis/dec/Android.mk
@@ -6,8 +6,33 @@ LOCAL_SRC_FILES := \
LOCAL_C_INCLUDES := \
frameworks/base/media/libstagefright/include \
- external/tremolo
+ external/tremolo \
LOCAL_MODULE := libstagefright_vorbisdec
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftVorbis.cpp
+
+LOCAL_C_INCLUDES := \
+ external/tremolo \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_vorbisdec
+
+LOCAL_SHARED_LIBRARIES := \
+ libvorbisidec libstagefright libstagefright_omx \
+ libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_vorbisdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
new file mode 100644
index 0000000..4091111
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2011 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 "SoftVorbis"
+#include <utils/Log.h>
+
+#include "SoftVorbis.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.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 {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftVorbis::SoftVorbis(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mInputBufferCount(0),
+ mState(NULL),
+ mVi(NULL),
+ mAnchorTimeUs(0),
+ mNumFramesOutput(0),
+ mNumFramesLeftOnPage(-1),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftVorbis::~SoftVorbis() {
+ if (mState != NULL) {
+ vorbis_dsp_clear(mState);
+ delete mState;
+ mState = NULL;
+ }
+
+ if (mVi != NULL) {
+ vorbis_info_clear(mVi);
+ delete mVi;
+ mVi = NULL;
+ }
+}
+
+void SoftVorbis::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.audio.cMIMEType =
+ const_cast<char *>(MEDIA_MIMETYPE_AUDIO_VORBIS);
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t);
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+ addPort(def);
+}
+
+status_t SoftVorbis::initDecoder() {
+ return OK;
+}
+
+OMX_ERRORTYPE SoftVorbis::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioVorbis:
+ {
+ OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams =
+ (OMX_AUDIO_PARAM_VORBISTYPE *)params;
+
+ if (vorbisParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ vorbisParams->nBitRate = 0;
+ vorbisParams->nMinBitRate = 0;
+ vorbisParams->nMaxBitRate = 0;
+ vorbisParams->nAudioBandWidth = 0;
+ vorbisParams->nQuality = 3;
+ vorbisParams->bManaged = OMX_FALSE;
+ vorbisParams->bDownmix = OMX_FALSE;
+
+ if (!isConfigured()) {
+ vorbisParams->nChannels = 1;
+ vorbisParams->nSampleRate = 44100;
+ } else {
+ vorbisParams->nChannels = mVi->channels;
+ vorbisParams->nSampleRate = mVi->rate;
+ vorbisParams->nBitRate = mVi->bitrate_nominal;
+ vorbisParams->nMinBitRate = mVi->bitrate_lower;
+ vorbisParams->nMaxBitRate = mVi->bitrate_upper;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ pcmParams->eNumData = OMX_NumericalDataSigned;
+ pcmParams->eEndian = OMX_EndianBig;
+ pcmParams->bInterleaved = OMX_TRUE;
+ pcmParams->nBitPerSample = 16;
+ pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+ if (!isConfigured()) {
+ pcmParams->nChannels = 1;
+ pcmParams->nSamplingRate = 44100;
+ } else {
+ pcmParams->nChannels = mVi->channels;
+ pcmParams->nSamplingRate = mVi->rate;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftVorbis::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.vorbis",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioVorbis:
+ {
+ const OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams =
+ (const OMX_AUDIO_PARAM_VORBISTYPE *)params;
+
+ if (vorbisParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+bool SoftVorbis::isConfigured() const {
+ return mInputBufferCount >= 2;
+}
+
+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);
+}
+
+void SoftVorbis::onQueueFilled(OMX_U32 portIndex) {
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ if (portIndex == 0 && mInputBufferCount < 2) {
+ BufferInfo *info = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *header = info->mHeader;
+
+ const uint8_t *data = header->pBuffer + header->nOffset;
+ size_t size = header->nFilledLen;
+
+ ogg_buffer buf;
+ ogg_reference ref;
+ oggpack_buffer bits;
+
+ makeBitReader(
+ (const uint8_t *)data + 7, size - 7,
+ &buf, &ref, &bits);
+
+ if (mInputBufferCount == 0) {
+ CHECK(mVi == NULL);
+ mVi = new vorbis_info;
+ vorbis_info_init(mVi);
+
+ CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits));
+ } else {
+ CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits));
+
+ CHECK(mState == NULL);
+ mState = new vorbis_dsp_state;
+ CHECK_EQ(0, vorbis_dsp_init(mState, mVi));
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ }
+
+ inQueue.erase(inQueue.begin());
+ info->mOwnedByUs = false;
+ notifyEmptyBufferDone(header);
+
+ ++mInputBufferCount;
+
+ return;
+ }
+
+ while (!inQueue.empty() && !outQueue.empty()) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+ return;
+ }
+
+ int32_t numPageSamples;
+ CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples));
+ memcpy(&numPageSamples,
+ inHeader->pBuffer
+ + inHeader->nOffset + inHeader->nFilledLen - 4,
+ sizeof(numPageSamples));
+
+ if (numPageSamples >= 0) {
+ mNumFramesLeftOnPage = numPageSamples;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumFramesOutput = 0;
+ }
+
+ inHeader->nFilledLen -= sizeof(numPageSamples);;
+
+ ogg_buffer buf;
+ buf.data = inHeader->pBuffer + inHeader->nOffset;
+ buf.size = inHeader->nFilledLen;
+ buf.refcount = 1;
+ buf.ptr.owner = NULL;
+
+ ogg_reference ref;
+ ref.buffer = &buf;
+ ref.begin = 0;
+ ref.length = buf.size;
+ ref.next = NULL;
+
+ ogg_packet pack;
+ pack.packet = &ref;
+ pack.bytes = ref.length;
+ pack.b_o_s = 0;
+ pack.e_o_s = 0;
+ pack.granulepos = 0;
+ pack.packetno = 0;
+
+ int numFrames = 0;
+
+ int err = vorbis_dsp_synthesis(mState, &pack, 1);
+ if (err != 0) {
+ LOGW("vorbis_dsp_synthesis returned %d", err);
+ } else {
+ numFrames = vorbis_dsp_pcmout(
+ mState, (int16_t *)outHeader->pBuffer,
+ kMaxNumSamplesPerBuffer);
+
+ if (numFrames < 0) {
+ LOGE("vorbis_dsp_pcmout returned %d", numFrames);
+ numFrames = 0;
+ }
+ }
+
+ if (mNumFramesLeftOnPage >= 0) {
+ if (numFrames > mNumFramesLeftOnPage) {
+ LOGV("discarding %d frames at end of page",
+ numFrames - mNumFramesLeftOnPage);
+ numFrames = mNumFramesLeftOnPage;
+ }
+ mNumFramesLeftOnPage -= numFrames;
+ }
+
+ outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels;
+ outHeader->nOffset = 0;
+ outHeader->nFlags = 0;
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumFramesOutput * 1000000ll) / mVi->rate;
+
+ mNumFramesOutput += numFrames;
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+
+ ++mInputBufferCount;
+ }
+}
+
+void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0 && mState != NULL) {
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+
+ mNumFramesOutput = 0;
+ vorbis_dsp_restart(mState);
+ }
+}
+
+void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftVorbis(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
new file mode 100644
index 0000000..e252f55
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 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 SOFT_VORBIS_H_
+
+#define SOFT_VORBIS_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct vorbis_dsp_state;
+struct vorbis_info;
+
+namespace android {
+
+struct SoftVorbis : public SimpleSoftOMXComponent {
+ SoftVorbis(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftVorbis();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+ enum {
+ kNumBuffers = 4,
+ kMaxNumSamplesPerBuffer = 8192 * 2
+ };
+
+ size_t mInputBufferCount;
+
+ vorbis_dsp_state *mState;
+ vorbis_info *mVi;
+
+ int64_t mAnchorTimeUs;
+ int64_t mNumFramesOutput;
+ int32_t mNumFramesLeftOnPage;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+ bool isConfigured() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftVorbis);
+};
+
+} // namespace android
+
+#endif // SOFT_VORBIS_H_
+