/* * 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. */ /** ************************************************************************* * @file M4MCS_VideoPreProcessing.c * @brief MCS implementation * @note This file implements the encoder callback of the MCS. ************************************************************************* **/ /** ******************************************************************** * Includes ******************************************************************** */ /* OSAL headers */ #include "M4OSA_Memory.h" /* OSAL memory management */ #include "M4OSA_Debug.h" /* OSAL debug management */ /* Core headers */ #include "M4MCS_InternalTypes.h" #include "M4MCS_ErrorCodes.h" /** * Video preprocessing interface definition */ #include "M4VPP_API.h" /** * Video filters */ #include "M4VIFI_FiltersAPI.h" /**< for M4VIFI_ResizeBilinearYUV420toYUV420() */ #ifndef M4MCS_AUDIOONLY #include "M4AIR_API.h" #endif /*M4MCS_AUDIOONLY*/ /**/ /* ****************************************************************************** * M4OSA_ERR M4MCS_intApplyVPP(M4VPP_Context pContext, M4VIFI_ImagePlane* pPlaneIn, * M4VIFI_ImagePlane* pPlaneOut) * @brief Do the video rendering and the resize (if needed) * @note It is called by the video encoder * @param pContext (IN) VPP context, which actually is the MCS internal context in our case * @param pPlaneIn (IN) Contains the image * @param pPlaneOut (IN/OUT) Pointer to an array of 3 planes that will contain the output * YUV420 image * @return M4NO_ERROR: No error * @return M4MCS_ERR_VIDEO_DECODE_ERROR: the video decoding failed * @return M4MCS_ERR_RESIZE_ERROR: the resizing failed * @return Any error returned by an underlaying module ****************************************************************************** */ M4OSA_ERR M4MCS_intApplyVPP(M4VPP_Context pContext, M4VIFI_ImagePlane* pPlaneIn, M4VIFI_ImagePlane* pPlaneOut) { M4OSA_ERR err = M4NO_ERROR; /* This part is used only if video codecs are compiled*/ #ifndef M4MCS_AUDIOONLY /** * The VPP context is actually the MCS context! */ M4MCS_InternalContext *pC = (M4MCS_InternalContext*)(pContext); M4_MediaTime mtCts = pC->dViDecCurrentCts; /** * When Closing after an error occured, it may happen that pReaderVideoAU->m_dataAddress has * not been allocated yet. When closing in pause mode, the decoder can be null. * We don't want an error to be returned because it would interrupt the close process and * thus some resources would be locked. So we return M4NO_ERROR. */ /* Initialize to black plane the output plane if the media rendering is black borders */ if(pC->MediaRendering == M4MCS_kBlackBorders) { memset((void *)pPlaneOut[0].pac_data,Y_PLANE_BORDER_VALUE, (pPlaneOut[0].u_height*pPlaneOut[0].u_stride)); memset((void *)pPlaneOut[1].pac_data,U_PLANE_BORDER_VALUE, (pPlaneOut[1].u_height*pPlaneOut[1].u_stride)); memset((void *)pPlaneOut[2].pac_data,V_PLANE_BORDER_VALUE, (pPlaneOut[2].u_height*pPlaneOut[2].u_stride)); } else if ((M4OSA_NULL == pC->ReaderVideoAU.m_dataAddress) || (M4OSA_NULL == pC->pViDecCtxt)) { /** * We must fill the input of the encoder with a dummy image, because * encoding noise leads to a huge video AU, and thus a writer buffer overflow. */ memset((void *)pPlaneOut[0].pac_data,0, pPlaneOut[0].u_stride * pPlaneOut[0].u_height); memset((void *)pPlaneOut[1].pac_data,0, pPlaneOut[1].u_stride * pPlaneOut[1].u_height); memset((void *)pPlaneOut[2].pac_data,0, pPlaneOut[2].u_stride * pPlaneOut[2].u_height); M4OSA_TRACE1_0("M4MCS_intApplyVPP: pReaderVideoAU->m_dataAddress is M4OSA_NULL,\ returning M4NO_ERROR"); return M4NO_ERROR; } if(pC->isRenderDup == M4OSA_FALSE) { /** * m_pPreResizeFrame different than M4OSA_NULL means that resizing is needed */ if (M4OSA_NULL != pC->pPreResizeFrame) { /** FB 2008/10/20: Used for cropping and black borders*/ M4AIR_Params Params; M4OSA_TRACE3_0("M4MCS_intApplyVPP: Need to resize"); err = pC->m_pVideoDecoder->m_pFctRender(pC->pViDecCtxt, &mtCts, pC->pPreResizeFrame, M4OSA_TRUE); if (M4NO_ERROR != err) { M4OSA_TRACE1_1("M4MCS_intApplyVPP: m_pFctRender returns 0x%x!", err); return err; } if(pC->MediaRendering == M4MCS_kResizing) { /* * Call the resize filter. From the intermediate frame to the encoder * image plane */ err = M4VIFI_ResizeBilinearYUV420toYUV420(M4OSA_NULL, pC->pPreResizeFrame, pPlaneOut); if (M4NO_ERROR != err) { M4OSA_TRACE1_1("M4MCS_intApplyVPP: M4ViFilResizeBilinearYUV420toYUV420\ returns 0x%x!", err); return err; } } else { M4VIFI_ImagePlane pImagePlanesTemp[3]; M4VIFI_ImagePlane* pPlaneTemp; M4OSA_UInt8* pOutPlaneY = pPlaneOut[0].pac_data + pPlaneOut[0].u_topleft; M4OSA_UInt8* pOutPlaneU = pPlaneOut[1].pac_data + pPlaneOut[1].u_topleft; M4OSA_UInt8* pOutPlaneV = pPlaneOut[2].pac_data + pPlaneOut[2].u_topleft; M4OSA_UInt8* pInPlaneY = M4OSA_NULL; M4OSA_UInt8* pInPlaneU = M4OSA_NULL; M4OSA_UInt8* pInPlaneV = M4OSA_NULL; M4OSA_UInt32 i = 0; /*FB 2008/10/20: to keep media aspect ratio*/ /*Initialize AIR Params*/ Params.m_inputCoord.m_x = 0; Params.m_inputCoord.m_y = 0; Params.m_inputSize.m_height = pC->pPreResizeFrame->u_height; Params.m_inputSize.m_width = pC->pPreResizeFrame->u_width; Params.m_outputSize.m_width = pPlaneOut->u_width; Params.m_outputSize.m_height = pPlaneOut->u_height; Params.m_bOutputStripe = M4OSA_FALSE; Params.m_outputOrientation = M4COMMON_kOrientationTopLeft; /** Media rendering: Black borders*/ if(pC->MediaRendering == M4MCS_kBlackBorders) { pImagePlanesTemp[0].u_width = pPlaneOut[0].u_width; pImagePlanesTemp[0].u_height = pPlaneOut[0].u_height; pImagePlanesTemp[0].u_stride = pPlaneOut[0].u_width; pImagePlanesTemp[0].u_topleft = 0; pImagePlanesTemp[1].u_width = pPlaneOut[1].u_width; pImagePlanesTemp[1].u_height = pPlaneOut[1].u_height; pImagePlanesTemp[1].u_stride = pPlaneOut[1].u_width; pImagePlanesTemp[1].u_topleft = 0; pImagePlanesTemp[2].u_width = pPlaneOut[2].u_width; pImagePlanesTemp[2].u_height = pPlaneOut[2].u_height; pImagePlanesTemp[2].u_stride = pPlaneOut[2].u_width; pImagePlanesTemp[2].u_topleft = 0; /* Allocates plan in local image plane structure */ pImagePlanesTemp[0].pac_data = (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc(pImagePlanesTemp[0]\ .u_width * pImagePlanesTemp[0].u_height, M4VS, (M4OSA_Char *)"M4xVSS_PictureCallbackFct: temporary plane bufferY") ; if(pImagePlanesTemp[0].pac_data == M4OSA_NULL) { M4OSA_TRACE1_0("Error alloc in M4MCS_intApplyVPP"); return M4ERR_ALLOC; } pImagePlanesTemp[1].pac_data = (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc(pImagePlanesTemp[1]\ .u_width * pImagePlanesTemp[1].u_height, M4VS, (M4OSA_Char *)"M4xVSS_PictureCallbackFct: temporary plane bufferU") ; if(pImagePlanesTemp[1].pac_data == M4OSA_NULL) { M4OSA_TRACE1_0("Error alloc in M4MCS_intApplyVPP"); return M4ERR_ALLOC; } pImagePlanesTemp[2].pac_data = (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc(pImagePlanesTemp[2]\ .u_width * pImagePlanesTemp[2].u_height, M4VS,(M4OSA_Char *)"M4xVSS_PictureCallbackFct: temporary plane bufferV") ; if(pImagePlanesTemp[2].pac_data == M4OSA_NULL) { M4OSA_TRACE1_0("Error alloc in M4MCS_intApplyVPP"); return M4ERR_ALLOC; } pInPlaneY = pImagePlanesTemp[0].pac_data ; pInPlaneU = pImagePlanesTemp[1].pac_data ; pInPlaneV = pImagePlanesTemp[2].pac_data ; memset((void *)pImagePlanesTemp[0].pac_data,Y_PLANE_BORDER_VALUE, (pImagePlanesTemp[0].u_height*pImagePlanesTemp[0].u_stride)); memset((void *)pImagePlanesTemp[1].pac_data,U_PLANE_BORDER_VALUE, (pImagePlanesTemp[1].u_height*pImagePlanesTemp[1].u_stride)); memset((void *)pImagePlanesTemp[2].pac_data,V_PLANE_BORDER_VALUE, (pImagePlanesTemp[2].u_height*pImagePlanesTemp[2].u_stride)); if((M4OSA_UInt32)((pC->pPreResizeFrame->u_height * pPlaneOut->u_width)\ /pC->pPreResizeFrame->u_width) <= pPlaneOut->u_height) //Params.m_inputSize.m_height < Params.m_inputSize.m_width) { /*it is height so black borders will be on the top and on the bottom side*/ Params.m_outputSize.m_width = pPlaneOut->u_width; Params.m_outputSize.m_height = (M4OSA_UInt32) ((pC->pPreResizeFrame->u_height * pPlaneOut->u_width)\ /pC->pPreResizeFrame->u_width); /*number of lines at the top*/ pImagePlanesTemp[0].u_topleft = (M4MCS_ABS((M4OSA_Int32) (pImagePlanesTemp[0].u_height\ -Params.m_outputSize.m_height)>>1)) * pImagePlanesTemp[0].u_stride; pImagePlanesTemp[0].u_height = Params.m_outputSize.m_height; pImagePlanesTemp[1].u_topleft = (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[1].u_height\ -(Params.m_outputSize.m_height>>1)))>>1)\ * pImagePlanesTemp[1].u_stride; pImagePlanesTemp[1].u_height = Params.m_outputSize.m_height>>1; pImagePlanesTemp[2].u_topleft = (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[2].u_height\ -(Params.m_outputSize.m_height>>1)))>>1)\ * pImagePlanesTemp[2].u_stride; pImagePlanesTemp[2].u_height = Params.m_outputSize.m_height>>1; } else { /*it is width so black borders will be on the left and right side*/ Params.m_outputSize.m_height = pPlaneOut->u_height; Params.m_outputSize.m_width = (M4OSA_UInt32)((pC->pPreResizeFrame->u_width * pPlaneOut->u_height)\ /pC->pPreResizeFrame->u_height); pImagePlanesTemp[0].u_topleft = (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[0].u_width-\ Params.m_outputSize.m_width)>>1)); pImagePlanesTemp[0].u_width = Params.m_outputSize.m_width; pImagePlanesTemp[1].u_topleft = (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[1].u_width-\ (Params.m_outputSize.m_width>>1)))>>1); pImagePlanesTemp[1].u_width = Params.m_outputSize.m_width>>1; pImagePlanesTemp[2].u_topleft = (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[2].u_width-\ (Params.m_outputSize.m_width>>1)))>>1); pImagePlanesTemp[2].u_width = Params.m_outputSize.m_width>>1; } /*Width and height have to be even*/ Params.m_outputSize.m_width = (Params.m_outputSize.m_width>>1)<<1; Params.m_outputSize.m_height = (Params.m_outputSize.m_height>>1)<<1; Params.m_inputSize.m_width = (Params.m_inputSize.m_width>>1)<<1; Params.m_inputSize.m_height = (Params.m_inputSize.m_height>>1)<<1; pImagePlanesTemp[0].u_width = (pImagePlanesTemp[0].u_width>>1)<<1; pImagePlanesTemp[1].u_width = (pImagePlanesTemp[1].u_width>>1)<<1; pImagePlanesTemp[2].u_width = (pImagePlanesTemp[2].u_width>>1)<<1; pImagePlanesTemp[0].u_height = (pImagePlanesTemp[0].u_height>>1)<<1; pImagePlanesTemp[1].u_height = (pImagePlanesTemp[1].u_height>>1)<<1; pImagePlanesTemp[2].u_height = (pImagePlanesTemp[2].u_height>>1)<<1; /*Check that values are coherent*/ if(Params.m_inputSize.m_height == Params.m_outputSize.m_height) { Params.m_inputSize.m_width = Params.m_outputSize.m_width; } else if(Params.m_inputSize.m_width == Params.m_outputSize.m_width) { Params.m_inputSize.m_height = Params.m_outputSize.m_height; } pPlaneTemp = pImagePlanesTemp; } /** Media rendering: Cropping*/ if(pC->MediaRendering == M4MCS_kCropping) { Params.m_outputSize.m_height = pPlaneOut->u_height; Params.m_outputSize.m_width = pPlaneOut->u_width; if((Params.m_outputSize.m_height * Params.m_inputSize.m_width)\ /Params.m_outputSize.m_width<Params.m_inputSize.m_height) { /*height will be cropped*/ Params.m_inputSize.m_height = (M4OSA_UInt32)((Params.m_outputSize.m_height \ * Params.m_inputSize.m_width) / Params.m_outputSize.m_width); Params.m_inputSize.m_height = (Params.m_inputSize.m_height>>1)<<1; Params.m_inputCoord.m_y = (M4OSA_Int32)((M4OSA_Int32) ((pC->pPreResizeFrame->u_height\ - Params.m_inputSize.m_height))>>1); } else { /*width will be cropped*/ Params.m_inputSize.m_width = (M4OSA_UInt32)((Params.m_outputSize.m_width\ * Params.m_inputSize.m_height) / Params.m_outputSize.m_height); Params.m_inputSize.m_width = (Params.m_inputSize.m_width>>1)<<1; Params.m_inputCoord.m_x = (M4OSA_Int32)((M4OSA_Int32) ((pC->pPreResizeFrame->u_width\ - Params.m_inputSize.m_width))>>1); } pPlaneTemp = pPlaneOut; } /** * Call AIR functions */ if(M4OSA_NULL == pC->m_air_context) { err = M4AIR_create(&pC->m_air_context, M4AIR_kYUV420P); if(err != M4NO_ERROR) { M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ Error when initializing AIR: 0x%x", err); return err; } } err = M4AIR_configure(pC->m_air_context, &Params); if(err != M4NO_ERROR) { M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ Error when configuring AIR: 0x%x", err); M4AIR_cleanUp(pC->m_air_context); return err; } err = M4AIR_get(pC->m_air_context, pC->pPreResizeFrame, pPlaneTemp); if(err != M4NO_ERROR) { M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ Error when getting AIR plane: 0x%x", err); M4AIR_cleanUp(pC->m_air_context); return err; } if(pC->MediaRendering == M4MCS_kBlackBorders) { for(i=0; i<pPlaneOut[0].u_height; i++) { memcpy( (void *)pOutPlaneY, (void *)pInPlaneY, pPlaneOut[0].u_width); pInPlaneY += pPlaneOut[0].u_width; pOutPlaneY += pPlaneOut[0].u_stride; } for(i=0; i<pPlaneOut[1].u_height; i++) { memcpy( (void *)pOutPlaneU, (void *)pInPlaneU, pPlaneOut[1].u_width); pInPlaneU += pPlaneOut[1].u_width; pOutPlaneU += pPlaneOut[1].u_stride; } for(i=0; i<pPlaneOut[2].u_height; i++) { memcpy( (void *)pOutPlaneV, (void *)pInPlaneV, pPlaneOut[2].u_width); pInPlaneV += pPlaneOut[2].u_width; pOutPlaneV += pPlaneOut[2].u_stride; } for(i=0; i<3; i++) { if(pImagePlanesTemp[i].pac_data != M4OSA_NULL) { free( pImagePlanesTemp[i].pac_data); pImagePlanesTemp[i].pac_data = M4OSA_NULL; } } } } } else { M4OSA_TRACE3_0("M4MCS_intApplyVPP: Don't need resizing"); err = pC->m_pVideoDecoder->m_pFctRender(pC->pViDecCtxt, &mtCts, pPlaneOut, M4OSA_TRUE); if (M4NO_ERROR != err) { M4OSA_TRACE1_1("M4MCS_intApplyVPP: m_pFctRender returns 0x%x!", err); return err; } } pC->lastDecodedPlane = pPlaneOut; } else { /* Copy last decoded plane to output plane */ memcpy((void *)pPlaneOut[0].pac_data, (void *)pC->lastDecodedPlane[0].pac_data, (pPlaneOut[0].u_height * pPlaneOut[0].u_width)); memcpy((void *)pPlaneOut[1].pac_data, (void *)pC->lastDecodedPlane[1].pac_data, (pPlaneOut[1].u_height * pPlaneOut[1].u_width)); memcpy((void *)pPlaneOut[2].pac_data, (void *)pC->lastDecodedPlane[2].pac_data, (pPlaneOut[2].u_height * pPlaneOut[2].u_width)); pC->lastDecodedPlane = pPlaneOut; } #endif /*M4MCS_AUDIOONLY*/ M4OSA_TRACE3_0("M4MCS_intApplyVPP: returning M4NO_ERROR"); return M4NO_ERROR; }