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