diff options
author | James Dong <jdong@google.com> | 2012-04-23 21:04:49 -0700 |
---|---|---|
committer | James Dong <jdong@google.com> | 2012-04-25 18:08:24 -0700 |
commit | 1700744ae0ce2fcf722816453ae0af2cd5646458 (patch) | |
tree | 54b8461c1e0be8588c317a249e351bdb6d6c1a87 /media | |
parent | e370bb62b89d2f2980f519392010ea08a24c558e (diff) | |
download | frameworks_av-1700744ae0ce2fcf722816453ae0af2cd5646458.zip frameworks_av-1700744ae0ce2fcf722816453ae0af2cd5646458.tar.gz frameworks_av-1700744ae0ce2fcf722816453ae0af2cd5646458.tar.bz2 |
Software MPEG4/H263 video encoder is now OMX-based
o related-to-bug: 6401068
Change-Id: If8eccea060f38e42ad31eb6e91aaa832e67c5559
Diffstat (limited to 'media')
4 files changed, 833 insertions, 0 deletions
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk index 7cbb38f..e6aa563 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk +++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ M4vH263Encoder.cpp \ + SoftMPEG4Encoder.cpp \ src/bitstream_io.cpp \ src/combined_encode.cpp \ src/datapart_encode.cpp \ @@ -35,3 +36,39 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/native/include/media/openmax include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftMPEG4Encoder.cpp + +LOCAL_C_INCLUDES := \ + frameworks/av/media/libstagefright/include \ + frameworks/native/include/media/openmax \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../common/include \ + $(LOCAL_PATH)/../common + +LOCAL_CFLAGS := \ + -DBX_RC \ + -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF= + + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_m4vh263enc + +LOCAL_SHARED_LIBRARIES := \ + libstagefright \ + libstagefright_enc_common \ + libstagefright_foundation \ + libstagefright_omx \ + libutils \ + + +LOCAL_MODULE := libstagefright_soft_mpeg4enc +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp new file mode 100644 index 0000000..a5a2332 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp @@ -0,0 +1,706 @@ +/* + * Copyright (C) 2012 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 "SoftMPEG4Encoder" +#include <utils/Log.h> + +#include "mp4enc_api.h" +#include "OMX_Video.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> + +#include "SoftMPEG4Encoder.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; +} + +inline static void ConvertYUV420SemiPlanarToYUV420Planar( + uint8_t *inyuv, uint8_t* outyuv, + int32_t width, int32_t height) { + + int32_t outYsize = width * height; + uint32_t *outy = (uint32_t *) outyuv; + uint16_t *outcb = (uint16_t *) (outyuv + outYsize); + uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2)); + + /* Y copying */ + memcpy(outy, inyuv, outYsize); + + /* U & V copying */ + uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize); + for (int32_t i = height >> 1; i > 0; --i) { + for (int32_t j = width >> 2; j > 0; --j) { + uint32_t temp = *inyuv_4++; + uint32_t tempU = temp & 0xFF; + tempU = tempU | ((temp >> 8) & 0xFF00); + + uint32_t tempV = (temp >> 8) & 0xFF; + tempV = tempV | ((temp >> 16) & 0xFF00); + + // Flip U and V + *outcb++ = tempV; + *outcr++ = tempU; + } + } +} + +SoftMPEG4Encoder::SoftMPEG4Encoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mEncodeMode(COMBINE_MODE_WITH_ERR_RES), + mVideoWidth(176), + mVideoHeight(144), + mVideoFrameRate(30), + mVideoBitRate(192000), + mVideoColorFormat(OMX_COLOR_FormatYUV420Planar), + mIDRFrameRefreshIntervalInSec(1), + mNumInputFrames(-1), + mStarted(false), + mSawInputEOS(false), + mSignalledError(false), + mHandle(new tagvideoEncControls), + mEncParams(new tagvideoEncOptions), + mInputFrameData(NULL) { + + if (!strcmp(name, "OMX.google.h263.encoder")) { + mEncodeMode = H263_MODE; + } else { + CHECK(!strcmp(name, "OMX.google.mpeg4.encoder")); + } + + initPorts(); + ALOGI("Construct SoftMPEG4Encoder"); +} + +SoftMPEG4Encoder::~SoftMPEG4Encoder() { + ALOGV("Destruct SoftMPEG4Encoder"); + releaseEncoder(); + List<BufferInfo *> &outQueue = getPortQueue(1); + List<BufferInfo *> &inQueue = getPortQueue(0); + CHECK(outQueue.empty()); + CHECK(inQueue.empty()); +} + +OMX_ERRORTYPE SoftMPEG4Encoder::initEncParams() { + CHECK(mHandle != NULL); + memset(mHandle, 0, sizeof(tagvideoEncControls)); + + CHECK(mEncParams != NULL); + memset(mEncParams, 0, sizeof(tagvideoEncOptions)); + if (!PVGetDefaultEncOption(mEncParams, 0)) { + ALOGE("Failed to get default encoding parameters"); + return OMX_ErrorUndefined; + } + mEncParams->encMode = mEncodeMode; + mEncParams->encWidth[0] = mVideoWidth; + mEncParams->encHeight[0] = mVideoHeight; + mEncParams->encFrameRate[0] = mVideoFrameRate; + mEncParams->rcType = VBR_1; + mEncParams->vbvDelay = 5.0f; + + // FIXME: + // Add more profile and level support for MPEG4 encoder + mEncParams->profile_level = CORE_PROFILE_LEVEL2; + mEncParams->packetSize = 32; + mEncParams->rvlcEnable = PV_OFF; + mEncParams->numLayers = 1; + mEncParams->timeIncRes = 1000; + mEncParams->tickPerSrc = mEncParams->timeIncRes / mVideoFrameRate; + + mEncParams->bitRate[0] = mVideoBitRate; + mEncParams->iQuant[0] = 15; + mEncParams->pQuant[0] = 12; + mEncParams->quantType[0] = 0; + mEncParams->noFrameSkipped = PV_OFF; + + if (mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { + // Color conversion is needed. + CHECK(mInputFrameData == NULL); + mInputFrameData = + (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1); + CHECK(mInputFrameData != NULL); + } + + // PV's MPEG4 encoder requires the video dimension of multiple + if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) { + ALOGE("Video frame size %dx%d must be a multiple of 16", + mVideoWidth, mVideoHeight); + return OMX_ErrorBadParameter; + } + + // Set IDR frame refresh interval + if (mIDRFrameRefreshIntervalInSec < 0) { + mEncParams->intraPeriod = -1; + } else if (mIDRFrameRefreshIntervalInSec == 0) { + mEncParams->intraPeriod = 1; // All I frames + } else { + mEncParams->intraPeriod = + (mIDRFrameRefreshIntervalInSec * mVideoFrameRate); + } + + mEncParams->numIntraMB = 0; + mEncParams->sceneDetect = PV_ON; + mEncParams->searchRange = 16; + mEncParams->mv8x8Enable = PV_OFF; + mEncParams->gobHeaderInterval = 0; + mEncParams->useACPred = PV_ON; + mEncParams->intraDCVlcTh = 0; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftMPEG4Encoder::initEncoder() { + CHECK(!mStarted); + + OMX_ERRORTYPE errType = OMX_ErrorNone; + if (OMX_ErrorNone != (errType = initEncParams())) { + ALOGE("Failed to initialized encoder params"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return errType; + } + + if (!PVInitVideoEncoder(mHandle, mEncParams)) { + ALOGE("Failed to initialize the encoder"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return OMX_ErrorUndefined; + } + + mNumInputFrames = -1; // 1st buffer for codec specific data + mStarted = true; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftMPEG4Encoder::releaseEncoder() { + if (!mStarted) { + return OMX_ErrorNone; + } + + PVCleanUpVideoEncoder(mHandle); + + delete mInputFrameData; + mInputFrameData = NULL; + + delete mEncParams; + mEncParams = NULL; + + delete mHandle; + mHandle = NULL; + + mStarted = false; + + return OMX_ErrorNone; +} + +void SoftMPEG4Encoder::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + const size_t kInputBufferSize = (mVideoWidth * mVideoHeight * 3) >> 1; + + // 256 * 1024 is a magic number for PV's encoder, not sure why + const size_t kOutputBufferSize = + (kInputBufferSize > 256 * 1024) + ? kInputBufferSize: 256 * 1024; + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kInputBufferSize; + 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 *>("video/raw"); + + def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; + def.format.video.xFramerate = (mVideoFrameRate << 16); // Q16 format + def.format.video.nBitrate = mVideoBitRate; + def.format.video.nFrameWidth = mVideoWidth; + def.format.video.nFrameHeight = mVideoHeight; + def.format.video.nStride = mVideoWidth; + def.format.video.nSliceHeight = mVideoHeight; + + 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_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.video.cMIMEType = + (mEncodeMode == COMBINE_MODE_WITH_ERR_RES) + ? const_cast<char *>(MEDIA_MIMETYPE_VIDEO_MPEG4) + : const_cast<char *>(MEDIA_MIMETYPE_VIDEO_H263); + + def.format.video.eCompressionFormat = + (mEncodeMode == COMBINE_MODE_WITH_ERR_RES) + ? OMX_VIDEO_CodingMPEG4 + : OMX_VIDEO_CodingH263; + + def.format.video.eColorFormat = OMX_COLOR_FormatUnused; + def.format.video.xFramerate = (0 << 16); // Q16 format + def.format.video.nBitrate = mVideoBitRate; + def.format.video.nFrameWidth = mVideoWidth; + def.format.video.nFrameHeight = mVideoHeight; + def.format.video.nStride = mVideoWidth; + def.format.video.nSliceHeight = mVideoHeight; + + addPort(def); +} + +OMX_ERRORTYPE SoftMPEG4Encoder::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamVideoErrorCorrection: + { + return OMX_ErrorNotImplemented; + } + + case OMX_IndexParamVideoBitrate: + { + OMX_VIDEO_PARAM_BITRATETYPE *bitRate = + (OMX_VIDEO_PARAM_BITRATETYPE *) params; + + if (bitRate->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + bitRate->eControlRate = OMX_Video_ControlRateVariable; + bitRate->nTargetBitrate = mVideoBitRate; + 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 > 1) { + return OMX_ErrorNoMore; + } + + if (formatParams->nPortIndex == 0) { + formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; + if (formatParams->nIndex == 0) { + formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; + } else { + formatParams->eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar; + } + } else { + formatParams->eCompressionFormat = + (mEncodeMode == COMBINE_MODE_WITH_ERR_RES) + ? OMX_VIDEO_CodingMPEG4 + : OMX_VIDEO_CodingH263; + + formatParams->eColorFormat = OMX_COLOR_FormatUnused; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoH263: + { + OMX_VIDEO_PARAM_H263TYPE *h263type = + (OMX_VIDEO_PARAM_H263TYPE *)params; + + if (h263type->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + h263type->nAllowedPictureTypes = + (OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP); + h263type->eProfile = OMX_VIDEO_H263ProfileBaseline; + h263type->eLevel = OMX_VIDEO_H263Level45; + h263type->bPLUSPTYPEAllowed = OMX_FALSE; + h263type->bForceRoundingTypeToZero = OMX_FALSE; + h263type->nPictureHeaderRepetition = 0; + h263type->nGOBHeaderInterval = 0; + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoMpeg4: + { + OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type = + (OMX_VIDEO_PARAM_MPEG4TYPE *)params; + + if (mpeg4type->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + mpeg4type->eProfile = OMX_VIDEO_MPEG4ProfileCore; + mpeg4type->eLevel = OMX_VIDEO_MPEG4Level2; + mpeg4type->nAllowedPictureTypes = + (OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP); + mpeg4type->nBFrames = 0; + mpeg4type->nIDCVLCThreshold = 0; + mpeg4type->bACPred = OMX_TRUE; + mpeg4type->nMaxPacketSize = 256; + mpeg4type->nTimeIncRes = 1000; + mpeg4type->nHeaderExtension = 0; + mpeg4type->bReversibleVLC = OMX_FALSE; + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoProfileLevelQuerySupported: + { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)params; + + if (profileLevel->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (profileLevel->nProfileIndex > 0) { + return OMX_ErrorNoMore; + } + + if (mEncodeMode == H263_MODE) { + profileLevel->eProfile = OMX_VIDEO_H263ProfileBaseline; + profileLevel->eLevel = OMX_VIDEO_H263Level45; + } else { + profileLevel->eProfile = OMX_VIDEO_MPEG4ProfileCore; + profileLevel->eLevel = OMX_VIDEO_MPEG4Level2; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamVideoErrorCorrection: + { + return OMX_ErrorNotImplemented; + } + + case OMX_IndexParamVideoBitrate: + { + OMX_VIDEO_PARAM_BITRATETYPE *bitRate = + (OMX_VIDEO_PARAM_BITRATETYPE *) params; + + if (bitRate->nPortIndex != 1 || + bitRate->eControlRate != OMX_Video_ControlRateVariable) { + return OMX_ErrorUndefined; + } + + mVideoBitRate = bitRate->nTargetBitrate; + return OMX_ErrorNone; + } + + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *def = + (OMX_PARAM_PORTDEFINITIONTYPE *)params; + if (def->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (def->nPortIndex == 0) { + if (def->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused || + (def->format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar && + def->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar)) { + return OMX_ErrorUndefined; + } + } else { + if ((mEncodeMode == COMBINE_MODE_WITH_ERR_RES && + def->format.video.eCompressionFormat != OMX_VIDEO_CodingMPEG4) || + (mEncodeMode == H263_MODE && + def->format.video.eCompressionFormat != OMX_VIDEO_CodingH263) || + (def->format.video.eColorFormat != OMX_COLOR_FormatUnused)) { + return OMX_ErrorUndefined; + } + } + + OMX_ERRORTYPE err = SimpleSoftOMXComponent::internalSetParameter(index, params); + if (OMX_ErrorNone != err) { + return err; + } + + if (def->nPortIndex == 0) { + mVideoWidth = def->format.video.nFrameWidth; + mVideoHeight = def->format.video.nFrameHeight; + mVideoFrameRate = def->format.video.xFramerate >> 16; + mVideoColorFormat = def->format.video.eColorFormat; + } else { + mVideoBitRate = def->format.video.nBitrate; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + (mEncodeMode == H263_MODE) + ? "video_encoder.h263": "video_encoder.mpeg4", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoPortFormat: + { + const OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 1) { + return OMX_ErrorNoMore; + } + + if (formatParams->nPortIndex == 0) { + if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused || + ((formatParams->nIndex == 0 && + formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) || + (formatParams->nIndex == 1 && + formatParams->eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar))) { + return OMX_ErrorUndefined; + } + mVideoColorFormat = formatParams->eColorFormat; + } else { + if ((mEncodeMode == H263_MODE && + formatParams->eCompressionFormat != OMX_VIDEO_CodingH263) || + (mEncodeMode == COMBINE_MODE_WITH_ERR_RES && + formatParams->eCompressionFormat != OMX_VIDEO_CodingMPEG4) || + formatParams->eColorFormat != OMX_COLOR_FormatUnused) { + return OMX_ErrorUndefined; + } + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoH263: + { + OMX_VIDEO_PARAM_H263TYPE *h263type = + (OMX_VIDEO_PARAM_H263TYPE *)params; + + if (h263type->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (h263type->eProfile != OMX_VIDEO_H263ProfileBaseline || + h263type->eLevel != OMX_VIDEO_H263Level45 || + (h263type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) || + h263type->bPLUSPTYPEAllowed != OMX_FALSE || + h263type->bForceRoundingTypeToZero != OMX_FALSE || + h263type->nPictureHeaderRepetition != 0 || + h263type->nGOBHeaderInterval != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoMpeg4: + { + OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type = + (OMX_VIDEO_PARAM_MPEG4TYPE *)params; + + if (mpeg4type->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (mpeg4type->eProfile != OMX_VIDEO_MPEG4ProfileCore || + mpeg4type->eLevel != OMX_VIDEO_MPEG4Level2 || + (mpeg4type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) || + mpeg4type->nBFrames != 0 || + mpeg4type->nIDCVLCThreshold != 0 || + mpeg4type->bACPred != OMX_TRUE || + mpeg4type->nMaxPacketSize != 256 || + mpeg4type->nTimeIncRes != 1000 || + mpeg4type->nHeaderExtension != 0 || + mpeg4type->bReversibleVLC != OMX_FALSE) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftMPEG4Encoder::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError || mSawInputEOS) { + return; + } + + if (!mStarted) { + if (OMX_ErrorNone != initEncoder()) { + return; + } + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + while (!mSawInputEOS && !inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + outHeader->nTimeStamp = 0; + outHeader->nFlags = 0; + outHeader->nOffset = 0; + outHeader->nFilledLen = 0; + outHeader->nOffset = 0; + + uint8_t *outPtr = (uint8_t *) outHeader->pBuffer; + int32_t dataLength = outHeader->nAllocLen; + + if (mNumInputFrames < 0) { + if (!PVGetVolHeader(mHandle, outPtr, &dataLength, 0)) { + ALOGE("Failed to get VOL header"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return; + } + ALOGV("Output VOL header: %d bytes", dataLength); + ++mNumInputFrames; + outHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; + outHeader->nFilledLen = dataLength; + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + // Save the input buffer info so that it can be + // passed to an output buffer + InputBufferInfo info; + info.mTimeUs = inHeader->nTimeStamp; + info.mFlags = inHeader->nFlags; + mInputBufferInfoVec.push(info); + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEOS = true; + } + + if (inHeader->nFilledLen > 0) { + const void *inData = inHeader->pBuffer + inHeader->nOffset; + uint8_t *inputData = (uint8_t *) inData; + if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) { + ConvertYUV420SemiPlanarToYUV420Planar( + inputData, mInputFrameData, mVideoWidth, mVideoHeight); + inputData = mInputFrameData; + } + CHECK(inputData != NULL); + + VideoEncFrameIO vin, vout; + memset(&vin, 0, sizeof(vin)); + memset(&vout, 0, sizeof(vout)); + vin.height = ((mVideoHeight + 15) >> 4) << 4; + vin.pitch = ((mVideoWidth + 15) >> 4) << 4; + vin.timestamp = (inHeader->nTimeStamp + 500) / 1000; // in ms + vin.yChan = inputData; + vin.uChan = vin.yChan + vin.height * vin.pitch; + vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2); + + unsigned long modTimeMs = 0; + int32_t nLayer = 0; + MP4HintTrack hintTrack; + if (!PVEncodeVideoFrame(mHandle, &vin, &vout, + &modTimeMs, outPtr, &dataLength, &nLayer) || + !PVGetHintTrack(mHandle, &hintTrack)) { + ALOGE("Failed to encode frame or get hink track at frame %lld", + mNumInputFrames); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + } + CHECK(NULL == PVGetOverrunBuffer(mHandle)); + if (hintTrack.CodeType == 0) { // I-frame serves as sync frame + outHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; + } + + ++mNumInputFrames; + } else { + dataLength = 0; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outQueue.erase(outQueue.begin()); + CHECK(!mInputBufferInfoVec.empty()); + InputBufferInfo *inputBufInfo = mInputBufferInfoVec.begin(); + mInputBufferInfoVec.erase(mInputBufferInfoVec.begin()); + outHeader->nTimeStamp = inputBufInfo->mTimeUs; + outHeader->nFlags |= (inputBufInfo->mFlags | OMX_BUFFERFLAG_ENDOFFRAME); + outHeader->nFilledLen = dataLength; + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftMPEG4Encoder(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h new file mode 100644 index 0000000..3e90d54 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2012 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_ENCODER_H_ +#define SOFT_MPEG4_ENCODER_H_ + +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/foundation/ABase.h> +#include "SimpleSoftOMXComponent.h" +#include "mp4enc_api.h" + + +namespace android { + +struct MediaBuffer; + +struct SoftMPEG4Encoder : public SimpleSoftOMXComponent { + SoftMPEG4Encoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + // Override SimpleSoftOMXComponent methods + 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); + +protected: + virtual ~SoftMPEG4Encoder(); + +private: + enum { + kNumBuffers = 2, + }; + + // OMX input buffer's timestamp and flags + typedef struct { + int64_t mTimeUs; + int32_t mFlags; + } InputBufferInfo; + + MP4EncodingMode mEncodeMode; + int32_t mVideoWidth; + int32_t mVideoHeight; + int32_t mVideoFrameRate; + int32_t mVideoBitRate; + int32_t mVideoColorFormat; + int32_t mIDRFrameRefreshIntervalInSec; + + int64_t mNumInputFrames; + bool mStarted; + bool mSawInputEOS; + bool mSignalledError; + + tagvideoEncControls *mHandle; + tagvideoEncOptions *mEncParams; + uint8_t *mInputFrameData; + Vector<InputBufferInfo> mInputBufferInfoVec; + + void initPorts(); + OMX_ERRORTYPE initEncParams(); + OMX_ERRORTYPE initEncoder(); + OMX_ERRORTYPE releaseEncoder(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4Encoder); +}; + +} // namespace android + +#endif // SOFT_MPEG4_ENCODER_H_ diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 9b7bb5a..6e53095 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -45,7 +45,9 @@ static const struct { { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" }, { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" }, { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" }, + { "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" }, { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" }, + { "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" }, { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" }, { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" }, { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" }, |