summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/codecs')
-rw-r--r--media/libstagefright/codecs/aacdec/Android.mk29
-rw-r--r--media/libstagefright/codecs/aacdec/SoftAAC.cpp449
-rw-r--r--media/libstagefright/codecs/aacdec/SoftAAC.h76
-rw-r--r--media/libstagefright/codecs/amrnb/dec/Android.mk30
-rw-r--r--media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp434
-rw-r--r--media/libstagefright/codecs/amrnb/dec/SoftAMR.h87
-rw-r--r--media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp2
-rw-r--r--media/libstagefright/codecs/amrwb/src/mime_io.cpp2
-rw-r--r--media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h4
-rw-r--r--media/libstagefright/codecs/avc/dec/Android.mk55
-rw-r--r--media/libstagefright/codecs/avc/dec/SoftAVC.cpp690
-rw-r--r--media/libstagefright/codecs/avc/dec/SoftAVC.h109
-rw-r--r--media/libstagefright/codecs/g711/dec/Android.mk19
-rw-r--r--media/libstagefright/codecs/g711/dec/SoftG711.cpp302
-rw-r--r--media/libstagefright/codecs/g711/dec/SoftG711.h63
-rw-r--r--media/libstagefright/codecs/m4v_h263/dec/Android.mk26
-rw-r--r--media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp528
-rw-r--r--media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h92
-rw-r--r--media/libstagefright/codecs/mp3dec/Android.mk23
-rw-r--r--media/libstagefright/codecs/mp3dec/SoftMP3.cpp325
-rw-r--r--media/libstagefright/codecs/mp3dec/SoftMP3.h80
-rw-r--r--media/libstagefright/codecs/on2/dec/Android.mk31
-rw-r--r--media/libstagefright/codecs/on2/dec/SoftVPX.cpp366
-rw-r--r--media/libstagefright/codecs/on2/dec/SoftVPX.h70
-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
27 files changed, 4420 insertions, 22 deletions
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 69e331f..5ef54fd 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -143,14 +143,39 @@ LOCAL_SRC_FILES := \
unpack_idx.cpp \
window_tables_fxp.cpp \
pvmp4setaudioconfig.cpp \
- AACDecoder.cpp
+ AACDecoder.cpp \
LOCAL_CFLAGS := -DAAC_PLUS -DHQ_SBR -DPARAMETRICSTEREO -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF= -DOSCL_UNUSED_ARG=
-LOCAL_C_INCLUDES := frameworks/base/media/libstagefright/include
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
LOCAL_ARM_MODE := arm
LOCAL_MODULE := libstagefright_aacdec
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAAC.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_aacdec
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_aacdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
new file mode 100644
index 0000000..7ce6128
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
@@ -0,0 +1,449 @@
+/*
+ * 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 "SoftAAC"
+#include <utils/Log.h>
+
+#include "SoftAAC.h"
+
+#include "pvmp4audiodecoder_api.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+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;
+}
+
+SoftAAC::SoftAAC(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mConfig(new tPVMP4AudioDecoderExternal),
+ mDecoderBuf(NULL),
+ mInputBufferCount(0),
+ mUpsamplingFactor(2),
+ mAnchorTimeUs(0),
+ mNumSamplesOutput(0),
+ mSignalledError(false),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAAC::~SoftAAC() {
+ free(mDecoderBuf);
+ mDecoderBuf = NULL;
+
+ delete mConfig;
+ mConfig = NULL;
+}
+
+void SoftAAC::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 *>("audio/aac");
+ 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 = 8192;
+ 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 SoftAAC::initDecoder() {
+ memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal));
+ mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED;
+ mConfig->aacPlusEnabled = 1;
+
+ // The software decoder doesn't properly support mono output on
+ // AACplus files. Always output stereo.
+ mConfig->desiredChannels = 2;
+
+ UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements();
+ mDecoderBuf = malloc(memRequirements);
+
+ Int err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf);
+ if (err != MP4AUDEC_SUCCESS) {
+ LOGE("Failed to initialize MP4 audio decoder");
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAAC::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioAac:
+ {
+ OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
+ (OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
+
+ if (aacParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ aacParams->nBitRate = 0;
+ aacParams->nAudioBandWidth = 0;
+ aacParams->nAACtools = 0;
+ aacParams->nAACERtools = 0;
+ aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;
+ aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+ aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
+
+ if (!isConfigured()) {
+ aacParams->nChannels = 1;
+ aacParams->nSampleRate = 44100;
+ aacParams->nFrameLength = 0;
+ } else {
+ aacParams->nChannels = mConfig->encodedChannels;
+ aacParams->nSampleRate = mConfig->samplingRate;
+ aacParams->nFrameLength = mConfig->frameLength;
+ }
+
+ 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 = mConfig->desiredChannels;
+ pcmParams->nSamplingRate = mConfig->samplingRate;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAAC::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.aac",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAac:
+ {
+ const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
+ (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
+
+ if (aacParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+bool SoftAAC::isConfigured() const {
+ return mInputBufferCount > 0;
+}
+
+void SoftAAC::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (portIndex == 0 && mInputBufferCount == 0) {
+ ++mInputBufferCount;
+
+ BufferInfo *info = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *header = info->mHeader;
+
+ mConfig->pInputBuffer = header->pBuffer + header->nOffset;
+ mConfig->inputBufferCurrentLength = header->nFilledLen;
+ mConfig->inputBufferMaxLength = 0;
+
+ Int err = PVMP4AudioDecoderConfig(mConfig, mDecoderBuf);
+ if (err != MP4AUDEC_SUCCESS) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, err, NULL);
+ return;
+ }
+
+ inQueue.erase(inQueue.begin());
+ info->mOwnedByUs = false;
+ notifyEmptyBufferDone(header);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ 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;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumSamplesOutput = 0;
+ }
+
+ mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
+ mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->inputBufferUsedLength = 0;
+ mConfig->remainderBits = 0;
+
+ mConfig->pOutputBuffer =
+ reinterpret_cast<Int16 *>(outHeader->pBuffer + outHeader->nOffset);
+
+ mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048];
+ mConfig->repositionFlag = false;
+
+ Int32 prevSamplingRate = mConfig->samplingRate;
+ Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf);
+
+ /*
+ * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+ * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+ * rate system and the sampling rate in the final output is actually
+ * doubled compared with the core AAC decoder sampling rate.
+ *
+ * Explicit signalling is done by explicitly defining SBR audio object
+ * type in the bitstream. Implicit signalling is done by embedding
+ * SBR content in AAC extension payload specific to SBR, and hence
+ * requires an AAC decoder to perform pre-checks on actual audio frames.
+ *
+ * Thus, we could not say for sure whether a stream is
+ * AAC+/eAAC+ until the first data frame is decoded.
+ */
+ if (mInputBufferCount <= 2) {
+ LOGV("audio/extended audio object type: %d + %d",
+ mConfig->audioObjectType, mConfig->extendedAudioObjectType);
+ LOGV("aac+ upsampling factor: %d desired channels: %d",
+ mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels);
+
+ if (mInputBufferCount == 1) {
+ mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor;
+ // Check on the sampling rate to see whether it is changed.
+ if (mConfig->samplingRate != prevSamplingRate) {
+ LOGW("Sample rate was %d Hz, but now is %d Hz",
+ prevSamplingRate, mConfig->samplingRate);
+
+ // We'll hold onto the input buffer and will decode
+ // it again once the output port has been reconfigured.
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+ } else { // mInputBufferCount == 2
+ if (mConfig->extendedAudioObjectType == MP4AUDIO_AAC_LC ||
+ mConfig->extendedAudioObjectType == MP4AUDIO_LTP) {
+ if (mUpsamplingFactor == 2) {
+ // The stream turns out to be not aacPlus mode anyway
+ LOGW("Disable AAC+/eAAC+ since extended audio object "
+ "type is %d",
+ mConfig->extendedAudioObjectType);
+ mConfig->aacPlusEnabled = 0;
+ }
+ } else {
+ if (mUpsamplingFactor == 1) {
+ // aacPlus mode does not buy us anything, but to cause
+ // 1. CPU load to increase, and
+ // 2. a half speed of decoding
+ LOGW("Disable AAC+/eAAC+ since upsampling factor is 1");
+ mConfig->aacPlusEnabled = 0;
+ }
+ }
+ }
+ }
+
+ size_t numOutBytes =
+ mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels;
+
+ if (decoderErr == MP4AUDEC_SUCCESS) {
+ CHECK_LE(mConfig->inputBufferUsedLength, inHeader->nFilledLen);
+
+ inHeader->nFilledLen -= mConfig->inputBufferUsedLength;
+ inHeader->nOffset += mConfig->inputBufferUsedLength;
+ } else {
+ memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes);
+ }
+
+ if (mUpsamplingFactor == 2) {
+ if (mConfig->desiredChannels == 1) {
+ memcpy(&mConfig->pOutputBuffer[1024],
+ &mConfig->pOutputBuffer[2048],
+ numOutBytes * 2);
+ }
+ numOutBytes *= 2;
+ }
+
+ outHeader->nFilledLen = numOutBytes;
+ outHeader->nFlags = 0;
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate;
+
+ mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor;
+
+ if (inHeader->nFilledLen == 0) {
+ 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 SoftAAC::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0) {
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+ PVMP4AudioDecoderResetBuffer(mDecoderBuf);
+ }
+}
+
+void SoftAAC::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::SoftAAC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.h b/media/libstagefright/codecs/aacdec/SoftAAC.h
new file mode 100644
index 0000000..963fd27
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.h
@@ -0,0 +1,76 @@
+/*
+ * 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_AAC_H_
+
+#define SOFT_AAC_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tPVMP4AudioDecoderExternal;
+
+namespace android {
+
+struct SoftAAC : public SimpleSoftOMXComponent {
+ SoftAAC(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAAC();
+
+ 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
+ };
+
+ tPVMP4AudioDecoderExternal *mConfig;
+ void *mDecoderBuf;
+
+ size_t mInputBufferCount;
+ size_t mUpsamplingFactor;
+ int64_t mAnchorTimeUs;
+ int64_t mNumSamplesOutput;
+
+ bool mSignalledError;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+ bool isConfigured() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAAC);
+};
+
+} // namespace android
+
+#endif // SOFT_AAC_H_
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk
index a545762..296cae4 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.mk
+++ b/media/libstagefright/codecs/amrnb/dec/Android.mk
@@ -52,3 +52,33 @@ LOCAL_CFLAGS := \
LOCAL_MODULE := libstagefright_amrnbdec
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAMR.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/../common/include \
+ $(LOCAL_PATH)/../common \
+ frameworks/base/media/libstagefright/codecs/amrwb/src \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_amrnbdec libstagefright_amrwbdec
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright_omx libstagefright_foundation libutils \
+ libstagefright_amrnb_common
+
+LOCAL_MODULE := libstagefright_soft_amrdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
new file mode 100644
index 0000000..c0a588f
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
@@ -0,0 +1,434 @@
+/*
+ * 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 "SoftAMR"
+#include <utils/Log.h>
+
+#include "SoftAMR.h"
+
+#include "gsmamr_dec.h"
+#include "pvamrwbdecoder.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+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;
+}
+
+SoftAMR::SoftAMR(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mMode(MODE_NARROW),
+ mState(NULL),
+ mDecoderBuf(NULL),
+ mDecoderCookie(NULL),
+ mInputBufferCount(0),
+ mAnchorTimeUs(0),
+ mNumSamplesOutput(0),
+ mSignalledError(false),
+ mOutputPortSettingsChange(NONE) {
+ if (!strcmp(name, "OMX.google.amrwb.decoder")) {
+ mMode = MODE_WIDE;
+ } else {
+ CHECK(!strcmp(name, "OMX.google.amrnb.decoder"));
+ }
+
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAMR::~SoftAMR() {
+ if (mMode == MODE_NARROW) {
+ GSMDecodeFrameExit(&mState);
+ mState = NULL;
+ } else {
+ free(mDecoderBuf);
+ mDecoderBuf = NULL;
+
+ mState = NULL;
+ mDecoderCookie = NULL;
+ }
+}
+
+void SoftAMR::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 =
+ mMode == MODE_NARROW
+ ? const_cast<char *>("audio/amr")
+ : const_cast<char *>("audio/amrwb");
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAMR;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+
+ def.nBufferSize =
+ (mMode == MODE_NARROW ? kNumSamplesPerFrameNB : kNumSamplesPerFrameWB)
+ * 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 SoftAMR::initDecoder() {
+ if (mMode == MODE_NARROW) {
+ Word16 err = GSMInitDecode(&mState, (Word8 *)"AMRNBDecoder");
+
+ if (err != 0) {
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ int32_t memReq = pvDecoder_AmrWbMemRequirements();
+ mDecoderBuf = malloc(memReq);
+
+ pvDecoder_AmrWb_Init(&mState, mDecoderBuf, &mDecoderCookie);
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAMR::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioAmr:
+ {
+ OMX_AUDIO_PARAM_AMRTYPE *amrParams =
+ (OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+ if (amrParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ amrParams->nChannels = 1;
+ amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
+ amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatConformance;
+
+ if (!isConfigured()) {
+ amrParams->nBitRate = 0;
+ amrParams->eAMRBandMode = OMX_AUDIO_AMRBandModeUnused;
+ } else {
+ amrParams->nBitRate = 0;
+ amrParams->eAMRBandMode =
+ mMode == MODE_NARROW
+ ? OMX_AUDIO_AMRBandModeNB0 : OMX_AUDIO_AMRBandModeWB0;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ pcmParams->nChannels = 1;
+ pcmParams->eNumData = OMX_NumericalDataSigned;
+ pcmParams->eEndian = OMX_EndianBig;
+ pcmParams->bInterleaved = OMX_TRUE;
+ pcmParams->nBitPerSample = 16;
+
+ pcmParams->nSamplingRate =
+ (mMode == MODE_NARROW) ? kSampleRateNB : kSampleRateWB;
+
+ pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAMR::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (mMode == MODE_NARROW) {
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.amrnb",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ } else {
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.amrwb",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAmr:
+ {
+ const OMX_AUDIO_PARAM_AMRTYPE *aacParams =
+ (const OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+ if (aacParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+bool SoftAMR::isConfigured() const {
+ return mInputBufferCount > 0;
+}
+
+static size_t getFrameSize(unsigned FT) {
+ static const size_t kFrameSizeWB[9] = {
+ 132, 177, 253, 285, 317, 365, 397, 461, 477
+ };
+
+ size_t frameSize = kFrameSizeWB[FT];
+
+ // Round up bits to bytes and add 1 for the header byte.
+ frameSize = (frameSize + 7) / 8 + 1;
+
+ return frameSize;
+}
+
+void SoftAMR::onQueueFilled(OMX_U32 portIndex) {
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ 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;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumSamplesOutput = 0;
+ }
+
+ const uint8_t *inputPtr = inHeader->pBuffer + inHeader->nOffset;
+ int32_t numBytesRead;
+
+ if (mMode == MODE_NARROW) {
+ numBytesRead =
+ AMRDecode(mState,
+ (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f),
+ (UWord8 *)&inputPtr[1],
+ reinterpret_cast<int16_t *>(outHeader->pBuffer),
+ MIME_IETF);
+
+ if (numBytesRead == -1) {
+ LOGE("PV AMR decoder AMRDecode() call failed");
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+
+ return;
+ }
+
+ ++numBytesRead; // Include the frame type header byte.
+
+ if (static_cast<size_t>(numBytesRead) > inHeader->nFilledLen) {
+ // This is bad, should never have happened, but did. Abort now.
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+
+ return;
+ }
+ } else {
+ int16 mode = ((inputPtr[0] >> 3) & 0x0f);
+ size_t frameSize = getFrameSize(mode);
+ CHECK_GE(inHeader->nFilledLen, frameSize);
+
+ int16 frameType;
+ RX_State_wb rx_state;
+ mime_unsorting(
+ const_cast<uint8_t *>(&inputPtr[1]),
+ mInputSampleBuffer,
+ &frameType, &mode, 1, &rx_state);
+
+ int16_t *outPtr = (int16_t *)outHeader->pBuffer;
+
+ int16_t numSamplesOutput;
+ pvDecoder_AmrWb(
+ mode, mInputSampleBuffer,
+ outPtr,
+ &numSamplesOutput,
+ mDecoderBuf, frameType, mDecoderCookie);
+
+ CHECK_EQ((int)numSamplesOutput, (int)kNumSamplesPerFrameWB);
+
+ for (int i = 0; i < kNumSamplesPerFrameWB; ++i) {
+ /* Delete the 2 LSBs (14-bit output) */
+ outPtr[i] &= 0xfffC;
+ }
+
+ numBytesRead = frameSize;
+ }
+
+ inHeader->nOffset += numBytesRead;
+ inHeader->nFilledLen -= numBytesRead;
+
+ outHeader->nFlags = 0;
+ outHeader->nOffset = 0;
+
+ if (mMode == MODE_NARROW) {
+ outHeader->nFilledLen = kNumSamplesPerFrameNB * sizeof(int16_t);
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumSamplesOutput * 1000000ll) / kSampleRateNB;
+
+ mNumSamplesOutput += kNumSamplesPerFrameNB;
+ } else {
+ outHeader->nFilledLen = kNumSamplesPerFrameWB * sizeof(int16_t);
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumSamplesOutput * 1000000ll) / kSampleRateWB;
+
+ mNumSamplesOutput += kNumSamplesPerFrameWB;
+ }
+
+ if (inHeader->nFilledLen == 0) {
+ 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 SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) {
+}
+
+void SoftAMR::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::SoftAMR(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
new file mode 100644
index 0000000..9a596e5
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
@@ -0,0 +1,87 @@
+/*
+ * 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_AMR_H_
+
+#define SOFT_AMR_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftAMR : public SimpleSoftOMXComponent {
+ SoftAMR(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAMR();
+
+ 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,
+ kSampleRateNB = 8000,
+ kSampleRateWB = 16000,
+ kNumSamplesPerFrameNB = 160,
+ kNumSamplesPerFrameWB = 320,
+ };
+
+ enum {
+ MODE_NARROW,
+ MODE_WIDE
+
+ } mMode;
+
+ void *mState;
+ void *mDecoderBuf;
+ int16_t *mDecoderCookie;
+
+ size_t mInputBufferCount;
+ int64_t mAnchorTimeUs;
+ int64_t mNumSamplesOutput;
+
+ bool mSignalledError;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ int16_t mInputSampleBuffer[477];
+
+ void initPorts();
+ status_t initDecoder();
+ bool isConfigured() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAMR);
+};
+
+} // namespace android
+
+#endif // SOFT_AMR_H_
+
diff --git a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
index 2a21472..5b111ef 100644
--- a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
+++ b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
@@ -177,7 +177,7 @@ status_t AMRWBDecoder::read(
CHECK(mInputBuffer->range_length() >= frameSize);
int16 frameType;
- RX_State rx_state;
+ RX_State_wb rx_state;
mime_unsorting(
const_cast<uint8_t *>(&inputPtr[1]),
mInputSampleBuffer,
diff --git a/media/libstagefright/codecs/amrwb/src/mime_io.cpp b/media/libstagefright/codecs/amrwb/src/mime_io.cpp
index 9ff8816..e1966c6 100644
--- a/media/libstagefright/codecs/amrwb/src/mime_io.cpp
+++ b/media/libstagefright/codecs/amrwb/src/mime_io.cpp
@@ -531,7 +531,7 @@ void mime_unsorting(uint8 unsorted_bits[],
int16 * frame_type,
int16 * mode,
uint8 quality,
- RX_State *st)
+ RX_State_wb *st)
{
int16 i;
diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
index 433fc92..c40bc10 100644
--- a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
+++ b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
@@ -101,7 +101,7 @@ typedef struct
{
int16 prev_ft;
int16 prev_mode;
-} RX_State;
+} RX_State_wb;
/*----------------------------------------------------------------------------
; ENUMERATED TYPEDEF'S
@@ -141,7 +141,7 @@ typedef struct
int16 *frame_type,
int16 *mode,
uint8 q,
- RX_State *st);
+ RX_State_wb *st);
/*----------------------------------------------------------------------------
diff --git a/media/libstagefright/codecs/avc/dec/Android.mk b/media/libstagefright/codecs/avc/dec/Android.mk
index 1b00347..afecdc4 100644
--- a/media/libstagefright/codecs/avc/dec/Android.mk
+++ b/media/libstagefright/codecs/avc/dec/Android.mk
@@ -3,25 +3,54 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
AVCDecoder.cpp \
- src/avcdec_api.cpp \
- src/avc_bitstream.cpp \
- src/header.cpp \
- src/itrans.cpp \
- src/pred_inter.cpp \
- src/pred_intra.cpp \
- src/residual.cpp \
- src/slice.cpp \
- src/vlc.cpp
+ src/avcdec_api.cpp \
+ src/avc_bitstream.cpp \
+ src/header.cpp \
+ src/itrans.cpp \
+ src/pred_inter.cpp \
+ src/pred_intra.cpp \
+ src/residual.cpp \
+ src/slice.cpp \
+ src/vlc.cpp
LOCAL_MODULE := libstagefright_avcdec
LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/src \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/../common/include \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/../common/include \
$(TOP)/frameworks/base/media/libstagefright/include \
- $(TOP)/frameworks/base/include/media/stagefright/openmax
+ frameworks/base/include/media/stagefright/openmax \
LOCAL_CFLAGS := -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF=
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAVC.cpp
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/../common/include \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_avcdec
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright_avc_common \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_avcdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.cpp b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp
new file mode 100644
index 0000000..9f141ac
--- /dev/null
+++ b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp
@@ -0,0 +1,690 @@
+/*
+ * 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 "SoftAVC"
+#include <utils/Log.h>
+
+#include "SoftAVC.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include "avcdec_api.h"
+#include "avcdec_int.h"
+
+namespace android {
+
+static const char kStartCode[4] = { 0x00, 0x00, 0x00, 0x01 };
+
+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;
+}
+
+static int32_t Malloc(void *userData, int32_t size, int32_t attrs) {
+ return reinterpret_cast<int32_t>(malloc(size));
+}
+
+static void Free(void *userData, int32_t ptr) {
+ free(reinterpret_cast<void *>(ptr));
+}
+
+SoftAVC::SoftAVC(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mHandle(new tagAVCHandle),
+ mInputBufferCount(0),
+ mWidth(160),
+ mHeight(120),
+ mCropLeft(0),
+ mCropTop(0),
+ mCropRight(mWidth - 1),
+ mCropBottom(mHeight - 1),
+ mSPSSeen(false),
+ mPPSSeen(false),
+ mCurrentTimeUs(-1),
+ mEOSStatus(INPUT_DATA_AVAILABLE),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAVC::~SoftAVC() {
+ PVAVCCleanUpDecoder(mHandle);
+
+ delete mHandle;
+ mHandle = NULL;
+}
+
+void SoftAVC::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumInputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_AVC);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;
+ def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+ def.format.video.pNativeWindow = NULL;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumOutputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ def.format.video.pNativeWindow = NULL;
+
+ def.nBufferSize =
+ (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+ addPort(def);
+}
+
+status_t SoftAVC::initDecoder() {
+ memset(mHandle, 0, sizeof(tagAVCHandle));
+ mHandle->AVCObject = NULL;
+ mHandle->userData = this;
+ mHandle->CBAVC_DPBAlloc = ActivateSPSWrapper;
+ mHandle->CBAVC_FrameBind = BindFrameWrapper;
+ mHandle->CBAVC_FrameUnbind = UnbindFrame;
+ mHandle->CBAVC_Malloc = Malloc;
+ mHandle->CBAVC_Free = Free;
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAVC::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ if (formatParams->nPortIndex == 0) {
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC;
+ formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+ formatParams->xFramerate = 0;
+ } else {
+ CHECK_EQ(formatParams->nPortIndex, 1u);
+
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ formatParams->xFramerate = 0;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAVC::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,
+ "video_decoder.avc",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAVC::getConfig(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexConfigCommonOutputCrop:
+ {
+ OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
+
+ if (rectParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ rectParams->nLeft = mCropLeft;
+ rectParams->nTop = mCropTop;
+ rectParams->nWidth = mCropRight - mCropLeft + 1;
+ rectParams->nHeight = mCropBottom - mCropTop + 1;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return OMX_ErrorUnsupportedIndex;
+ }
+}
+
+static void findNALFragment(
+ const OMX_BUFFERHEADERTYPE *inHeader,
+ const uint8_t **fragPtr, size_t *fragSize) {
+ const uint8_t *data = inHeader->pBuffer + inHeader->nOffset;
+
+ size_t size = inHeader->nFilledLen;
+
+ CHECK(size >= 4);
+ CHECK(!memcmp(kStartCode, data, 4));
+
+ size_t offset = 4;
+ while (offset + 3 < size && memcmp(kStartCode, &data[offset], 4)) {
+ ++offset;
+ }
+
+ *fragPtr = &data[4];
+ if (offset + 3 >= size) {
+ *fragSize = size - 4;
+ } else {
+ *fragSize = offset - 4;
+ }
+}
+
+void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
+ return;
+ }
+
+ while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty())
+ && outQueue.size() == kNumOutputBuffers) {
+ if (mEOSStatus == INPUT_EOS_SEEN) {
+ OMX_BUFFERHEADERTYPE *outHeader;
+ if (drainOutputBuffer(&outHeader)) {
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ return;
+ }
+
+ BufferInfo *outInfo = *outQueue.begin();
+ outHeader = outInfo->mHeader;
+
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ outHeader->nTimeStamp = 0;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+
+ mEOSStatus = OUTPUT_FRAMES_FLUSHED;
+ return;
+ }
+
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ mEOSStatus = INPUT_EOS_SEEN;
+ continue;
+ }
+
+ mCurrentTimeUs = inHeader->nTimeStamp;
+
+ const uint8_t *fragPtr;
+ size_t fragSize;
+ findNALFragment(inHeader, &fragPtr, &fragSize);
+
+ bool releaseFragment;
+ OMX_BUFFERHEADERTYPE *outHeader;
+ status_t err = decodeFragment(
+ fragPtr, fragSize,
+ &releaseFragment, &outHeader);
+
+ if (releaseFragment) {
+ CHECK_GE(inHeader->nFilledLen, fragSize + 4);
+
+ inHeader->nOffset += fragSize + 4;
+ inHeader->nFilledLen -= fragSize + 4;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ }
+
+ if (outHeader != NULL) {
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ return;
+ }
+
+ if (err == INFO_FORMAT_CHANGED) {
+ return;
+ }
+
+ if (err != OK) {
+ notify(OMX_EventError, OMX_ErrorUndefined, err, NULL);
+ return;
+ }
+ }
+}
+
+status_t SoftAVC::decodeFragment(
+ const uint8_t *fragPtr, size_t fragSize,
+ bool *releaseFragment,
+ OMX_BUFFERHEADERTYPE **outHeader) {
+ *releaseFragment = true;
+ *outHeader = NULL;
+
+ int nalType;
+ int nalRefIdc;
+ AVCDec_Status res =
+ PVAVCDecGetNALType(
+ const_cast<uint8_t *>(fragPtr), fragSize,
+ &nalType, &nalRefIdc);
+
+ if (res != AVCDEC_SUCCESS) {
+ LOGV("cannot determine nal type");
+ return ERROR_MALFORMED;
+ }
+
+ if (nalType != AVC_NALTYPE_SPS && nalType != AVC_NALTYPE_PPS
+ && (!mSPSSeen || !mPPSSeen)) {
+ // We haven't seen SPS or PPS yet.
+ return OK;
+ }
+
+ switch (nalType) {
+ case AVC_NALTYPE_SPS:
+ {
+ mSPSSeen = true;
+
+ res = PVAVCDecSeqParamSet(
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
+
+ if (res != AVCDEC_SUCCESS) {
+ return ERROR_MALFORMED;
+ }
+
+ AVCDecObject *pDecVid = (AVCDecObject *)mHandle->AVCObject;
+
+ int32_t width =
+ (pDecVid->seqParams[0]->pic_width_in_mbs_minus1 + 1) * 16;
+
+ int32_t height =
+ (pDecVid->seqParams[0]->pic_height_in_map_units_minus1 + 1) * 16;
+
+ int32_t crop_left, crop_right, crop_top, crop_bottom;
+ if (pDecVid->seqParams[0]->frame_cropping_flag)
+ {
+ crop_left = 2 * pDecVid->seqParams[0]->frame_crop_left_offset;
+ crop_right =
+ width - (2 * pDecVid->seqParams[0]->frame_crop_right_offset + 1);
+
+ if (pDecVid->seqParams[0]->frame_mbs_only_flag)
+ {
+ crop_top = 2 * pDecVid->seqParams[0]->frame_crop_top_offset;
+ crop_bottom =
+ height -
+ (2 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
+ }
+ else
+ {
+ crop_top = 4 * pDecVid->seqParams[0]->frame_crop_top_offset;
+ crop_bottom =
+ height -
+ (4 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
+ }
+ } else {
+ crop_bottom = height - 1;
+ crop_right = width - 1;
+ crop_top = crop_left = 0;
+ }
+
+ status_t err = OK;
+
+ if (mWidth != width || mHeight != height) {
+ mWidth = width;
+ mHeight = height;
+
+ updatePortDefinitions();
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+
+ err = INFO_FORMAT_CHANGED;
+ }
+
+ if (mCropLeft != crop_left
+ || mCropTop != crop_top
+ || mCropRight != crop_right
+ || mCropBottom != crop_bottom) {
+ mCropLeft = crop_left;
+ mCropTop = crop_top;
+ mCropRight = crop_right;
+ mCropBottom = crop_bottom;
+
+ notify(OMX_EventPortSettingsChanged,
+ 1,
+ OMX_IndexConfigCommonOutputCrop,
+ NULL);
+ }
+
+ return err;
+ }
+
+ case AVC_NALTYPE_PPS:
+ {
+ mPPSSeen = true;
+
+ res = PVAVCDecPicParamSet(
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
+
+ if (res != AVCDEC_SUCCESS) {
+ LOGV("PVAVCDecPicParamSet returned error %d", res);
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+ }
+
+ case AVC_NALTYPE_SLICE:
+ case AVC_NALTYPE_IDR:
+ {
+ res = PVAVCDecodeSlice(
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
+
+ if (res == AVCDEC_PICTURE_OUTPUT_READY) {
+ *releaseFragment = false;
+
+ if (!drainOutputBuffer(outHeader)) {
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+ }
+
+ if (res == AVCDEC_PICTURE_READY || res == AVCDEC_SUCCESS) {
+ return OK;
+ } else {
+ LOGV("PVAVCDecodeSlice returned error %d", res);
+ return ERROR_MALFORMED;
+ }
+ }
+
+ case AVC_NALTYPE_SEI:
+ {
+ res = PVAVCDecSEI(
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
+
+ if (res != AVCDEC_SUCCESS) {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+ }
+
+ case AVC_NALTYPE_AUD:
+ case AVC_NALTYPE_FILL:
+ case AVC_NALTYPE_EOSEQ:
+ {
+ return OK;
+ }
+
+ default:
+ {
+ LOGE("Should not be here, unknown nalType %d", nalType);
+
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+bool SoftAVC::drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader) {
+ int32_t index;
+ int32_t Release;
+ AVCFrameIO Output;
+ Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
+ AVCDec_Status status =
+ PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
+
+ if (status != AVCDEC_SUCCESS) {
+ return false;
+ }
+
+ PortInfo *port = editPortInfo(1);
+ CHECK_GE(index, 0);
+ CHECK_LT((size_t)index, port->mBuffers.size());
+ CHECK(port->mBuffers.editItemAt(index).mOwnedByUs);
+
+ *outHeader = port->mBuffers.editItemAt(index).mHeader;
+ (*outHeader)->nOffset = 0;
+ (*outHeader)->nFilledLen = port->mDef.nBufferSize;
+ (*outHeader)->nFlags = 0;
+
+ return true;
+}
+
+void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0) {
+ PVAVCDecReset(mHandle);
+
+ mEOSStatus = INPUT_DATA_AVAILABLE;
+ }
+}
+
+void SoftAVC::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;
+ }
+ }
+}
+
+void SoftAVC::updatePortDefinitions() {
+ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def = &editPortInfo(1)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def->nBufferSize =
+ (def->format.video.nFrameWidth
+ * def->format.video.nFrameHeight * 3) / 2;
+}
+
+// static
+int32_t SoftAVC::ActivateSPSWrapper(
+ void *userData, unsigned int sizeInMbs, unsigned int numBuffers) {
+ return static_cast<SoftAVC *>(userData)->activateSPS(sizeInMbs, numBuffers);
+}
+
+// static
+int32_t SoftAVC::BindFrameWrapper(
+ void *userData, int32_t index, uint8_t **yuv) {
+ return static_cast<SoftAVC *>(userData)->bindFrame(index, yuv);
+}
+
+// static
+void SoftAVC::UnbindFrame(void *userData, int32_t index) {
+}
+
+int32_t SoftAVC::activateSPS(
+ unsigned int sizeInMbs, unsigned int numBuffers) {
+ PortInfo *port = editPortInfo(1);
+ CHECK_GE(port->mBuffers.size(), numBuffers);
+ CHECK_GE(port->mDef.nBufferSize, (sizeInMbs << 7) * 3);
+
+ return 1;
+}
+
+int32_t SoftAVC::bindFrame(int32_t index, uint8_t **yuv) {
+ PortInfo *port = editPortInfo(1);
+
+ CHECK_GE(index, 0);
+ CHECK_LT((size_t)index, port->mBuffers.size());
+
+ BufferInfo *outBuffer =
+ &port->mBuffers.editItemAt(index);
+
+ CHECK(outBuffer->mOwnedByUs);
+
+ outBuffer->mHeader->nTimeStamp = mCurrentTimeUs;
+ *yuv = outBuffer->mHeader->pBuffer;
+
+ return 1;
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftAVC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.h b/media/libstagefright/codecs/avc/dec/SoftAVC.h
new file mode 100644
index 0000000..1594b4d
--- /dev/null
+++ b/media/libstagefright/codecs/avc/dec/SoftAVC.h
@@ -0,0 +1,109 @@
+/*
+ * 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_AVC_H_
+
+#define SOFT_AVC_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tagAVCHandle;
+
+namespace android {
+
+struct SoftAVC : public SimpleSoftOMXComponent {
+ SoftAVC(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAVC();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, 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 {
+ kNumInputBuffers = 4,
+ kNumOutputBuffers = 18,
+ };
+
+ enum EOSStatus {
+ INPUT_DATA_AVAILABLE,
+ INPUT_EOS_SEEN,
+ OUTPUT_FRAMES_FLUSHED,
+ };
+
+ tagAVCHandle *mHandle;
+
+ size_t mInputBufferCount;
+
+ int32_t mWidth, mHeight;
+ int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+
+ bool mSPSSeen, mPPSSeen;
+
+ int64_t mCurrentTimeUs;
+
+ EOSStatus mEOSStatus;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+
+ status_t decodeFragment(
+ const uint8_t *fragPtr, size_t fragSize,
+ bool *releaseFrames,
+ OMX_BUFFERHEADERTYPE **outHeader);
+
+ void updatePortDefinitions();
+ bool drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader);
+
+ static int32_t ActivateSPSWrapper(
+ void *userData, unsigned int sizeInMbs, unsigned int numBuffers);
+
+ static int32_t BindFrameWrapper(
+ void *userData, int32_t index, uint8_t **yuv);
+
+ static void UnbindFrame(void *userData, int32_t index);
+
+ int32_t activateSPS(
+ unsigned int sizeInMbs, unsigned int numBuffers);
+
+ int32_t bindFrame(int32_t index, uint8_t **yuv);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
+};
+
+} // namespace android
+
+#endif // SOFT_AVC_H_
+
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
index cfb9fe4..b6953bc 100644
--- a/media/libstagefright/codecs/g711/dec/Android.mk
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -10,3 +10,22 @@ LOCAL_C_INCLUDES := \
LOCAL_MODULE := libstagefright_g711dec
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftG711.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_g711dec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
new file mode 100644
index 0000000..15e2c26
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
@@ -0,0 +1,302 @@
+/*
+ * 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 "SoftG711"
+#include <utils/Log.h>
+
+#include "SoftG711.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+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;
+}
+
+SoftG711::SoftG711(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mIsMLaw(true),
+ mNumChannels(1),
+ mSignalledError(false) {
+ if (!strcmp(name, "OMX.google.g711.alaw.decoder")) {
+ mIsMLaw = false;
+ } else {
+ CHECK(!strcmp(name, "OMX.google.g711.mlaw.decoder"));
+ }
+
+ initPorts();
+}
+
+SoftG711::~SoftG711() {
+}
+
+void SoftG711::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 *>(
+ mIsMLaw
+ ? MEDIA_MIMETYPE_AUDIO_G711_MLAW
+ : MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingG711;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = kMaxNumSamplesPerFrame * 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);
+}
+
+OMX_ERRORTYPE SoftG711::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ 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;
+
+ pcmParams->nChannels = mNumChannels;
+ pcmParams->nSamplingRate = 8000;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftG711::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) {
+ return OMX_ErrorUndefined;
+ }
+
+ mNumChannels = pcmParams->nChannels;
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (mIsMLaw) {
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.g711mlaw",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ } else {
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.g711alaw",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+void SoftG711::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ 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;
+ }
+
+ if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) {
+ LOGE("input buffer too large (%ld).", inHeader->nFilledLen);
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ }
+
+ const uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset;
+
+ if (mIsMLaw) {
+ DecodeMLaw(
+ reinterpret_cast<int16_t *>(outHeader->pBuffer),
+ inputptr, inHeader->nFilledLen);
+ } else {
+ DecodeALaw(
+ reinterpret_cast<int16_t *>(outHeader->pBuffer),
+ inputptr, inHeader->nFilledLen);
+ }
+
+ outHeader->nTimeStamp = inHeader->nTimeStamp;
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = inHeader->nFilledLen * sizeof(int16_t);
+ outHeader->nFlags = 0;
+
+ 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;
+ }
+}
+
+// static
+void SoftG711::DecodeALaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t ix = x ^ 0x55;
+ ix &= 0x7f;
+
+ int32_t iexp = ix >> 4;
+ int32_t mant = ix & 0x0f;
+
+ if (iexp > 0) {
+ mant += 16;
+ }
+
+ mant = (mant << 4) + 8;
+
+ if (iexp > 1) {
+ mant = mant << (iexp - 1);
+ }
+
+ *out++ = (x > 127) ? mant : -mant;
+ }
+}
+
+// static
+void SoftG711::DecodeMLaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t mantissa = ~x;
+ int32_t exponent = (mantissa >> 4) & 7;
+ int32_t segment = exponent + 1;
+ mantissa &= 0x0f;
+
+ int32_t step = 4 << segment;
+
+ int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
+
+ *out++ = (x < 0x80) ? -abs : abs;
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftG711(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.h b/media/libstagefright/codecs/g711/dec/SoftG711.h
new file mode 100644
index 0000000..bff0c68
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.h
@@ -0,0 +1,63 @@
+/*
+ * 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_G711_H_
+
+#define SOFT_G711_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftG711 : public SimpleSoftOMXComponent {
+ SoftG711(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftG711();
+
+ 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);
+
+private:
+ enum {
+ kNumBuffers = 4,
+ kMaxNumSamplesPerFrame = 16384,
+ };
+
+ bool mIsMLaw;
+ OMX_U32 mNumChannels;
+ bool mSignalledError;
+
+ void initPorts();
+
+ static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
+ static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftG711);
+};
+
+} // namespace android
+
+#endif // SOFT_G711_H_
+
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
index 2d9bcc6..65b642c 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
@@ -48,3 +48,29 @@ LOCAL_C_INCLUDES := \
LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF=
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftMPEG4.cpp
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_m4vh263dec
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_mpeg4dec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
new file mode 100644
index 0000000..13e1662
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -0,0 +1,528 @@
+/*
+ * 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 "SoftMPEG4"
+#include <utils/Log.h>
+
+#include "SoftMPEG4.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include "mp4dec_api.h"
+
+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;
+}
+
+SoftMPEG4::SoftMPEG4(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mMode(MODE_MPEG4),
+ mHandle(new tagvideoDecControls),
+ mInputBufferCount(0),
+ mWidth(352),
+ mHeight(288),
+ mCropLeft(0),
+ mCropTop(0),
+ mCropRight(mWidth - 1),
+ mCropBottom(mHeight - 1),
+ mSignalledError(false),
+ mInitialized(false),
+ mFramesConfigured(false),
+ mNumSamplesOutput(0),
+ mOutputPortSettingsChange(NONE) {
+ if (!strcmp(name, "OMX.google.h263.decoder")) {
+ mMode = MODE_H263;
+ } else {
+ CHECK(!strcmp(name, "OMX.google.mpeg4.decoder"));
+ }
+
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftMPEG4::~SoftMPEG4() {
+ if (mInitialized) {
+ PVCleanUpVideoDecoder(mHandle);
+ }
+
+ delete mHandle;
+ mHandle = NULL;
+}
+
+void SoftMPEG4::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumInputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.video.cMIMEType =
+ (mMode == MODE_MPEG4)
+ ? const_cast<char *>(MEDIA_MIMETYPE_VIDEO_MPEG4)
+ : const_cast<char *>(MEDIA_MIMETYPE_VIDEO_H263);
+
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+
+ def.format.video.eCompressionFormat =
+ mMode == MODE_MPEG4 ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263;
+
+ def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+ def.format.video.pNativeWindow = NULL;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumOutputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ def.format.video.pNativeWindow = NULL;
+
+ def.nBufferSize =
+ (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+ addPort(def);
+}
+
+status_t SoftMPEG4::initDecoder() {
+ memset(mHandle, 0, sizeof(tagvideoDecControls));
+ return OK;
+}
+
+OMX_ERRORTYPE SoftMPEG4::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ if (formatParams->nPortIndex == 0) {
+ formatParams->eCompressionFormat =
+ (mMode == MODE_MPEG4)
+ ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263;
+
+ formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+ formatParams->xFramerate = 0;
+ } else {
+ CHECK_EQ(formatParams->nPortIndex, 1u);
+
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ formatParams->xFramerate = 0;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftMPEG4::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (mMode == MODE_MPEG4) {
+ if (strncmp((const char *)roleParams->cRole,
+ "video_decoder.mpeg4",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ } else {
+ if (strncmp((const char *)roleParams->cRole,
+ "video_decoder.h263",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftMPEG4::getConfig(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexConfigCommonOutputCrop:
+ {
+ OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
+
+ if (rectParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ rectParams->nLeft = mCropLeft;
+ rectParams->nTop = mCropTop;
+ rectParams->nWidth = mCropRight - mCropLeft + 1;
+ rectParams->nHeight = mCropBottom - mCropTop + 1;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return OMX_ErrorUnsupportedIndex;
+ }
+}
+
+void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ PortInfo *port = editPortInfo(1);
+
+ OMX_BUFFERHEADERTYPE *outHeader =
+ port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ ++mInputBufferCount;
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ return;
+ }
+
+ uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset;
+
+ if (!mInitialized) {
+ uint8_t *vol_data[1];
+ int32_t vol_size = 0;
+
+ vol_data[0] = NULL;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ vol_data[0] = bitstream;
+ vol_size = inHeader->nFilledLen;
+ }
+
+ MP4DecodingMode mode =
+ (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE;
+
+ Bool success = PVInitVideoDecoder(
+ mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode);
+
+ if (!success) {
+ LOGW("PVInitVideoDecoder failed. Unsupported content?");
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle);
+ if (mode != actualMode) {
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ PVSetPostProcType((VideoDecControls *) mHandle, 0);
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+
+ mInitialized = true;
+
+ if (mode == MPEG4_MODE && portSettingsChanged()) {
+ return;
+ }
+
+ continue;
+ }
+
+ if (!mFramesConfigured) {
+ PortInfo *port = editPortInfo(1);
+ OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader;
+
+ PVSetReferenceYUV(mHandle, outHeader->pBuffer);
+
+ mFramesConfigured = true;
+ }
+
+ uint32_t timestamp = 0xFFFFFFFF;
+ int32_t bufferSize = inHeader->nFilledLen;
+
+ uint32_t useExtTimestamp = 0;
+ if (PVDecodeVideoFrame(
+ mHandle, &bitstream, &timestamp, &bufferSize,
+ &useExtTimestamp,
+ outHeader->pBuffer) != PV_TRUE) {
+ LOGE("failed to decode video frame.");
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ if (portSettingsChanged()) {
+ return;
+ }
+
+ outHeader->nTimeStamp = inHeader->nTimeStamp;
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+
+ ++mInputBufferCount;
+
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+ outHeader->nFlags = 0;
+
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+
+ ++mNumSamplesOutput;
+ }
+}
+
+bool SoftMPEG4::portSettingsChanged() {
+ int32_t disp_width, disp_height;
+ PVGetVideoDimensions(mHandle, &disp_width, &disp_height);
+
+ int32_t buf_width, buf_height;
+ PVGetBufferDimensions(mHandle, &buf_width, &buf_height);
+
+ CHECK_LE(disp_width, buf_width);
+ CHECK_LE(disp_height, buf_height);
+
+ LOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d",
+ disp_width, disp_height, buf_width, buf_height);
+
+ if (mCropRight != disp_width - 1
+ || mCropBottom != disp_height - 1) {
+ mCropLeft = 0;
+ mCropTop = 0;
+ mCropRight = disp_width - 1;
+ mCropBottom = disp_height - 1;
+
+ notify(OMX_EventPortSettingsChanged,
+ 1,
+ OMX_IndexConfigCommonOutputCrop,
+ NULL);
+ }
+
+ if (buf_width != mWidth || buf_height != mHeight) {
+ mWidth = buf_width;
+ mHeight = buf_height;
+
+ updatePortDefinitions();
+
+ if (mMode == MODE_H263) {
+ PVCleanUpVideoDecoder(mHandle);
+
+ uint8_t *vol_data[1];
+ int32_t vol_size = 0;
+
+ vol_data[0] = NULL;
+ if (!PVInitVideoDecoder(
+ mHandle, vol_data, &vol_size, 1, mWidth, mHeight,
+ H263_MODE)) {
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return true;
+ }
+ }
+
+ mFramesConfigured = false;
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return true;
+ }
+
+ return false;
+}
+
+void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0 && mInitialized) {
+ CHECK_EQ((int)PVResetVideoDecoder(mHandle), (int)PV_TRUE);
+ }
+}
+
+void SoftMPEG4::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;
+ }
+ }
+}
+
+void SoftMPEG4::updatePortDefinitions() {
+ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def = &editPortInfo(1)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def->nBufferSize =
+ (((def->format.video.nFrameWidth + 15) & -16)
+ * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2;
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftMPEG4(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
new file mode 100644
index 0000000..dff08a7
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
@@ -0,0 +1,92 @@
+/*
+ * 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_MPEG4_H_
+
+#define SOFT_MPEG4_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tagvideoDecControls;
+
+namespace android {
+
+struct SoftMPEG4 : public SimpleSoftOMXComponent {
+ SoftMPEG4(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftMPEG4();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, 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 {
+ kNumInputBuffers = 4,
+ kNumOutputBuffers = 2,
+ };
+
+ enum {
+ MODE_MPEG4,
+ MODE_H263,
+
+ } mMode;
+
+ tagvideoDecControls *mHandle;
+
+ size_t mInputBufferCount;
+
+ int32_t mWidth, mHeight;
+ int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+
+ bool mSignalledError;
+ bool mInitialized;
+ bool mFramesConfigured;
+
+ int32_t mNumSamplesOutput;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+
+ void updatePortDefinitions();
+ bool portSettingsChanged();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4);
+};
+
+} // namespace android
+
+#endif // SOFT_MPEG4_H_
+
+
diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk
index 753500e..2d35183 100644
--- a/media/libstagefright/codecs/mp3dec/Android.mk
+++ b/media/libstagefright/codecs/mp3dec/Android.mk
@@ -57,3 +57,26 @@ LOCAL_ARM_MODE := arm
include $(BUILD_STATIC_LIBRARY)
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftMP3.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_mp3dec
+
+LOCAL_MODULE := libstagefright_soft_mp3dec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
new file mode 100644
index 0000000..f6770b0
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
@@ -0,0 +1,325 @@
+/*
+ * 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 "SoftMP3"
+#include <utils/Log.h>
+
+#include "SoftMP3.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+#include "include/pvmp3decoder_api.h"
+
+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;
+}
+
+SoftMP3::SoftMP3(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mConfig(new tPVMP3DecoderExternal),
+ mDecoderBuf(NULL),
+ mAnchorTimeUs(0),
+ mNumFramesOutput(0),
+ mNumChannels(2),
+ mSamplingRate(44100),
+ mSignalledError(false),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ initDecoder();
+}
+
+SoftMP3::~SoftMP3() {
+ if (mDecoderBuf != NULL) {
+ free(mDecoderBuf);
+ mDecoderBuf = NULL;
+ }
+
+ delete mConfig;
+ mConfig = NULL;
+}
+
+void SoftMP3::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_MPEG);
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingMP3;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = kOutputBufferSize;
+ 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);
+}
+
+void SoftMP3::initDecoder() {
+ mConfig->equalizerType = flat;
+ mConfig->crcEnabled = false;
+
+ uint32_t memRequirements = pvmp3_decoderMemRequirements();
+ mDecoderBuf = malloc(memRequirements);
+
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+}
+
+OMX_ERRORTYPE SoftMP3::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ 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;
+
+ pcmParams->nChannels = mNumChannels;
+ pcmParams->nSamplingRate = mSamplingRate;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftMP3::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.mp3",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+void SoftMP3::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ 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;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumFramesOutput = 0;
+ }
+
+ mConfig->pInputBuffer =
+ inHeader->pBuffer + inHeader->nOffset;
+
+ mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->inputBufferUsedLength = 0;
+
+ mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t);
+
+ mConfig->pOutputBuffer =
+ reinterpret_cast<int16_t *>(outHeader->pBuffer);
+
+ ERROR_CODE decoderErr;
+ if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+ != NO_DECODING_ERROR) {
+ LOGV("mp3 decoder returned error %d", decoderErr);
+
+ if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR ||
+ mConfig->outputFrameSize == 0) {
+
+ if (mConfig->outputFrameSize == 0) {
+ LOGE("Output frame size is 0");
+ }
+
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ // This is recoverable, just ignore the current frame and
+ // play silence instead.
+ memset(outHeader->pBuffer,
+ 0,
+ mConfig->outputFrameSize * sizeof(int16_t));
+
+ mConfig->inputBufferUsedLength = inHeader->nFilledLen;
+ } else if (mConfig->samplingRate != mSamplingRate
+ || mConfig->num_channels != mNumChannels) {
+ mSamplingRate = mConfig->samplingRate;
+ mNumChannels = mConfig->num_channels;
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t);
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate;
+
+ outHeader->nFlags = 0;
+
+ CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength);
+
+ inHeader->nOffset += mConfig->inputBufferUsedLength;
+ inHeader->nFilledLen -= mConfig->inputBufferUsedLength;
+
+ mNumFramesOutput += mConfig->outputFrameSize / mNumChannels;
+
+ if (inHeader->nFilledLen == 0) {
+ 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;
+ }
+}
+
+void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0) {
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+ }
+}
+
+void SoftMP3::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::SoftMP3(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h
new file mode 100644
index 0000000..70d0682
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h
@@ -0,0 +1,80 @@
+/*
+ * 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_MP3_H_
+
+#define SOFT_MP3_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tPVMP3DecoderExternal;
+
+namespace android {
+
+struct SoftMP3 : public SimpleSoftOMXComponent {
+ SoftMP3(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftMP3();
+
+ 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,
+ kOutputBufferSize = 4608 * 2
+ };
+
+ tPVMP3DecoderExternal *mConfig;
+ void *mDecoderBuf;
+ int64_t mAnchorTimeUs;
+ int64_t mNumFramesOutput;
+
+ int32_t mNumChannels;
+ int32_t mSamplingRate;
+
+ bool mConfigured;
+
+ bool mSignalledError;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ void initDecoder();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftMP3);
+};
+
+} // namespace android
+
+#endif // SOFT_MP3_H_
+
+
diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk
index b769f0d..1b3088f 100644
--- a/media/libstagefright/codecs/on2/dec/Android.mk
+++ b/media/libstagefright/codecs/on2/dec/Android.mk
@@ -2,15 +2,42 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- VPXDecoder.cpp
+ VPXDecoder.cpp \
LOCAL_MODULE := libstagefright_vpxdec
LOCAL_C_INCLUDES := \
$(TOP)/frameworks/base/media/libstagefright/include \
- $(TOP)/frameworks/base/include/media/stagefright/openmax \
+ frameworks/base/include/media/stagefright/openmax \
$(TOP)/external/libvpx \
$(TOP)/external/libvpx/vpx_codec \
$(TOP)/external/libvpx/vpx_ports
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftVPX.cpp
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/external/libvpx \
+ $(TOP)/external/libvpx/vpx_codec \
+ $(TOP)/external/libvpx/vpx_ports \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_vpxdec \
+ libvpx
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_vpxdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
new file mode 100644
index 0000000..e9ce719
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -0,0 +1,366 @@
+/*
+ * 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 "SoftVPX"
+#include <utils/Log.h>
+
+#include "SoftVPX.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+#include "vpx/vpx_decoder.h"
+#include "vpx/vpx_codec.h"
+#include "vpx/vp8dx.h"
+
+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;
+}
+
+SoftVPX::SoftVPX(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mCtx(NULL),
+ mWidth(320),
+ mHeight(240),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftVPX::~SoftVPX() {
+ vpx_codec_destroy((vpx_codec_ctx_t *)mCtx);
+ delete (vpx_codec_ctx_t *)mCtx;
+ mCtx = NULL;
+}
+
+void SoftVPX::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_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VPX);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX;
+ def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+ def.format.video.pNativeWindow = NULL;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ def.format.video.pNativeWindow = NULL;
+
+ def.nBufferSize =
+ (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+ addPort(def);
+}
+
+status_t SoftVPX::initDecoder() {
+ mCtx = new vpx_codec_ctx_t;
+ vpx_codec_err_t vpx_err;
+ if ((vpx_err = vpx_codec_dec_init(
+ (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) {
+ LOGE("on2 decoder failed to initialize. (%d)", vpx_err);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftVPX::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ if (formatParams->nPortIndex == 0) {
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX;
+ formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+ formatParams->xFramerate = 0;
+ } else {
+ CHECK_EQ(formatParams->nPortIndex, 1u);
+
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ formatParams->xFramerate = 0;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftVPX::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,
+ "video_decoder.vpx",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+void SoftVPX::onQueueFilled(OMX_U32 portIndex) {
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ 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;
+ }
+
+ if (vpx_codec_decode(
+ (vpx_codec_ctx_t *)mCtx,
+ inHeader->pBuffer + inHeader->nOffset,
+ inHeader->nFilledLen,
+ NULL,
+ 0)) {
+ LOGE("on2 decoder failed to decode frame.");
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ vpx_codec_iter_t iter = NULL;
+ vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);
+
+ if (img != NULL) {
+ CHECK_EQ(img->fmt, IMG_FMT_I420);
+
+ int32_t width = img->d_w;
+ int32_t height = img->d_h;
+
+ if (width != mWidth || height != mHeight) {
+ mWidth = width;
+ mHeight = height;
+
+ updatePortDefinitions();
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = (width * height * 3) / 2;
+ outHeader->nFlags = 0;
+ outHeader->nTimeStamp = inHeader->nTimeStamp;
+
+ const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y];
+ uint8_t *dst = outHeader->pBuffer;
+ for (size_t i = 0; i < img->d_h; ++i) {
+ memcpy(dst, srcLine, img->d_w);
+
+ srcLine += img->stride[PLANE_Y];
+ dst += img->d_w;
+ }
+
+ srcLine = (const uint8_t *)img->planes[PLANE_U];
+ for (size_t i = 0; i < img->d_h / 2; ++i) {
+ memcpy(dst, srcLine, img->d_w / 2);
+
+ srcLine += img->stride[PLANE_U];
+ dst += img->d_w / 2;
+ }
+
+ srcLine = (const uint8_t *)img->planes[PLANE_V];
+ for (size_t i = 0; i < img->d_h / 2; ++i) {
+ memcpy(dst, srcLine, img->d_w / 2);
+
+ srcLine += img->stride[PLANE_V];
+ dst += img->d_w / 2;
+ }
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ }
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+}
+
+void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) {
+}
+
+void SoftVPX::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;
+ }
+ }
+}
+
+void SoftVPX::updatePortDefinitions() {
+ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def = &editPortInfo(1)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def->nBufferSize =
+ (def->format.video.nFrameWidth
+ * def->format.video.nFrameHeight * 3) / 2;
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftVPX(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h
new file mode 100644
index 0000000..3e814a2
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h
@@ -0,0 +1,70 @@
+/*
+ * 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_VPX_H_
+
+#define SOFT_VPX_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftVPX : public SimpleSoftOMXComponent {
+ SoftVPX(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftVPX();
+
+ 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
+ };
+
+ void *mCtx;
+
+ int32_t mWidth;
+ int32_t mHeight;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+
+ void updatePortDefinitions();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftVPX);
+};
+
+} // namespace android
+
+#endif // SOFT_VPX_H_
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_
+