videoeditorengine/h263decoder/src/decvp_mpeg.cpp
author Mikael Laine <mikael.laine@ixonos.com>
Fri, 29 Jan 2010 14:08:33 +0200
changeset 0 951a5db380a0
permissions -rw-r--r--
Committing the Video Editor package under the Eclipse Public License

/*
* Copyright (c) 2010 Ixonos Plc.
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - Initial contribution
*
* Contributors:
* Ixonos Plc
*
* Description:  
* Video packet decoding module (MPEG-4).
*
*/


/*
 * Includes
 */
#include "h263dConfig.h"
#include "decvp_mpeg.h"
#include "block.h"
#include "debug.h"
#include "decmbs.h"
#include "stckheap.h"
#include "sync.h"
#include "viddemux.h"
#include "biblin.h"
/* MVE */
#include "MPEG4Transcoder.h"

/*
 * Global functions
 */

/* {{-output"dvpGetAndDecodeVideoPacketHeader.txt"}} */
/*
 * dvpGetAndDecodeVideoPacketHeader
 *    
 *
 * Parameters:
 *    inParam                    input parameters
 *    inOutParam                 input/output parameters, these parameters
 *                               may be modified in the function
 *
 * Function:
 *    This function gets and decodes a Video Packet header at the 
 *    current position of the bit buffer. It checks its correctness and 
 *    if it fits to the sequence of VPs, and does the necessary actions for 
 *    correction.
 *
 * Returns:
 *    >= 0                       the function was successful
 *    < 0                        an error occured
 *
 */

int dvpGetAndDecodeVideoPacketHeader(
   const dvpVPInParam_t *inParam,
   dvpVPInOutParam_t *inOutParam)
