diff options
Diffstat (limited to 'media/libstagefright/codecs/m4v_h263/dec')
-rw-r--r-- | media/libstagefright/codecs/m4v_h263/dec/Android.mk | 26 | ||||
-rw-r--r-- | media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp | 528 | ||||
-rw-r--r-- | media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h | 92 |
3 files changed, 646 insertions, 0 deletions
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk index 2d9bcc6..f1bec08 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 := optional + +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, ×tamp, &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_ + + |