| /************************************************************************************* |
| * INTEL CONFIDENTIAL |
| * Copyright 2011 Intel Corporation All Rights Reserved. |
| * The source code contained or described herein and all documents related |
| * to the source code ("Material") are owned by Intel Corporation or its |
| * suppliers or licensors. Title to the Material remains with Intel |
| * Corporation or its suppliers and licensors. The Material contains trade |
| * secrets and proprietary and confidential information of Intel or its |
| * suppliers and licensors. The Material is protected by worldwide copyright |
| * and trade secret laws and treaty provisions. No part of the Material may |
| * be used, copied, reproduced, modified, published, uploaded, posted, |
| * transmitted, distributed, or disclosed in any way without Intel's prior |
| * express written permission. |
| * |
| * No license under any patent, copyright, trade secret or other intellectual |
| * property right is granted to or conferred upon you by disclosure or delivery |
| * of the Materials, either expressly, by implication, inducement, estoppel or |
| * otherwise. Any license under such intellectual property rights must be express |
| * and approved by Intel in writing. |
| ************************************************************************************/ |
| /* |
| * 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. |
| */ |
| |
| /* |
| ****************************************************************************** |
| * M4OSA_ERR M4MCS_intApplyVPP_NV12(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 2 planes that will contain the output |
| * NV12 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 |
| ****************************************************************************** |
| */ |
| |
| |
| /** |
| ******************************************************************** |
| * 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() */ |
| #include "M4AIR_API_NV12.h" |
| #include "VideoEditorToolsNV12.h" |
| |
| #define UV_PLANE_BORDER_VALUE 0x80 |
| |
| M4OSA_ERR M4MCS_intApplyVPP_NV12(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,UV_PLANE_BORDER_VALUE, |
| (pPlaneOut[1].u_height*pPlaneOut[1].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); |
| |
| M4OSA_TRACE1_0("M4MCS_intApplyVPP_NV12: 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_NV12: 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_NV12: 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_ResizeBilinearNV12toNV12(M4OSA_NULL, |
| pC->pPreResizeFrame, pPlaneOut); |
| if (M4NO_ERROR != err) |
| { |
| M4OSA_TRACE1_1("M4MCS_intApplyVPP_NV12: M4ViFilResizeBilinearNV12toNV12\ |
| returns 0x%x!", err); |
| return err; |
| } |
| } |
| else |
| { |
| M4VIFI_ImagePlane pImagePlanesTemp[2]; |
| M4VIFI_ImagePlane* pPlaneTemp; |
| M4OSA_UInt8* pOutPlaneY = pPlaneOut[0].pac_data + |
| pPlaneOut[0].u_topleft; |
| M4OSA_UInt8* pOutPlaneUV = pPlaneOut[1].pac_data + |
| pPlaneOut[1].u_topleft; |
| M4OSA_UInt8* pInPlaneY = M4OSA_NULL; |
| M4OSA_UInt8* pInPlaneUV = 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; |
| |
| |
| /* 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_NV12"); |
| 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_NV12"); |
| return M4ERR_ALLOC; |
| } |
| |
| pInPlaneY = pImagePlanesTemp[0].pac_data ; |
| pInPlaneUV = pImagePlanesTemp[1].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,UV_PLANE_BORDER_VALUE, |
| (pImagePlanesTemp[1].u_height*pImagePlanesTemp[1].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; |
| |
| } |
| 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); |
| pImagePlanesTemp[1].u_width = Params.m_outputSize.m_width; |
| |
| } |
| |
| /*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[0].u_height = (pImagePlanesTemp[0].u_height>>1)<<1; |
| pImagePlanesTemp[1].u_height = (pImagePlanesTemp[1].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_NV12(&pC->m_air_context, M4AIR_kNV12P); |
| if(err != M4NO_ERROR) |
| { |
| M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ |
| Error when initializing AIR_NV12: 0x%x", err); |
| return err; |
| } |
| } |
| |
| err = M4AIR_configure_NV12(pC->m_air_context, &Params); |
| if(err != M4NO_ERROR) |
| { |
| M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ |
| Error when configuring AIR_NV12: 0x%x", err); |
| M4AIR_cleanUp_NV12(pC->m_air_context); |
| return err; |
| } |
| |
| err = M4AIR_get_NV12(pC->m_air_context, pC->pPreResizeFrame, |
| pPlaneTemp); |
| if(err != M4NO_ERROR) |
| { |
| M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ |
| Error when getting AIR_NV12 plane: 0x%x", err); |
| M4AIR_cleanUp_NV12(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 *)pOutPlaneUV,(void *)pInPlaneUV, |
| pPlaneOut[1].u_width); |
| pInPlaneUV += pPlaneOut[1].u_width; |
| pOutPlaneUV += pPlaneOut[1].u_stride; |
| } |
| |
| for(i=0; i<2; 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_NV12: 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_NV12: 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)); |
| |
| pC->lastDecodedPlane = pPlaneOut; |
| } |
| |
| |
| #endif /*M4MCS_AUDIOONLY*/ |
| M4OSA_TRACE3_0("M4MCS_intApplyVPP_NV12: returning M4NO_ERROR"); |
| return M4NO_ERROR; |
| } |
| |