/* {{-output"dvpGetAndDecodeVideoPacketHeader.txt"}} */
{
   int16 error = 0;
   vdxGetVideoPacketHeaderInputParam_t vdxParam;
   vdxVideoPacketHeader_t header;
   bibBuffer_t *inBuffer = inParam->inBuffer;
   int bitErrorIndication = 0, retVal, fLostSegment = FALSE;

   /* 
    * Get VP header 
    */

   vdxParam.fcode_forward = inParam->pictParam->fcode_forward;
   vdxParam.time_increment_resolution = inParam->pictParam->time_increment_resolution;
   vdxParam.numOfMBs = inParam->pictParam->numMBsInGOB;

   retVal = vdxGetVideoPacketHeader(inBuffer, &vdxParam, &header, &bitErrorIndication);
   if (retVal < 0) {
     return DGOB_ERR;
   } else if (retVal > 0) {
      deb("dvpGetAndDecodeVideoPacketHeader: ERROR - vdxGetVideoPacketHeader failed.\n");
      goto headerFailure;
   }


   /*
    * Check header validity
    */

   if (header.currMBNum == 0 || header.currMBNum >= inParam->pictParam->numMBsInGOB) {
      deb("dvpGetAndDecodeVideoPacketHeader: ERROR - too big currMBNum.\n");
      goto headerFailure;
   }
   
   /* quant can not be zero */
   if(header.quant == 0) {
      goto headerFailure;
   }

   if (header.fHEC) {

      if (header.time_base_incr < 0 || header.time_base_incr > 60) {
         if (bitErrorIndication) {
            goto headerFailure;
         }
      }

      /* fcode can have only the valid values 1..7 */
      if (header.coding_type == VDX_VOP_TYPE_P) 
         if (header.fcode_forward == 0) {
            goto headerFailure;
         }

      if (!inParam->fVOPHeaderCorrupted) {

         if ((inParam->pictParam->pictureType != header.coding_type) ||
            (inParam->pictParam->time_base_incr != header.time_base_incr) ||
            (inParam->pictParam->time_inc != header.time_inc)) {
            deb("dvpGetAndDecodeVideoPacketHeader: ERROR - Parameter change VOP<->VP header.\n");
            goto headerFailure;
         }
      }
   }
   if (inParam->fVOPHeaderCorrupted) {
      /* Get the coding type parameter since it is needed in the concealment before the other updates */
      if (header.fHEC) {
         inParam->pictParam->pictureType = header.coding_type;
      }
   }
   if (header.currMBNum != inOutParam->currMBNum) {
      if ( header.currMBNum > inOutParam->currMBNum && bitErrorIndication == 0) {

         fLostSegment = TRUE;
         goto headerFailure;

      } else if (header.currMBNum < inOutParam->currMBNum) {
         deb("dvpGetAndDecodeVideoPacketHeader: ERROR - MB counting is out of sync.\n");
         goto headerFailure;
      }
   }

   /*
    * Update parameters
    */

   inOutParam->currMBNum = header.currMBNum;
   inOutParam->quant = header.quant;

   if (inParam->fVOPHeaderCorrupted) {
      if (header.fHEC) {
         inParam->pictParam->mod_time_base += inParam->pictParam->time_base_incr = header.time_base_incr;
         
         inParam->pictParam->time_inc = header.time_inc;

         inOutParam->frameNum = (int) ((inParam->pictParam->mod_time_base + 
            ((double) header.time_inc) / ((double) inParam->pictParam->time_increment_resolution)) *
            30.0 + 0.001);

         inParam->pictParam->tr = inOutParam->frameNum % 256;
         inParam->pictParam->pictureType = header.coding_type;

         inParam->pictParam->intra_dc_vlc_thr = header.intra_dc_vlc_thr;

         if (header.coding_type == VDX_VOP_TYPE_P)
             if (inParam->pictParam->fcode_forward != header.fcode_forward) {
                 /* Initialize once to count parameters for the mvc module */
                 int r_size, scale_factor;
                 
                 inParam->pictParam->fcode_forward = header.fcode_forward;

                 inOutParam->mvcData->f_code = inParam->pictParam->fcode_forward;
                 r_size = inParam->pictParam->fcode_forward - 1;
                 scale_factor = (1 << r_size);
                 inOutParam->mvcData->range = 160 * scale_factor;
             }
      } else {
         /* seek next PSC, VP start code is not good enough */
         sncRewindAndSeekNewMPEGSync(-1, inBuffer, 0, &error);
         return DGOB_OK_BUT_BIT_ERROR;
      }
   }
   
   if (fLostSegment)
       return DGOB_OK_BUT_BIT_ERROR;
   else
       return DGOB_OK;

   headerFailure:
      sncRewindAndSeekNewMPEGSync(-1, inBuffer, inParam->pictParam->fcode_forward, &error);
     
     if (error && error != ERR_BIB_NOT_ENOUGH_DATA)
         return DGOB_ERR;
      return DGOB_OK_BUT_BIT_ERROR;
}


/* {{-output"dvpGetAndDecodeVideoPacketContents.txt"}} */
/*
 * dvpGetAndDecodeVideoPacketContents
 *    
 *
 * Parameters:
 *    inParam                    input parameters
 *    fGetNewReferenceFrame      non-zero if a new reference frame must be
 *                               requested from the image store, otherwise 0
 *    inOutParam                 input/output parameters, these parameters
 *                               may be modified in the function
 *
 * Function:
 *    This function gets and decodes the contents of a Video Packet
 *    after the header of the VP (either VP header or picture
 *    header) is already got and processed. It works MB-by-MB or if VP
 *    is data partitioned calls the corresponding decoding functions.
 *    Error concealment for the missing (not decodable) MBs in a P-frame
 *    is called.
 *
 * Returns:
 *    >= 0                       the function was successful
 *    < 0                        an error occured
 *
 */

int dvpGetAndDecodeVideoPacketContents(
   const dvpVPInParam_t *inParam,
   int fGetNewReferenceFrame,
   dvpVPInOutParam_t *inOutParam, CMPEG4Transcoder *hTranscoder)
