| /* |
| * Copyright 2014 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. |
| */ |
| |
| #include <inttypes.h> |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "SoftVideoEncoderOMXComponent" |
| #include <utils/Log.h> |
| #include <utils/misc.h> |
| |
| #include "include/SoftVideoEncoderOMXComponent.h" |
| |
| #include <media/hardware/HardwareAPI.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/ALooper.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| #include <media/stagefright/foundation/AUtils.h> |
| #include <media/stagefright/MediaDefs.h> |
| |
| #include <ui/GraphicBuffer.h> |
| #include <ui/GraphicBufferMapper.h> |
| |
| #include <OMX_IndexExt.h> |
| |
| namespace android { |
| |
| const static OMX_COLOR_FORMATTYPE kSupportedColorFormats[] = { |
| OMX_COLOR_FormatYUV420Planar, |
| OMX_COLOR_FormatYUV420SemiPlanar, |
| OMX_COLOR_FormatAndroidOpaque |
| }; |
| |
| 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; |
| } |
| |
| SoftVideoEncoderOMXComponent::SoftVideoEncoderOMXComponent( |
| const char *name, |
| const char *componentRole, |
| OMX_VIDEO_CODINGTYPE codingType, |
| const CodecProfileLevel *profileLevels, |
| size_t numProfileLevels, |
| int32_t width, |
| int32_t height, |
| const OMX_CALLBACKTYPE *callbacks, |
| OMX_PTR appData, |
| OMX_COMPONENTTYPE **component) |
| : SimpleSoftOMXComponent(name, callbacks, appData, component), |
| mInputDataIsMeta(false), |
| mWidth(width), |
| mHeight(height), |
| mBitrate(192000), |
| mFramerate(30 << 16), // Q16 format |
| mColorFormat(OMX_COLOR_FormatYUV420Planar), |
| mMinOutputBufferSize(384), // arbitrary, using one uncompressed macroblock |
| mMinCompressionRatio(1), // max output size is normally the input size |
| mComponentRole(componentRole), |
| mCodingType(codingType), |
| mProfileLevels(profileLevels), |
| mNumProfileLevels(numProfileLevels) { |
| } |
| |
| void SoftVideoEncoderOMXComponent::initPorts( |
| OMX_U32 numInputBuffers, OMX_U32 numOutputBuffers, OMX_U32 outputBufferSize, |
| const char *mime, OMX_U32 minCompressionRatio) { |
| OMX_PARAM_PORTDEFINITIONTYPE def; |
| |
| mMinOutputBufferSize = outputBufferSize; |
| mMinCompressionRatio = minCompressionRatio; |
| |
| InitOMXParams(&def); |
| |
| def.nPortIndex = kInputPortIndex; |
| def.eDir = OMX_DirInput; |
| def.nBufferCountMin = numInputBuffers; |
| def.nBufferCountActual = def.nBufferCountMin; |
| def.bEnabled = OMX_TRUE; |
| def.bPopulated = OMX_FALSE; |
| def.eDomain = OMX_PortDomainVideo; |
| def.bBuffersContiguous = OMX_FALSE; |
| 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; |
| // frameRate is in Q16 format. |
| def.format.video.xFramerate = mFramerate; |
| def.format.video.bFlagErrorConcealment = OMX_FALSE; |
| def.nBufferAlignment = kInputBufferAlignment; |
| def.format.video.cMIMEType = const_cast<char *>("video/raw"); |
| def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; |
| def.format.video.eColorFormat = mColorFormat; |
| def.format.video.pNativeWindow = NULL; |
| // buffersize set in updatePortParams |
| |
| addPort(def); |
| |
| InitOMXParams(&def); |
| |
| def.nPortIndex = kOutputPortIndex; |
| def.eDir = OMX_DirOutput; |
| def.nBufferCountMin = numOutputBuffers; |
| def.nBufferCountActual = def.nBufferCountMin; |
| def.bEnabled = OMX_TRUE; |
| def.bPopulated = OMX_FALSE; |
| def.eDomain = OMX_PortDomainVideo; |
| def.bBuffersContiguous = OMX_FALSE; |
| def.format.video.pNativeRender = NULL; |
| def.format.video.nFrameWidth = mWidth; |
| def.format.video.nFrameHeight = mHeight; |
| def.format.video.nStride = 0; |
| def.format.video.nSliceHeight = 0; |
| def.format.video.nBitrate = mBitrate; |
| def.format.video.xFramerate = 0 << 16; |
| def.format.video.bFlagErrorConcealment = OMX_FALSE; |
| def.nBufferAlignment = kOutputBufferAlignment; |
| def.format.video.cMIMEType = const_cast<char *>(mime); |
| def.format.video.eCompressionFormat = mCodingType; |
| def.format.video.eColorFormat = OMX_COLOR_FormatUnused; |
| def.format.video.pNativeWindow = NULL; |
| // buffersize set in updatePortParams |
| |
| addPort(def); |
| |
| updatePortParams(); |
| } |
| |
| void SoftVideoEncoderOMXComponent::updatePortParams() { |
| OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef; |
| inDef->format.video.nFrameWidth = mWidth; |
| inDef->format.video.nFrameHeight = mHeight; |
| inDef->format.video.nStride = inDef->format.video.nFrameWidth; |
| inDef->format.video.nSliceHeight = inDef->format.video.nFrameHeight; |
| inDef->format.video.xFramerate = mFramerate; |
| inDef->format.video.eColorFormat = mColorFormat; |
| uint32_t rawBufferSize = |
| inDef->format.video.nStride * inDef->format.video.nSliceHeight * 3 / 2; |
| if (inDef->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) { |
| inDef->nBufferSize = max(sizeof(VideoNativeMetadata), sizeof(VideoGrallocMetadata)); |
| } else { |
| inDef->nBufferSize = rawBufferSize; |
| } |
| |
| OMX_PARAM_PORTDEFINITIONTYPE *outDef = &editPortInfo(kOutputPortIndex)->mDef; |
| outDef->format.video.nFrameWidth = mWidth; |
| outDef->format.video.nFrameHeight = mHeight; |
| outDef->format.video.nBitrate = mBitrate; |
| |
| outDef->nBufferSize = max(mMinOutputBufferSize, rawBufferSize / mMinCompressionRatio); |
| } |
| |
| OMX_ERRORTYPE SoftVideoEncoderOMXComponent::internalSetPortParams( |
| const OMX_PARAM_PORTDEFINITIONTYPE *port) { |
| |
| if (!isValidOMXParam(port)) { |
| return OMX_ErrorBadParameter; |
| } |
| |
| if (port->nPortIndex == kInputPortIndex) { |
| mWidth = port->format.video.nFrameWidth; |
| mHeight = port->format.video.nFrameHeight; |
| |
| // xFramerate comes in Q16 format, in frames per second unit |
| mFramerate = port->format.video.xFramerate; |
| |
| if (port->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused |
| || (port->format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar |
| && port->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar |
| && port->format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque)) { |
| return OMX_ErrorUnsupportedSetting; |
| } |
| |
| mColorFormat = port->format.video.eColorFormat; |
| } else if (port->nPortIndex == kOutputPortIndex) { |
| if (port->format.video.eCompressionFormat != mCodingType |
| || port->format.video.eColorFormat != OMX_COLOR_FormatUnused) { |
| return OMX_ErrorUnsupportedSetting; |
| } |
| |
| mBitrate = port->format.video.nBitrate; |
| } else { |
| return OMX_ErrorBadPortIndex; |
| } |
| |
| updatePortParams(); |
| return OMX_ErrorNone; |
| } |
| |
| OMX_ERRORTYPE SoftVideoEncoderOMXComponent::internalSetParameter( |
| OMX_INDEXTYPE index, const OMX_PTR param) { |
| // can include extension index OMX_INDEXEXTTYPE |
| const int32_t indexFull = index; |
| |
| switch (indexFull) { |
| case OMX_IndexParamVideoErrorCorrection: |
| { |
| return OMX_ErrorNotImplemented; |
| } |
| |
| case OMX_IndexParamStandardComponentRole: |
| { |
| const OMX_PARAM_COMPONENTROLETYPE *roleParams = |
| (const OMX_PARAM_COMPONENTROLETYPE *)param; |
| |
| if (!isValidOMXParam(roleParams)) { |
| return OMX_ErrorBadParameter; |
| } |
| |
| if (strncmp((const char *)roleParams->cRole, |
| mComponentRole, |
| OMX_MAX_STRINGNAME_SIZE - 1)) { |
| return OMX_ErrorUnsupportedSetting; |
| } |
| |
| return OMX_ErrorNone; |
| } |
| |
| case OMX_IndexParamPortDefinition: |
| { |
| OMX_ERRORTYPE err = internalSetPortParams((const OMX_PARAM_PORTDEFINITIONTYPE *)param); |
| |
| if (err != OMX_ErrorNone) { |
| return err; |
| } |
| |
| return SimpleSoftOMXComponent::internalSetParameter(index, param); |
| } |
| |
| case OMX_IndexParamVideoPortFormat: |
| { |
| const OMX_VIDEO_PARAM_PORTFORMATTYPE* format = |
| (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param; |
| |
| if (!isValidOMXParam(format)) { |
| return OMX_ErrorBadParameter; |
| } |
| |
| if (format->nPortIndex == kInputPortIndex) { |
| if (format->eColorFormat == OMX_COLOR_FormatYUV420Planar || |
| format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || |
| format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) { |
| mColorFormat = format->eColorFormat; |
| |
| updatePortParams(); |
| return OMX_ErrorNone; |
| } else { |
| ALOGE("Unsupported color format %i", format->eColorFormat); |
| return OMX_ErrorUnsupportedSetting; |
| } |
| } else if (format->nPortIndex == kOutputPortIndex) { |
| if (format->eCompressionFormat == mCodingType) { |
| return OMX_ErrorNone; |
| } else { |
| return OMX_ErrorUnsupportedSetting; |
| } |
| } else { |
| return OMX_ErrorBadPortIndex; |
| } |
| } |
| |
| case kStoreMetaDataExtensionIndex: |
| { |
| // storeMetaDataInBuffers |
| const StoreMetaDataInBuffersParams *storeParam = |
| (const StoreMetaDataInBuffersParams *)param; |
| |
| if (!isValidOMXParam(storeParam)) { |
| return OMX_ErrorBadParameter; |
| } |
| |
| if (storeParam->nPortIndex == kOutputPortIndex) { |
| return storeParam->bStoreMetaData ? OMX_ErrorUnsupportedSetting : OMX_ErrorNone; |
| } else if (storeParam->nPortIndex != kInputPortIndex) { |
| return OMX_ErrorBadPortIndex; |
| } |
| |
| mInputDataIsMeta = (storeParam->bStoreMetaData == OMX_TRUE); |
| if (mInputDataIsMeta) { |
| mColorFormat = OMX_COLOR_FormatAndroidOpaque; |
| } else if (mColorFormat == OMX_COLOR_FormatAndroidOpaque) { |
| mColorFormat = OMX_COLOR_FormatYUV420Planar; |
| } |
| updatePortParams(); |
| return OMX_ErrorNone; |
| } |
| |
| default: |
| return SimpleSoftOMXComponent::internalSetParameter(index, param); |
| } |
| } |
| |
| OMX_ERRORTYPE SoftVideoEncoderOMXComponent::internalGetParameter( |
| OMX_INDEXTYPE index, OMX_PTR param) { |
| switch ((int)index) { |
| case OMX_IndexParamVideoErrorCorrection: |
| { |
| return OMX_ErrorNotImplemented; |
| } |
| |
| case OMX_IndexParamVideoPortFormat: |
| { |
| OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = |
| (OMX_VIDEO_PARAM_PORTFORMATTYPE *)param; |
| |
| if (!isValidOMXParam(formatParams)) { |
| return OMX_ErrorBadParameter; |
| } |
| |
| if (formatParams->nPortIndex == kInputPortIndex) { |
| if (formatParams->nIndex >= NELEM(kSupportedColorFormats)) { |
| return OMX_ErrorNoMore; |
| } |
| |
| // Color formats, in order of preference |
| formatParams->eColorFormat = kSupportedColorFormats[formatParams->nIndex]; |
| formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; |
| formatParams->xFramerate = mFramerate; |
| return OMX_ErrorNone; |
| } else if (formatParams->nPortIndex == kOutputPortIndex) { |
| formatParams->eCompressionFormat = mCodingType; |
| formatParams->eColorFormat = OMX_COLOR_FormatUnused; |
| formatParams->xFramerate = 0; |
| return OMX_ErrorNone; |
| } else { |
| return OMX_ErrorBadPortIndex; |
| } |
| } |
| |
| case OMX_IndexParamVideoProfileLevelQuerySupported: |
| { |
| OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = |
| (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) param; |
| |
| if (!isValidOMXParam(profileLevel)) { |
| return OMX_ErrorBadParameter; |
| } |
| |
| if (profileLevel->nPortIndex != kOutputPortIndex) { |
| ALOGE("Invalid port index: %u", profileLevel->nPortIndex); |
| return OMX_ErrorUnsupportedIndex; |
| } |
| |
| if (profileLevel->nProfileIndex >= mNumProfileLevels) { |
| return OMX_ErrorNoMore; |
| } |
| |
| profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile; |
| profileLevel->eLevel = mProfileLevels[profileLevel->nProfileIndex].mLevel; |
| return OMX_ErrorNone; |
| } |
| |
| case OMX_IndexParamConsumerUsageBits: |
| { |
| OMX_U32 *usageBits = (OMX_U32 *)param; |
| *usageBits = GRALLOC_USAGE_SW_READ_OFTEN; |
| return OMX_ErrorNone; |
| } |
| |
| default: |
| return SimpleSoftOMXComponent::internalGetParameter(index, param); |
| } |
| } |
| |
| // static |
| __attribute__((no_sanitize("integer"))) |
| void SoftVideoEncoderOMXComponent::ConvertFlexYUVToPlanar( |
| uint8_t *dst, size_t dstStride, size_t dstVStride, |
| struct android_ycbcr *ycbcr, int32_t width, int32_t height) { |
| const uint8_t *src = (const uint8_t *)ycbcr->y; |
| const uint8_t *srcU = (const uint8_t *)ycbcr->cb; |
| const uint8_t *srcV = (const uint8_t *)ycbcr->cr; |
| uint8_t *dstU = dst + dstVStride * dstStride; |
| uint8_t *dstV = dstU + (dstVStride >> 1) * (dstStride >> 1); |
| |
| for (size_t y = height; y > 0; --y) { |
| memcpy(dst, src, width); |
| dst += dstStride; |
| src += ycbcr->ystride; |
| } |
| if (ycbcr->cstride == ycbcr->ystride >> 1 && ycbcr->chroma_step == 1) { |
| // planar |
| for (size_t y = height >> 1; y > 0; --y) { |
| memcpy(dstU, srcU, width >> 1); |
| dstU += dstStride >> 1; |
| srcU += ycbcr->cstride; |
| memcpy(dstV, srcV, width >> 1); |
| dstV += dstStride >> 1; |
| srcV += ycbcr->cstride; |
| } |
| } else { |
| // arbitrary |
| for (size_t y = height >> 1; y > 0; --y) { |
| for (size_t x = width >> 1; x > 0; --x) { |
| *dstU++ = *srcU; |
| *dstV++ = *srcV; |
| srcU += ycbcr->chroma_step; |
| srcV += ycbcr->chroma_step; |
| } |
| dstU += (dstStride >> 1) - (width >> 1); |
| dstV += (dstStride >> 1) - (width >> 1); |
| srcU += ycbcr->cstride - (width >> 1) * ycbcr->chroma_step; |
| srcV += ycbcr->cstride - (width >> 1) * ycbcr->chroma_step; |
| } |
| } |
| } |
| |
| // static |
| __attribute__((no_sanitize("integer"))) |
| void SoftVideoEncoderOMXComponent::ConvertYUV420SemiPlanarToYUV420Planar( |
| const uint8_t *inYVU, uint8_t* outYUV, int32_t width, int32_t height) { |
| // TODO: add support for stride |
| 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, inYVU, outYsize); |
| |
| /* U & V copying */ |
| // FIXME this only works if width is multiple of 4 |
| uint32_t *inYVU_4 = (uint32_t *) (inYVU + outYsize); |
| for (int32_t i = height >> 1; i > 0; --i) { |
| for (int32_t j = width >> 2; j > 0; --j) { |
| uint32_t temp = *inYVU_4++; |
| uint32_t tempU = temp & 0xFF; |
| tempU = tempU | ((temp >> 8) & 0xFF00); |
| |
| uint32_t tempV = (temp >> 8) & 0xFF; |
| tempV = tempV | ((temp >> 16) & 0xFF00); |
| |
| *outCb++ = tempU; |
| *outCr++ = tempV; |
| } |
| } |
| } |
| |
| // static |
| __attribute__((no_sanitize("integer"))) |
| void SoftVideoEncoderOMXComponent::ConvertRGB32ToPlanar( |
| uint8_t *dstY, size_t dstStride, size_t dstVStride, |
| const uint8_t *src, size_t width, size_t height, size_t srcStride, |
| bool bgr) { |
| CHECK((width & 1) == 0); |
| CHECK((height & 1) == 0); |
| |
| uint8_t *dstU = dstY + dstStride * dstVStride; |
| uint8_t *dstV = dstU + (dstStride >> 1) * (dstVStride >> 1); |
| |
| #ifdef SURFACE_IS_BGR32 |
| bgr = !bgr; |
| #endif |
| |
| const size_t redOffset = bgr ? 2 : 0; |
| const size_t greenOffset = 1; |
| const size_t blueOffset = bgr ? 0 : 2; |
| |
| for (size_t y = 0; y < height; ++y) { |
| for (size_t x = 0; x < width; ++x) { |
| unsigned red = src[redOffset]; |
| unsigned green = src[greenOffset]; |
| unsigned blue = src[blueOffset]; |
| |
| // using ITU-R BT.601 conversion matrix |
| unsigned luma = |
| ((red * 66 + green * 129 + blue * 25) >> 8) + 16; |
| |
| dstY[x] = luma; |
| |
| if ((x & 1) == 0 && (y & 1) == 0) { |
| unsigned U = |
| ((-red * 38 - green * 74 + blue * 112) >> 8) + 128; |
| |
| unsigned V = |
| ((red * 112 - green * 94 - blue * 18) >> 8) + 128; |
| |
| dstU[x >> 1] = U; |
| dstV[x >> 1] = V; |
| } |
| src += 4; |
| } |
| |
| if ((y & 1) == 0) { |
| dstU += dstStride >> 1; |
| dstV += dstStride >> 1; |
| } |
| |
| src += srcStride - 4 * width; |
| dstY += dstStride; |
| } |
| } |
| |
| const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer( |
| uint8_t *dst, size_t dstSize, |
| const uint8_t *src, size_t srcSize, |
| size_t width, size_t height) const { |
| size_t dstStride = width; |
| size_t dstVStride = height; |
| |
| MetadataBufferType bufferType = *(MetadataBufferType *)src; |
| bool usingANWBuffer = bufferType == kMetadataBufferTypeANWBuffer; |
| if (!usingANWBuffer && bufferType != kMetadataBufferTypeGrallocSource) { |
| ALOGE("Unsupported metadata type (%d)", bufferType); |
| return NULL; |
| } |
| |
| buffer_handle_t handle; |
| int format; |
| size_t srcStride; |
| size_t srcVStride; |
| if (usingANWBuffer) { |
| if (srcSize < sizeof(VideoNativeMetadata)) { |
| ALOGE("Metadata is too small (%zu vs %zu)", srcSize, sizeof(VideoNativeMetadata)); |
| return NULL; |
| } |
| |
| VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)src; |
| ANativeWindowBuffer *buffer = nativeMeta.pBuffer; |
| handle = buffer->handle; |
| format = buffer->format; |
| srcStride = buffer->stride; |
| srcVStride = buffer->height; |
| // convert stride from pixels to bytes |
| if (format != HAL_PIXEL_FORMAT_YV12 && |
| format != HAL_PIXEL_FORMAT_YCbCr_420_888) { |
| // TODO do we need to support other formats? |
| srcStride *= 4; |
| } |
| |
| if (nativeMeta.nFenceFd >= 0) { |
| sp<Fence> fence = new Fence(nativeMeta.nFenceFd); |
| nativeMeta.nFenceFd = -1; |
| status_t err = fence->wait(IOMX::kFenceTimeoutMs); |
| if (err != OK) { |
| ALOGE("Timed out waiting on input fence"); |
| return NULL; |
| } |
| } |
| } else { |
| // TODO: remove this part. Check if anyone uses this. |
| |
| if (srcSize < sizeof(VideoGrallocMetadata)) { |
| ALOGE("Metadata is too small (%zu vs %zu)", srcSize, sizeof(VideoGrallocMetadata)); |
| return NULL; |
| } |
| |
| VideoGrallocMetadata &grallocMeta = *(VideoGrallocMetadata *)(src); |
| handle = grallocMeta.pHandle; |
| // assume HAL_PIXEL_FORMAT_RGBA_8888 |
| // there is no way to get the src stride without the graphic buffer |
| format = HAL_PIXEL_FORMAT_RGBA_8888; |
| srcStride = width * 4; |
| srcVStride = height; |
| } |
| |
| size_t neededSize = |
| dstStride * dstVStride + (width >> 1) |
| + (dstStride >> 1) * ((dstVStride >> 1) + (height >> 1) - 1); |
| if (dstSize < neededSize) { |
| ALOGE("destination buffer is too small (%zu vs %zu)", dstSize, neededSize); |
| return NULL; |
| } |
| |
| auto& mapper = GraphicBufferMapper::get(); |
| |
| void *bits = NULL; |
| struct android_ycbcr ycbcr; |
| status_t res; |
| if (format == HAL_PIXEL_FORMAT_YCbCr_420_888) { |
| res = mapper.lockYCbCr( |
| handle, |
| GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER, |
| Rect(width, height), &ycbcr); |
| } else { |
| res = mapper.lock( |
| handle, |
| GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER, |
| Rect(width, height), &bits); |
| } |
| if (res != OK) { |
| ALOGE("Unable to lock image buffer %p for access", handle); |
| return NULL; |
| } |
| |
| switch (format) { |
| case HAL_PIXEL_FORMAT_YV12: // YCrCb / YVU planar |
| // convert to flex YUV |
| ycbcr.y = bits; |
| ycbcr.cr = (uint8_t *)bits + srcStride * srcVStride; |
| ycbcr.cb = (uint8_t *)ycbcr.cr + (srcStride >> 1) * (srcVStride >> 1); |
| ycbcr.chroma_step = 1; |
| ycbcr.cstride = srcVStride >> 1; |
| ycbcr.ystride = srcVStride; |
| ConvertFlexYUVToPlanar(dst, dstStride, dstVStride, &ycbcr, width, height); |
| break; |
| case HAL_PIXEL_FORMAT_YCrCb_420_SP: // YCrCb / YVU semiplanar, NV21 |
| // convert to flex YUV |
| ycbcr.y = bits; |
| ycbcr.cr = (uint8_t *)bits + srcStride * srcVStride; |
| ycbcr.cb = (uint8_t *)ycbcr.cr + 1; |
| ycbcr.chroma_step = 2; |
| ycbcr.cstride = srcVStride; |
| ycbcr.ystride = srcVStride; |
| ConvertFlexYUVToPlanar(dst, dstStride, dstVStride, &ycbcr, width, height); |
| break; |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| ConvertFlexYUVToPlanar(dst, dstStride, dstVStride, &ycbcr, width, height); |
| break; |
| case HAL_PIXEL_FORMAT_RGBX_8888: |
| case HAL_PIXEL_FORMAT_RGBA_8888: |
| case HAL_PIXEL_FORMAT_BGRA_8888: |
| ConvertRGB32ToPlanar( |
| dst, dstStride, dstVStride, |
| (const uint8_t *)bits, width, height, srcStride, |
| format == HAL_PIXEL_FORMAT_BGRA_8888); |
| break; |
| default: |
| ALOGE("Unsupported pixel format %#x", format); |
| dst = NULL; |
| break; |
| } |
| |
| if (mapper.unlock(handle) != OK) { |
| ALOGE("Unable to unlock image buffer %p for access", handle); |
| } |
| |
| return dst; |
| } |
| |
| OMX_ERRORTYPE SoftVideoEncoderOMXComponent::getExtensionIndex( |
| const char *name, OMX_INDEXTYPE *index) { |
| if (!strcmp(name, "OMX.google.android.index.storeMetaDataInBuffers") || |
| !strcmp(name, "OMX.google.android.index.storeANWBufferInMetadata")) { |
| *(int32_t*)index = kStoreMetaDataExtensionIndex; |
| return OMX_ErrorNone; |
| } |
| return SimpleSoftOMXComponent::getExtensionIndex(name, index); |
| } |
| |
| } // namespace android |