M4MCS_VideoPreProcessing.c revision b5c7784c96a606890eb8a8b560153ef4a5d1a0d9
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 */
70M4OSA_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