/* {{-output"dvpGetAndDecodeVideoPacketContents.txt"}} */
{
   int16 error = 0;
   int bitErrorIndication = 0;
   dmbPFrameMBInParam_t dpmbi;
   dmbPFrameMBInOutParam_t dpmbio;
   dmbIFrameMBInParam_t dimbi;
   dmbIFrameMBInOutParam_t dimbio;
   int yPosInMBs, xPosInMBs = 0;
   bibBuffer_t *inBuffer;
   bibBuffer_t 
       *outBuffer;        /* Output bit buffer instance */
   
   bibBufferEdit_t              
       *bufEdit; 
   
   int colorEffect; 
   TBool getDecodedFrame;


   int fSegmentCorrupted = 0;

   int dmbRetValue;
   int sncCode = SNC_NO_SYNC;
   int lastMBNumInVP = 0;
   int startMB = inOutParam->currMBNum;

   SOH_DEFINE(blcDiffMB_t, pDiffMB); /* Storage for the previous difference blocks */
  
   SOH_ALLOC(blcDiffMB_t, pDiffMB);

   if (pDiffMB == NULL) {
      deb("dvpGetAndDecodeVideoPacketContents: SOH_ALLOC failed.\n");
      goto unexpectedError;
   }

   pDiffMB->cbpy = 0;

   inBuffer = inParam->inBuffer;
   outBuffer = inParam->outBuffer;
   bufEdit = inParam->bufEdit;
   colorEffect = inParam->iColorEffect;
   getDecodedFrame = inParam->iGetDecodedFrame;

   /* If the reference frame changed */
   if (fGetNewReferenceFrame) {
      vdeIms_t *store = inOutParam->imageStore;
      vdeImsItem_t *imsItem;
      vdeImb_t *imb;
      int width, height;

     if (vdeImsGetReference(store, VDEIMS_REF_LATEST, 0, &imsItem) < 0) {
        deb("dvpGetAndDecodeVideoPacketContents: ERROR - vdeImsGetReference "
           "failed.\n");
        goto unexpectedError;
     }

      /* If no reference frame available */
      if (!imsItem) {
         /* Treat the situation like a decoding error.*/
         deb("dvpGetAndDecodeVideoPacketContents: Warning - no reference frame "
            "available.\n");
         goto headerFailure;
      }

      if (vdeImsStoreItemToImageBuffer(imsItem, &imb) < 0) {
         deb("dvpGetAndDecodeVideoPacketContents: ERROR - vdeImsStoreItemToImageBuffer "
            "failed.\n");
         goto unexpectedError;
      }

      if (vdeImbYUV(imb, &inOutParam->refY, &inOutParam->refU, 
         &inOutParam->refV, &width, &height) < 0) {
         deb("dvpGetAndDecodeVideoPacketContents: ERROR - vdeImbYUV "
            "failed.\n");
         goto unexpectedError;
      }
   }

   xPosInMBs = (inOutParam->currMBNum % inParam->pictParam->numMBsInMBLine);
   yPosInMBs = (inOutParam->currMBNum / inParam->pictParam->numMBsInMBLine);

   /* if VOP header corrupted and first VP -> exit */
   if(inParam->fVOPHeaderCorrupted && inOutParam->currMBNum==0) {
      fSegmentCorrupted = 1;
      goto exitWhenVOPHeaderCorrupted;
   }

   /* in case of an I-VOP */
   if (inParam->pictParam->pictureType == VDX_VOP_TYPE_I) {
      dimbi.inBuffer = inBuffer;
      dimbi.outBuffer = outBuffer;
      dimbi.bufEdit = bufEdit;
      dimbi.iColorEffect = colorEffect;
      dimbi.iGetDecodedFrame = getDecodedFrame;

      dimbi.pictParam = inParam->pictParam;

      dimbi.xPosInMBs = xPosInMBs;
      dimbi.yPosInMBs = yPosInMBs;

      dimbio.currMBNum = inOutParam->currMBNum;
      dimbio.currMBNumInVP = 0;

     dimbio.aicData = inOutParam->aicData;

      dimbio.fCodedMBs = inOutParam->fCodedMBs;
      dimbio.numOfCodedMBs = inOutParam->numOfCodedMBs;
      dimbio.quant = inOutParam->quant;
     
      /* YUV pointers */
     {
      int32 yOffset, uvOffset;
   
      yOffset = inParam->pictParam->lumMemWidth * dimbi.yPosInMBs + dimbi.xPosInMBs;
      uvOffset = (inParam->pictParam->lumMemWidth >> 1) * dimbi.yPosInMBs + dimbi.xPosInMBs;

      if ( inOutParam->currPY != NULL )
        {
          dimbio.yMBInFrame = inOutParam->currPY + (yOffset << 4);
          dimbio.uBlockInFrame = inOutParam->currPU + (uvOffset << 3);
          dimbio.vBlockInFrame = inOutParam->currPV + (uvOffset << 3);
        }
      else
        {
          dimbio.yMBInFrame = NULL;
          dimbio.uBlockInFrame = NULL;
          dimbio.vBlockInFrame = NULL;
        }
     }
   }
   /* in case of a P-VOP */
   else {
      dpmbi.inBuffer = inBuffer;
      dpmbi.outBuffer = outBuffer;
      dpmbi.bufEdit = bufEdit;
      dpmbi.iColorEffect = colorEffect;
      dpmbi.iGetDecodedFrame = getDecodedFrame;

      dpmbi.pictParam = inParam->pictParam;

      dpmbi.xPosInMBs = xPosInMBs;
      dpmbi.yPosInMBs = yPosInMBs;

      dpmbi.refY = inOutParam->refY;
      dpmbi.refU = inOutParam->refU;
      dpmbi.refV = inOutParam->refV;
      dpmbi.currPY = inOutParam->currPY;
      dpmbi.currPU = inOutParam->currPU;
      dpmbi.currPV = inOutParam->currPV;

      dpmbio.currMBNum = inOutParam->currMBNum;
      dpmbio.currMBNumInVP = 0;

      dpmbio.fCodedMBs = inOutParam->fCodedMBs;
      dpmbio.numOfCodedMBs = inOutParam->numOfCodedMBs;
      dpmbio.quant = inOutParam->quant;

     dpmbio.aicData = inOutParam->aicData;

     dpmbio.mvcData = inOutParam->mvcData;
      dpmbio.diffMB = pDiffMB;

      /* YUV pointers */
     {
      int32 yOffset, uvOffset;
   
      yOffset = inParam->pictParam->lumMemWidth * dpmbi.yPosInMBs + dpmbi.xPosInMBs;
      uvOffset = (inParam->pictParam->lumMemWidth >> 1) * dpmbi.yPosInMBs + dpmbi.xPosInMBs;

      if ( inOutParam->currPY != NULL )
        {
          dpmbio.yMBInFrame = inOutParam->currPY + (yOffset << 4);
          dpmbio.uBlockInFrame = inOutParam->currPU + (uvOffset << 3);
          dpmbio.vBlockInFrame = inOutParam->currPV + (uvOffset << 3);
        }
      else
        {
          dimbio.yMBInFrame = NULL;
          dimbio.uBlockInFrame = NULL;
          dimbio.vBlockInFrame = NULL;
        }
     }
   }

   /* Decode multiple Macroblocks until a resync_marker is found or error occurs */
   while (inParam->pictParam->numMBsInGOB != inOutParam->currMBNum && 
         sncCode != SNC_VIDPACK && sncCode != SNC_VOP) {

       /* MVE */
       hTranscoder->BeginOneMB((inParam->pictParam->pictureType == VDX_VOP_TYPE_I) ? dimbio.currMBNum : dpmbio.currMBNum );
       
     /* decode an I-frame MB */
      if (inParam->pictParam->pictureType == VDX_VOP_TYPE_I) {
         
         if(inParam->pictParam->data_partitioned) {

            dmbRetValue = dmbsGetAndDecodeIMBsDataPartitioned(&dimbi, &dimbio,
               inOutParam->quantParams, hTranscoder);

            if (dmbRetValue < 0)
               goto unexpectedError;
            
            inOutParam->currMBNum = dimbio.currMBNum;

            /* Video Packet corrupted */
            if ( fSegmentCorrupted )
               break;

         } else {

            dmbRetValue = dmbGetAndDecodeIFrameMB(&dimbi, &dimbio, 1, hTranscoder);
            
            if (dmbRetValue < 0)
               goto unexpectedError;
            else if (dmbRetValue == DMB_BIT_ERR ) {
               /* Video Packet corrupted */
               fSegmentCorrupted = 1;
               break;
            }

            /* Store quantizer */
            inOutParam->quantParams[dimbio.currMBNum] = dimbio.quant;

            /* increment the frame pointers and MB counters */
            dimbio.currMBNum++;
            dimbio.currMBNumInVP++;

            if ( dimbio.yMBInFrame != NULL )
            {
                dimbio.yMBInFrame += 16;
                dimbio.uBlockInFrame += 8;
                dimbio.vBlockInFrame += 8;
            }
            dimbi.xPosInMBs++;

            if (dimbi.xPosInMBs == inParam->pictParam->numMBsInMBLine) {
              if ( dimbio.yMBInFrame != NULL )
                {
                   dimbio.yMBInFrame += 15 * inParam->pictParam->lumMemWidth;
                   dimbio.uBlockInFrame += 7 * (inParam->pictParam->lumMemWidth >> 1);
                   dimbio.vBlockInFrame += 7 * (inParam->pictParam->lumMemWidth >> 1);
                }
               dimbi.xPosInMBs = 0;
               dimbi.yPosInMBs++;
            }

            inOutParam->currMBNum = dimbio.currMBNum;
         }

         /* decode a P-frame MB */
     } else {

         if(inParam->pictParam->data_partitioned) {

            dmbRetValue = dmbsGetAndDecodePMBsDataPartitioned(&dpmbi, &dpmbio,
               inOutParam->quantParams, hTranscoder);

            if (dmbRetValue < 0)
               goto unexpectedError;

            inOutParam->currMBNum = dpmbio.currMBNum;
            lastMBNumInVP = dpmbio.currMBNumInVP;

            /* Video Packet corrupted */
            if ( fSegmentCorrupted )
               break;

         } else {

            dmbRetValue = dmbGetAndDecodePFrameMB(&dpmbi, &dpmbio, 1, hTranscoder);
            
            if (dmbRetValue < 0)
               goto unexpectedError;
            else if (dmbRetValue == DMB_BIT_ERR ) {
               /* Video Packet corrupted */
               fSegmentCorrupted = 1;
               break;
            }
            
            /* Store quantizer */
            inOutParam->quantParams[dpmbio.currMBNum] = dpmbio.quant;

            /* increment the frame pointers and MB counters */
            dpmbio.currMBNum++;
            dpmbio.currMBNumInVP++;

            if ( dpmbio.yMBInFrame != NULL )
              {
                dpmbio.yMBInFrame += 16;
                dpmbio.uBlockInFrame += 8;
                dpmbio.vBlockInFrame += 8;
              }
            dpmbi.xPosInMBs++;

            if (dpmbi.xPosInMBs == inParam->pictParam->numMBsInMBLine) {
                if ( dpmbio.yMBInFrame != NULL )
                  {
                   dpmbio.yMBInFrame += 15 * inParam->pictParam->lumMemWidth;
                   dpmbio.uBlockInFrame += 7 * (inParam->pictParam->lumMemWidth >> 1);
                   dpmbio.vBlockInFrame += 7 * (inParam->pictParam->lumMemWidth >> 1);
                  }
               dpmbi.xPosInMBs = 0;
               dpmbi.yPosInMBs++;
            }

            inOutParam->currMBNum = dpmbio.currMBNum;
         }
     }

     /* check for a resync_marker */
     sncCode = sncCheckMpegSync(inBuffer, inParam->pictParam->fcode_forward, &error);
     
     /* If sncCheckMpegSync failed */
     if (error && error != ERR_BIB_NOT_ENOUGH_DATA) {
         deb1p("dvpGetAndDecodeVideoPacketContents: ERROR - sncCheckSync returned %d.\n", error);
         goto unexpectedError;
         
     } else 
         /* If buffer ends (in one-frame-per-one-buffer case) */
         if (sncCode == SNC_EOB ||
             sncCode == SNC_STUFFING ||
             error == ERR_BIB_NOT_ENOUGH_DATA) {
             break; 
         }
     
   }


// <--

   inOutParam->numOfCodedMBs = (inParam->pictParam->pictureType != VDX_VOP_TYPE_I) ? 
        dpmbio.numOfCodedMBs : dimbio.numOfCodedMBs;
   
   if (fSegmentCorrupted) {
      u_int32 nextVPBitPos;

      /* find the next resync marker, to get the number of MBs in the current VP */
      sncCode = sncRewindAndSeekNewMPEGSync(-1, inBuffer, inParam->pictParam->fcode_forward, &error);
      if (error && error != ERR_BIB_NOT_ENOUGH_DATA) {
         goto unexpectedError;
      } else if (sncCode == SNC_EOB) {
         goto exitFunction;
      }

      nextVPBitPos = bibNumberOfFlushedBits(inParam->inBuffer);

      /* if the lastMBNumInVP was not yet read */
      if (inParam->pictParam->pictureType == VDX_VOP_TYPE_I || 
         !inParam->pictParam->data_partitioned || 
         lastMBNumInVP == 0) { 

         /* if VP header is found: read it, and get last MB number in current vop */
         if (sncCode == SNC_VIDPACK) {
            vdxVideoPacketHeader_t header;
            vdxGetVideoPacketHeaderInputParam_t vdxParam;
            int retValue;
            
            vdxParam.fcode_forward = inParam->pictParam->fcode_forward;
            vdxParam.time_increment_resolution = inParam->pictParam->time_increment_resolution;
            vdxParam.numOfMBs = inParam->pictParam->numMBsInGOB;
            
            retValue = vdxGetVideoPacketHeader(inParam->inBuffer, &vdxParam, &header, &bitErrorIndication);
            if (retValue < 0) {
               goto unexpectedError;
            } else if (retValue == VDX_OK_BUT_BIT_ERROR) {
               /* If bit error occurred */
               goto headerFailure;
            }
            
            /* rewind the bits to the beginning of the VP header */
            bibRewindBits(bibNumberOfFlushedBits(inParam->inBuffer) - nextVPBitPos,
               inParam->inBuffer, &error);
            
            lastMBNumInVP = header.currMBNum;
            
         } else {
            lastMBNumInVP = inParam->pictParam->numMBsInGOB;
         }
      }

      /* if possibly the next VP header is damaged (it gives MB index which is smaller than the start of the current one */
      if (lastMBNumInVP <= startMB || lastMBNumInVP > inParam->pictParam->numMBsInGOB)
         goto exitFunction;

      /* if the MB counting due to error overran the next VP header's value */
      if (inOutParam->currMBNum > lastMBNumInVP)
         inOutParam->currMBNum = VDC_MAX(lastMBNumInVP-3,startMB);
      
      /* if not all the MBs have been predicted due to error */
      else if (inOutParam->currMBNum < lastMBNumInVP)
         inOutParam->currMBNum = VDC_MAX(inOutParam->currMBNum-1,startMB);

      
      inOutParam->currMBNum = lastMBNumInVP;
   }

   exitFunction:


   exitWhenVOPHeaderCorrupted:

   if (!fSegmentCorrupted) {
      SOH_DEALLOC(pDiffMB);
      return DGOB_OK;
   }
   else {
      SOH_DEALLOC(pDiffMB);
      return DGOB_OK_BUT_BIT_ERROR;
   }

   headerFailure:
      SOH_DEALLOC(pDiffMB);
      sncRewindAndSeekNewMPEGSync(-1, inBuffer, 0, &error);
      if (error && error != ERR_BIB_NOT_ENOUGH_DATA)
         return DGOB_ERR;
      return DGOB_OK_BUT_BIT_ERROR;

   unexpectedError:
      SOH_DEALLOC(pDiffMB);
      return DGOB_ERR;
}
// End of File