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 */
M4MCS_intApplyVPP(M4VPP_Context pContext,M4VIFI_ImagePlane * pPlaneIn,M4VIFI_ImagePlane * pPlaneOut)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