1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 /** 17 ************************************************************************* 18 * @file M4MCS_VideoPreProcessing.c 19 * @brief MCS implementation 20 * @note This file implements the encoder callback of the MCS. 21 ************************************************************************* 22 **/ 23 24 /** 25 ******************************************************************** 26 * Includes 27 ******************************************************************** 28 */ 29 /* OSAL headers */ 30 #include "M4OSA_Memory.h" /* OSAL memory management */ 31 #include "M4OSA_Debug.h" /* OSAL debug management */ 32 33 34 /* Core headers */ 35 #include "M4MCS_InternalTypes.h" 36 #include "M4MCS_ErrorCodes.h" 37 38 /** 39 * Video preprocessing interface definition */ 40 #include "M4VPP_API.h" 41 42 /** 43 * Video filters */ 44 #include "M4VIFI_FiltersAPI.h" /**< for M4VIFI_ResizeBilinearYUV420toYUV420() */ 45 46 #ifndef M4MCS_AUDIOONLY 47 #include "M4AIR_API.h" 48 #endif /*M4MCS_AUDIOONLY*/ 49 /**/ 50 51 52 53 54 /* 55 ****************************************************************************** 56 * M4OSA_ERR M4MCS_intApplyVPP(M4VPP_Context pContext, M4VIFI_ImagePlane* pPlaneIn, 57 * M4VIFI_ImagePlane* pPlaneOut) 58 * @brief Do the video rendering and the resize (if needed) 59 * @note It is called by the video encoder 60 * @param pContext (IN) VPP context, which actually is the MCS internal context in our case 61 * @param pPlaneIn (IN) Contains the image 62 * @param pPlaneOut (IN/OUT) Pointer to an array of 3 planes that will contain the output 63 * YUV420 image 64 * @return M4NO_ERROR: No error 65 * @return M4MCS_ERR_VIDEO_DECODE_ERROR: the video decoding failed 66 * @return M4MCS_ERR_RESIZE_ERROR: the resizing failed 67 * @return Any error returned by an underlaying module 68 ****************************************************************************** 69 */ 70 M4OSA_ERR M4MCS_intApplyVPP(M4VPP_Context pContext, M4VIFI_ImagePlane* pPlaneIn, 71 M4VIFI_ImagePlane* pPlaneOut) 72 { 73 M4OSA_ERR err = M4NO_ERROR; 74 75 /* This part is used only if video codecs are compiled*/ 76 #ifndef M4MCS_AUDIOONLY 77 /** 78 * The VPP context is actually the MCS context! */ 79 M4MCS_InternalContext *pC = (M4MCS_InternalContext*)(pContext); 80 81 M4_MediaTime mtCts = pC->dViDecCurrentCts; 82 83 /** 84 * When Closing after an error occured, it may happen that pReaderVideoAU->m_dataAddress has 85 * not been allocated yet. When closing in pause mode, the decoder can be null. 86 * We don't want an error to be returned because it would interrupt the close process and 87 * thus some resources would be locked. So we return M4NO_ERROR. 88 */ 89 /* Initialize to black plane the output plane if the media rendering 90 is black borders */ 91 if(pC->MediaRendering == M4MCS_kBlackBorders) 92 { 93 memset((void *)pPlaneOut[0].pac_data,Y_PLANE_BORDER_VALUE, 94 (pPlaneOut[0].u_height*pPlaneOut[0].u_stride)); 95 memset((void *)pPlaneOut[1].pac_data,U_PLANE_BORDER_VALUE, 96 (pPlaneOut[1].u_height*pPlaneOut[1].u_stride)); 97 memset((void *)pPlaneOut[2].pac_data,V_PLANE_BORDER_VALUE, 98 (pPlaneOut[2].u_height*pPlaneOut[2].u_stride)); 99 } 100 else if ((M4OSA_NULL == pC->ReaderVideoAU.m_dataAddress) || 101 (M4OSA_NULL == pC->pViDecCtxt)) 102 { 103 /** 104 * We must fill the input of the encoder with a dummy image, because 105 * encoding noise leads to a huge video AU, and thus a writer buffer overflow. */ 106 memset((void *)pPlaneOut[0].pac_data,0, 107 pPlaneOut[0].u_stride * pPlaneOut[0].u_height); 108 memset((void *)pPlaneOut[1].pac_data,0, 109 pPlaneOut[1].u_stride * pPlaneOut[1].u_height); 110 memset((void *)pPlaneOut[2].pac_data,0, 111 pPlaneOut[2].u_stride * pPlaneOut[2].u_height); 112 113 M4OSA_TRACE1_0("M4MCS_intApplyVPP: pReaderVideoAU->m_dataAddress is M4OSA_NULL,\ 114 returning M4NO_ERROR"); 115 return M4NO_ERROR; 116 } 117 118 if(pC->isRenderDup == M4OSA_FALSE) 119 { 120 /** 121 * m_pPreResizeFrame different than M4OSA_NULL means that resizing is needed */ 122 if (M4OSA_NULL != pC->pPreResizeFrame) 123 { 124 /** FB 2008/10/20: 125 Used for cropping and black borders*/ 126 M4AIR_Params Params; 127 128 M4OSA_TRACE3_0("M4MCS_intApplyVPP: Need to resize"); 129 err = pC->m_pVideoDecoder->m_pFctRender(pC->pViDecCtxt, &mtCts, 130 pC->pPreResizeFrame, M4OSA_TRUE); 131 if (M4NO_ERROR != err) 132 { 133 M4OSA_TRACE1_1("M4MCS_intApplyVPP: m_pFctRender returns 0x%x!", err); 134 return err; 135 } 136 137 if(pC->MediaRendering == M4MCS_kResizing) 138 { 139 /* 140 * Call the resize filter. From the intermediate frame to the encoder 141 * image plane 142 */ 143 err = M4VIFI_ResizeBilinearYUV420toYUV420(M4OSA_NULL, 144 pC->pPreResizeFrame, pPlaneOut); 145 if (M4NO_ERROR != err) 146 { 147 M4OSA_TRACE1_1("M4MCS_intApplyVPP: M4ViFilResizeBilinearYUV420toYUV420\ 148 returns 0x%x!", err); 149 return err; 150 } 151 } 152 else 153 { 154 M4VIFI_ImagePlane pImagePlanesTemp[3]; 155 M4VIFI_ImagePlane* pPlaneTemp; 156 M4OSA_UInt8* pOutPlaneY = pPlaneOut[0].pac_data + 157 pPlaneOut[0].u_topleft; 158 M4OSA_UInt8* pOutPlaneU = pPlaneOut[1].pac_data + 159 pPlaneOut[1].u_topleft; 160 M4OSA_UInt8* pOutPlaneV = pPlaneOut[2].pac_data + 161 pPlaneOut[2].u_topleft; 162 M4OSA_UInt8* pInPlaneY = M4OSA_NULL; 163 M4OSA_UInt8* pInPlaneU = M4OSA_NULL; 164 M4OSA_UInt8* pInPlaneV = M4OSA_NULL; 165 M4OSA_UInt32 i = 0; 166 167 /*FB 2008/10/20: to keep media aspect ratio*/ 168 /*Initialize AIR Params*/ 169 Params.m_inputCoord.m_x = 0; 170 Params.m_inputCoord.m_y = 0; 171 Params.m_inputSize.m_height = pC->pPreResizeFrame->u_height; 172 Params.m_inputSize.m_width = pC->pPreResizeFrame->u_width; 173 Params.m_outputSize.m_width = pPlaneOut->u_width; 174 Params.m_outputSize.m_height = pPlaneOut->u_height; 175 Params.m_bOutputStripe = M4OSA_FALSE; 176 Params.m_outputOrientation = M4COMMON_kOrientationTopLeft; 177 178 /** 179 Media rendering: Black borders*/ 180 if(pC->MediaRendering == M4MCS_kBlackBorders) 181 { 182 pImagePlanesTemp[0].u_width = pPlaneOut[0].u_width; 183 pImagePlanesTemp[0].u_height = pPlaneOut[0].u_height; 184 pImagePlanesTemp[0].u_stride = pPlaneOut[0].u_width; 185 pImagePlanesTemp[0].u_topleft = 0; 186 187 pImagePlanesTemp[1].u_width = pPlaneOut[1].u_width; 188 pImagePlanesTemp[1].u_height = pPlaneOut[1].u_height; 189 pImagePlanesTemp[1].u_stride = pPlaneOut[1].u_width; 190 pImagePlanesTemp[1].u_topleft = 0; 191 192 pImagePlanesTemp[2].u_width = pPlaneOut[2].u_width; 193 pImagePlanesTemp[2].u_height = pPlaneOut[2].u_height; 194 pImagePlanesTemp[2].u_stride = pPlaneOut[2].u_width; 195 pImagePlanesTemp[2].u_topleft = 0; 196 197 /* Allocates plan in local image plane structure */ 198 pImagePlanesTemp[0].pac_data = 199 (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc(pImagePlanesTemp[0]\ 200 .u_width * pImagePlanesTemp[0].u_height, M4VS, 201 (M4OSA_Char *)"M4xVSS_PictureCallbackFct: temporary plane bufferY") ; 202 if(pImagePlanesTemp[0].pac_data == M4OSA_NULL) 203 { 204 M4OSA_TRACE1_0("Error alloc in M4MCS_intApplyVPP"); 205 return M4ERR_ALLOC; 206 } 207 pImagePlanesTemp[1].pac_data = 208 (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc(pImagePlanesTemp[1]\ 209 .u_width * pImagePlanesTemp[1].u_height, M4VS, 210 (M4OSA_Char *)"M4xVSS_PictureCallbackFct: temporary plane bufferU") ; 211 if(pImagePlanesTemp[1].pac_data == M4OSA_NULL) 212 { 213 M4OSA_TRACE1_0("Error alloc in M4MCS_intApplyVPP"); 214 return M4ERR_ALLOC; 215 } 216 pImagePlanesTemp[2].pac_data = 217 (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc(pImagePlanesTemp[2]\ 218 .u_width * pImagePlanesTemp[2].u_height, 219 M4VS,(M4OSA_Char *)"M4xVSS_PictureCallbackFct: temporary plane bufferV") ; 220 if(pImagePlanesTemp[2].pac_data == M4OSA_NULL) 221 { 222 M4OSA_TRACE1_0("Error alloc in M4MCS_intApplyVPP"); 223 return M4ERR_ALLOC; 224 } 225 226 pInPlaneY = pImagePlanesTemp[0].pac_data ; 227 pInPlaneU = pImagePlanesTemp[1].pac_data ; 228 pInPlaneV = pImagePlanesTemp[2].pac_data ; 229 230 memset((void *)pImagePlanesTemp[0].pac_data,Y_PLANE_BORDER_VALUE, 231 (pImagePlanesTemp[0].u_height*pImagePlanesTemp[0].u_stride)); 232 memset((void *)pImagePlanesTemp[1].pac_data,U_PLANE_BORDER_VALUE, 233 (pImagePlanesTemp[1].u_height*pImagePlanesTemp[1].u_stride)); 234 memset((void *)pImagePlanesTemp[2].pac_data,V_PLANE_BORDER_VALUE, 235 (pImagePlanesTemp[2].u_height*pImagePlanesTemp[2].u_stride)); 236 237 if((M4OSA_UInt32)((pC->pPreResizeFrame->u_height * pPlaneOut->u_width)\ 238 /pC->pPreResizeFrame->u_width) <= pPlaneOut->u_height) 239 //Params.m_inputSize.m_height < Params.m_inputSize.m_width) 240 { 241 /*it is height so black borders will be on the top and on the bottom side*/ 242 Params.m_outputSize.m_width = pPlaneOut->u_width; 243 Params.m_outputSize.m_height = 244 (M4OSA_UInt32) 245 ((pC->pPreResizeFrame->u_height * pPlaneOut->u_width)\ 246 /pC->pPreResizeFrame->u_width); 247 /*number of lines at the top*/ 248 pImagePlanesTemp[0].u_topleft = 249 (M4MCS_ABS((M4OSA_Int32) 250 (pImagePlanesTemp[0].u_height\ 251 -Params.m_outputSize.m_height)>>1)) * 252 pImagePlanesTemp[0].u_stride; 253 pImagePlanesTemp[0].u_height = Params.m_outputSize.m_height; 254 pImagePlanesTemp[1].u_topleft = 255 (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[1].u_height\ 256 -(Params.m_outputSize.m_height>>1)))>>1)\ 257 * pImagePlanesTemp[1].u_stride; 258 pImagePlanesTemp[1].u_height = Params.m_outputSize.m_height>>1; 259 pImagePlanesTemp[2].u_topleft = 260 (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[2].u_height\ 261 -(Params.m_outputSize.m_height>>1)))>>1)\ 262 * pImagePlanesTemp[2].u_stride; 263 pImagePlanesTemp[2].u_height = Params.m_outputSize.m_height>>1; 264 } 265 else 266 { 267 /*it is width so black borders will be on the left and right side*/ 268 Params.m_outputSize.m_height = pPlaneOut->u_height; 269 Params.m_outputSize.m_width = 270 (M4OSA_UInt32)((pC->pPreResizeFrame->u_width 271 * pPlaneOut->u_height)\ 272 /pC->pPreResizeFrame->u_height); 273 274 pImagePlanesTemp[0].u_topleft = 275 (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[0].u_width-\ 276 Params.m_outputSize.m_width)>>1)); 277 pImagePlanesTemp[0].u_width = Params.m_outputSize.m_width; 278 pImagePlanesTemp[1].u_topleft = 279 (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[1].u_width-\ 280 (Params.m_outputSize.m_width>>1)))>>1); 281 pImagePlanesTemp[1].u_width = Params.m_outputSize.m_width>>1; 282 pImagePlanesTemp[2].u_topleft = 283 (M4MCS_ABS((M4OSA_Int32)(pImagePlanesTemp[2].u_width-\ 284 (Params.m_outputSize.m_width>>1)))>>1); 285 pImagePlanesTemp[2].u_width = Params.m_outputSize.m_width>>1; 286 } 287 288 /*Width and height have to be even*/ 289 Params.m_outputSize.m_width = (Params.m_outputSize.m_width>>1)<<1; 290 Params.m_outputSize.m_height = (Params.m_outputSize.m_height>>1)<<1; 291 Params.m_inputSize.m_width = (Params.m_inputSize.m_width>>1)<<1; 292 Params.m_inputSize.m_height = (Params.m_inputSize.m_height>>1)<<1; 293 pImagePlanesTemp[0].u_width = (pImagePlanesTemp[0].u_width>>1)<<1; 294 pImagePlanesTemp[1].u_width = (pImagePlanesTemp[1].u_width>>1)<<1; 295 pImagePlanesTemp[2].u_width = (pImagePlanesTemp[2].u_width>>1)<<1; 296 pImagePlanesTemp[0].u_height = (pImagePlanesTemp[0].u_height>>1)<<1; 297 pImagePlanesTemp[1].u_height = (pImagePlanesTemp[1].u_height>>1)<<1; 298 pImagePlanesTemp[2].u_height = (pImagePlanesTemp[2].u_height>>1)<<1; 299 300 /*Check that values are coherent*/ 301 if(Params.m_inputSize.m_height == Params.m_outputSize.m_height) 302 { 303 Params.m_inputSize.m_width = Params.m_outputSize.m_width; 304 } 305 else if(Params.m_inputSize.m_width == Params.m_outputSize.m_width) 306 { 307 Params.m_inputSize.m_height = Params.m_outputSize.m_height; 308 } 309 pPlaneTemp = pImagePlanesTemp; 310 } 311 312 /** 313 Media rendering: Cropping*/ 314 if(pC->MediaRendering == M4MCS_kCropping) 315 { 316 Params.m_outputSize.m_height = pPlaneOut->u_height; 317 Params.m_outputSize.m_width = pPlaneOut->u_width; 318 if((Params.m_outputSize.m_height * Params.m_inputSize.m_width)\ 319 /Params.m_outputSize.m_width<Params.m_inputSize.m_height) 320 { 321 /*height will be cropped*/ 322 Params.m_inputSize.m_height = 323 (M4OSA_UInt32)((Params.m_outputSize.m_height \ 324 * Params.m_inputSize.m_width) / 325 Params.m_outputSize.m_width); 326 Params.m_inputSize.m_height = 327 (Params.m_inputSize.m_height>>1)<<1; 328 Params.m_inputCoord.m_y = 329 (M4OSA_Int32)((M4OSA_Int32) 330 ((pC->pPreResizeFrame->u_height\ 331 - Params.m_inputSize.m_height))>>1); 332 } 333 else 334 { 335 /*width will be cropped*/ 336 Params.m_inputSize.m_width = 337 (M4OSA_UInt32)((Params.m_outputSize.m_width\ 338 * Params.m_inputSize.m_height) / 339 Params.m_outputSize.m_height); 340 Params.m_inputSize.m_width = 341 (Params.m_inputSize.m_width>>1)<<1; 342 Params.m_inputCoord.m_x = 343 (M4OSA_Int32)((M4OSA_Int32) 344 ((pC->pPreResizeFrame->u_width\ 345 - Params.m_inputSize.m_width))>>1); 346 } 347 pPlaneTemp = pPlaneOut; 348 } 349 /** 350 * Call AIR functions */ 351 if(M4OSA_NULL == pC->m_air_context) 352 { 353 err = M4AIR_create(&pC->m_air_context, M4AIR_kYUV420P); 354 if(err != M4NO_ERROR) 355 { 356 M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ 357 Error when initializing AIR: 0x%x", err); 358 return err; 359 } 360 } 361 362 err = M4AIR_configure(pC->m_air_context, &Params); 363 if(err != M4NO_ERROR) 364 { 365 M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ 366 Error when configuring AIR: 0x%x", err); 367 M4AIR_cleanUp(pC->m_air_context); 368 return err; 369 } 370 371 err = M4AIR_get(pC->m_air_context, pC->pPreResizeFrame, 372 pPlaneTemp); 373 if(err != M4NO_ERROR) 374 { 375 M4OSA_TRACE1_1("M4xVSS_PictureCallbackFct:\ 376 Error when getting AIR plane: 0x%x", err); 377 M4AIR_cleanUp(pC->m_air_context); 378 return err; 379 } 380 381 if(pC->MediaRendering == M4MCS_kBlackBorders) 382 { 383 for(i=0; i<pPlaneOut[0].u_height; i++) 384 { 385 memcpy( (void *)pOutPlaneY, 386 (void *)pInPlaneY, 387 pPlaneOut[0].u_width); 388 pInPlaneY += pPlaneOut[0].u_width; 389 pOutPlaneY += pPlaneOut[0].u_stride; 390 } 391 for(i=0; i<pPlaneOut[1].u_height; i++) 392 { 393 memcpy( (void *)pOutPlaneU, 394 (void *)pInPlaneU, 395 pPlaneOut[1].u_width); 396 pInPlaneU += pPlaneOut[1].u_width; 397 pOutPlaneU += pPlaneOut[1].u_stride; 398 } 399 for(i=0; i<pPlaneOut[2].u_height; i++) 400 { 401 memcpy( (void *)pOutPlaneV, 402 (void *)pInPlaneV, 403 pPlaneOut[2].u_width); 404 pInPlaneV += pPlaneOut[2].u_width; 405 pOutPlaneV += pPlaneOut[2].u_stride; 406 } 407 408 for(i=0; i<3; i++) 409 { 410 if(pImagePlanesTemp[i].pac_data != M4OSA_NULL) 411 { 412 free( 413 pImagePlanesTemp[i].pac_data); 414 pImagePlanesTemp[i].pac_data = M4OSA_NULL; 415 } 416 } 417 } 418 } 419 } 420 else 421 { 422 M4OSA_TRACE3_0("M4MCS_intApplyVPP: Don't need resizing"); 423 err = pC->m_pVideoDecoder->m_pFctRender(pC->pViDecCtxt, 424 &mtCts, pPlaneOut, 425 M4OSA_TRUE); 426 if (M4NO_ERROR != err) 427 { 428 M4OSA_TRACE1_1("M4MCS_intApplyVPP: m_pFctRender returns 0x%x!", err); 429 return err; 430 } 431 } 432 pC->lastDecodedPlane = pPlaneOut; 433 } 434 else 435 { 436 /* Copy last decoded plane to output plane */ 437 memcpy((void *)pPlaneOut[0].pac_data, 438 (void *)pC->lastDecodedPlane[0].pac_data, 439 (pPlaneOut[0].u_height * pPlaneOut[0].u_width)); 440 memcpy((void *)pPlaneOut[1].pac_data, 441 (void *)pC->lastDecodedPlane[1].pac_data, 442 (pPlaneOut[1].u_height * pPlaneOut[1].u_width)); 443 memcpy((void *)pPlaneOut[2].pac_data, 444 (void *)pC->lastDecodedPlane[2].pac_data, 445 (pPlaneOut[2].u_height * pPlaneOut[2].u_width)); 446 pC->lastDecodedPlane = pPlaneOut; 447 } 448 449 450 #endif /*M4MCS_AUDIOONLY*/ 451 M4OSA_TRACE3_0("M4MCS_intApplyVPP: returning M4NO_ERROR"); 452 return M4NO_ERROR; 453 } 454 455 456