videoeditorengine/avcedit/src/dpb.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:
*
*/


#include <string.h>
#include "globals.h"
#include "framebuffer.h"
#include "dpb.h"



/*
 *
 * dpbOpen:
 *
 * Parameters:
 *
 * Function:
 *      Allocate DPB.
 *
 * Returns:
 *      Pointer to dpb or NULL
 *
 */
dpb_s *dpbOpen()
{
  dpb_s *dpb;

  dpb = (dpb_s *)User::Alloc(sizeof(dpb_s));

  if (dpb != NULL)
    memset(dpb, 0, sizeof(dpb_s));

  return dpb;
}


/*
 *
 * dpbClose:
 *
 * Parameters:
 *      dpb                   DPB object
 *
 * Function:
 *      Deinitialize DPB.
 *
 * Returns:
 *      -
 *
 */
void dpbClose(dpb_s *dpb)
{
  int i;

  for (i = 0; i < DPB_MAX_SIZE; i++)
    frmCloseRef(dpb->buffers[i]);

  User::Free(dpb);
}


/*
 *
 * dpbSetSize:
 *
 * Parameters:
 *      dpb                   DPB object
 *      dpbSize               New DPB size
 *
 * Function:
 *      Set DPB size in frames.
 *
 * Returns:
 *      -
 *
 */
void dpbSetSize(dpb_s *dpb, int dpbSize)
{
  int i;

  /* If new DPB size is smaller than old, close any unneeded frame buffers */
  for (i = dpbSize; i < dpb->size; i++) {
    frmCloseRef(dpb->buffers[i]);
    dpb->buffers[i] = NULL;
  }

  dpb->size = min(dpbSize, DPB_MAX_SIZE);
}


/*
 *
 * dpbGetNextOutputPic:
 *
 * Parameters:
 *      dpb                   DPB object
 *      dpbHasIDRorMMCO5      Set upon return if IDR picture or picture with
 *                            MMCO5 command is found in DPB
 *
 * Function:
 *      Find next frame to output (frame with the lowest POC).
 *      Search is started from the last active frame in dpb and is
 *      stopped if all frames have been checked or IDR picture or
 *      picture with MMCO5 is found.
 *
 * Returns:
 *      Framebuffer with the lowest POC or 0 if DPB is empty
 *
 */
frmBuf_s *dpbGetNextOutputPic(dpb_s *dpb, int *dpbHasIDRorMMCO5)
{
  frmBuf_s * tmpFrm;
  int i;

  tmpFrm = 0;

  /* Find first output pic in decoding order */
  for (i = dpb->fullness-1; i >= 0; i--) {
    if (dpb->buffers[i]->forOutput) {
      tmpFrm = dpb->buffers[i];
      break;
    }
  }

  *dpbHasIDRorMMCO5 = 0;

  /* Find picture with lowest poc. Stop search if IDR or MMCO5 is found */
  for (; i >= 0; i--) {
    if (dpb->buffers[i]->isIDR || dpb->buffers[i]->hasMMCO5) {
      *dpbHasIDRorMMCO5 = 1;
      break;
    }
    if (dpb->buffers[i]->forOutput && dpb->buffers[i]->poc < tmpFrm->poc)
      tmpFrm = dpb->buffers[i];
  }

  return tmpFrm;
}


/*
 *
 * dpbStorePicture:
 *
 * Parameters:
 *      dpb                   DPB object
 *      currPic               Current picture
 *      outputQueue           Output queue
 *
 * Function:
 *      - Remove unused frames (non-reference, non-output frames) from the DPB.
 *      - If there is room in the DPB, put picture to DPB.
 *      - If there is no room in DPB, put pictures to output queue
 *        until frame is available.
 *
 * Returns:
 *      The number of pictures in output queue or negative value for error.
 *
 */
int dpbStorePicture(dpb_s *dpb, frmBuf_s *currPic, frmBuf_s *outputQueue[])
{
  frmBuf_s *tmpRemList[DPB_MAX_SIZE];
  frmBuf_s *tmpFrm;
  int numFrm, numRemFrm;
  int i;
  int freeBufferFound;
  int numOutput;
  int dpbHasIDRorMMCO5;

  /*
   * If the current picture is a reference picture and DPB is already full of
   * reference pictures, we cannot insert current picture to DPB. Therefore,
   * we force one reference picture out of DPB to make space for current
   * picture. This situation can only happen with corrupted bitstreams.
   */
  if (currPic->refType != FRM_NON_REF_PIC &&
      (dpb->numShortTermPics+dpb->numLongTermPics) == dpb->size)
  {
    if (dpb->numLongTermPics == dpb->size)
      dpb->buffers[dpb->fullness-1]->refType = FRM_NON_REF_PIC;
    else
      dpbMarkLowestShortTermPicAsNonRef(dpb);
  }

  /*
   * Remove unused frames from dpb
   */

  numFrm = 0;
  numRemFrm = 0;
  for (i = 0; i < dpb->fullness; i++) {
    if (dpb->buffers[i]->refType != FRM_NON_REF_PIC || dpb->buffers[i]->forOutput) {
      dpb->buffers[numFrm] = dpb->buffers[i];
      numFrm++;
    }
    else {
      /* Put unsused pics to temporary list */
      tmpRemList[numRemFrm] = dpb->buffers[i];
      numRemFrm++;
    }
  }

  /* Copy unused pics after active pics */
  for (i = 0; i < numRemFrm; i++)
    dpb->buffers[numFrm+i] = tmpRemList[i];

  dpb->fullness = numFrm;


  /*
   * Try to store current pic to dpb. If there is no room in dpb, we have to
   * output some pictures to make room.
   */

  /* Case 1: if current pic is unused, it won't be stored in dpb */
  if (currPic->refType == FRM_NON_REF_PIC && !currPic->forOutput) {
    return 0;
  }

  /* Case 2: if there is space left in dpb, store current pic */
  if (dpb->fullness < dpb->size) {

    tmpFrm = dpb->buffers[dpb->fullness];   /* Unused frame */

    tmpFrm = frmMakeRefFrame(currPic, tmpFrm);
    if (tmpFrm == NULL)
      return DPB_ERR_MEM_ALLOC;

    /* Move frames one position toward end of the list */
    for (i = dpb->fullness; i > 0; i--)
      dpb->buffers[i] = dpb->buffers[i-1];

    /* Always insert new frame to the beginning of the list */
    dpb->buffers[0] = tmpFrm;
    dpb->fullness++;

    if (currPic->refType == FRM_SHORT_TERM_PIC)
      dpb->numShortTermPics++;
    else if (currPic->refType == FRM_LONG_TERM_PIC)
      dpb->numLongTermPics++;

    /* Current picture is marked unused */
    currPic->forOutput = 0;
    currPic->refType   = FRM_NON_REF_PIC;

    /* No pictures in ouput queue */
    return 0;
  }

  /* Case 3: output pictures with bumping process until there is space in dpb or
  *  current pic is non-reference and has lowest poc */
  freeBufferFound = 0;
  numOutput       = 0;
  while (!freeBufferFound) {

    /* Next picture to output is a picture having smallest POC in DPB */
    tmpFrm = dpbGetNextOutputPic(dpb, &dpbHasIDRorMMCO5);

    /* Current picture is output if it is non-reference picture having */
    /* smaller POC than any of the pictures in DPB                     */
    if (currPic->refType == FRM_NON_REF_PIC &&
        (tmpFrm == 0 || (!dpbHasIDRorMMCO5 && currPic->poc < tmpFrm->poc)))
    {
      /* Output current picture  */
      currPic->forOutput = 0;
      outputQueue[numOutput] = currPic;
      numOutput++;
      freeBufferFound = 1;
    }
    else {
      /* Output picture that we got from DPB */
      tmpFrm->forOutput = 0;
      outputQueue[numOutput] = tmpFrm;
      numOutput++;
      if (tmpFrm->refType == FRM_NON_REF_PIC)
        freeBufferFound = 1;
    }
  }

  return numOutput;
}


/*
 *
 * dpbUpdatePicNums:
 *
 * Parameters:
 *      dpb                   DPB object
 *      frameNum              Current picture frame number
 *      maxFrameNum           Maximum frame number
 *
 * Function:
 *      Compute picture numbers for all pictures in DPB.
 *
 * Returns:
 *      -
 *
 */
void dpbUpdatePicNums(dpb_s *dpb, int32 frameNum, int32 maxFrameNum)
{
  int i;
  frmBuf_s **refPicList;

  refPicList = dpb->buffers;

  for (i = 0; i < dpb->fullness; i++) {
    if (refPicList[i]->refType == FRM_SHORT_TERM_PIC) {
      /* Short term pictures */
      if (refPicList[i]->frameNum > frameNum)
        refPicList[i]->picNum = refPicList[i]->frameNum - maxFrameNum;
      else
        refPicList[i]->picNum = refPicList[i]->frameNum;
    }
    else if (refPicList[i]->refType == FRM_LONG_TERM_PIC)
      /* Long term pictures */
      refPicList[i]->longTermPicNum = refPicList[i]->longTermFrmIdx;
  }
}


/*
 *
 * dpbMarkAllPicsAsNonRef:
 *
 * Parameters:
 *      dpb                   DPB object
 *
 * Function:
 *      Mark all picrures as non-reference pictures.
 *
 * Returns:
 *      -
 *
 */
void dpbMarkAllPicsAsNonRef(dpb_s *dpb)
{
  int i;

  /* Mark all pictures as not used for reference */
  for (i = 0; i < dpb->fullness; i++)
    dpb->buffers[i]->refType = FRM_NON_REF_PIC;

  dpb->numShortTermPics = 0;
  dpb->numLongTermPics  = 0;
}


/*
 *
 * dpbMarkLowestShortTermPicAsNonRef:
 *
 * Parameters:
 *      dpb                   DPB object
 *
 * Function:
 *      Mark short-term picture having lowest picture number as
 *      non-reference pictures.
 *
 * Returns:
 *      -
 *
 */
void dpbMarkLowestShortTermPicAsNonRef(dpb_s *dpb)
{
  int picIdx;
  int i;

  /* Find short term pic with lowest picNum */
  picIdx = -1;
  for (i = dpb->fullness-1; i >= 0; i--) {
    if (dpb->buffers[i]->refType == FRM_SHORT_TERM_PIC &&
        (picIdx < 0 || dpb->buffers[i]->picNum < dpb->buffers[picIdx]->picNum))
      picIdx = i;
  }

  /* Mark short term pic with lowest picNum as not reference picture */
  if (picIdx >= 0) {
    dpb->buffers[picIdx]->refType = FRM_NON_REF_PIC;
    dpb->numShortTermPics--;
  }
}


/*
 *
 * dpbMarkShortTermPicAsNonRef:
 *
 * Parameters:
 *      dpb                   DPB object
 *      picNum                Picture number
 *
 * Function:
 *      Mark short-term picture having picture number picNum as
 *      non-reference picture.
 *
 * Returns:
 *      DPB_OK or DPB_ERR_PICTURE_NOT_FOUND
 *
 */
int dpbMarkShortTermPicAsNonRef(dpb_s *dpb, int32 picNum)
{
  int i;

  for (i = 0; i < dpb->fullness; i++) {
    if (dpb->buffers[i]->refType == FRM_SHORT_TERM_PIC &&
        dpb->buffers[i]->picNum == picNum)
    {
      dpb->buffers[i]->refType = FRM_NON_REF_PIC;
      dpb->numShortTermPics--;
      return DPB_OK;
    }
  }

  return DPB_ERR_PICTURE_NOT_FOUND;
}


/*
 *
 * dpbMarkLongTermPicAsNonRef:
 *
 * Parameters:
 *      dpb                   DPB object
 *      longTermPicNum        Long-term picture number
 *
 * Function:
 *      Mark long-term picture having long-term picture number longTermPicNum
 *      as non-reference picture.
 *
 * Returns:
 *      DPB_OK or DPB_ERR_PICTURE_NOT_FOUND
 *
 */
int dpbMarkLongTermPicAsNonRef(dpb_s *dpb, int longTermPicNum)
{
  int i;

  for (i = 0; i < dpb->fullness; i++) {
    if (dpb->buffers[i]->refType == FRM_LONG_TERM_PIC &&
        dpb->buffers[i]->longTermPicNum == longTermPicNum)
    {
      dpb->buffers[i]->refType = FRM_NON_REF_PIC;
      dpb->numLongTermPics--;
      return DPB_OK;
    }
  }

  return DPB_ERR_PICTURE_NOT_FOUND;
}


/*
 *
 * dpbVerifyLongTermFrmIdx:
 *
 * Parameters:
 *      dpb                   DPB object
 *      longTermFrmIdx        Long-term frame index
 *
 * Function:
 *      If there is a long-term picture having long term frame index
 *      longTermFrmIdx, mark that picture as non-reference picture.
 *
 * Returns:
 *      -
 *
 */
void dpbVerifyLongTermFrmIdx(dpb_s *dpb, int longTermFrmIdx)
{
  int i;

  /* Check if longTermFrmIdx is already in use */
  for (i = 0; i < dpb->fullness; i++) {
    if (dpb->buffers[i]->refType == FRM_LONG_TERM_PIC &&
        dpb->buffers[i]->longTermFrmIdx == longTermFrmIdx)
    {
      dpb->buffers[i]->refType = FRM_NON_REF_PIC;
      dpb->numLongTermPics--;
      break;
    }
  }
}


/*
 *
 * dpbMarkShortTermPicAsLongTerm:
 *
 * Parameters:
 *      dpb                   DPB object
 *      picNum                Picture number
 *      longTermFrmIdx        Long-term frame index
 *
 * Function:
 *      Mark short-term picture having picture number picNum as long-term
 *      picture having long-term frame index longTermFrmIdx.
 *
 * Returns:
 *      DPB_OK or DPB_ERR_PICTURE_NOT_FOUND
 *
 */
int dpbMarkShortTermPicAsLongTerm(dpb_s *dpb, int32 picNum, int longTermFrmIdx)
{
  int i;

  /* To avoid duplicate of longTermFrmIdx */
  dpbVerifyLongTermFrmIdx(dpb, longTermFrmIdx);

  /* Mark pic with picNum as long term and assign longTermFrmIdx to it */
  for (i = 0; i < dpb->fullness; i++) {
    if (dpb->buffers[i]->refType == FRM_SHORT_TERM_PIC &&
        dpb->buffers[i]->picNum == picNum)
    {
      dpb->buffers[i]->refType = FRM_LONG_TERM_PIC;
      dpb->buffers[i]->longTermFrmIdx = longTermFrmIdx;
      dpb->numShortTermPics--;
      dpb->numLongTermPics++;
      return DPB_OK;
    }
  }

  return DPB_ERR_PICTURE_NOT_FOUND;
}


/*
 *
 * dpbSetMaxLongTermFrameIdx:
 *
 * Parameters:
 *      dpb                     DPB object
 *      maxLongTermFrmIdxPlus1  Maximum long-term frame index plus 1
 *
 * Function:
 *      Set maximum long-term frame index. All long-term pictures having
 *      bigger long-term frame index than maxLongTermFrmIdxPlus1-1 are
 *      marked as non-reference pictures.
 *
 * Returns:
 *      -
 *
 */
void dpbSetMaxLongTermFrameIdx(dpb_s *dpb, int maxLongTermFrmIdxPlus1)
{
  int i;

  for (i = 0; i < dpb->fullness; i++) {
    if (dpb->buffers[i]->refType == FRM_LONG_TERM_PIC &&
        dpb->buffers[i]->longTermFrmIdx > maxLongTermFrmIdxPlus1-1)
    {
      dpb->buffers[i]->refType = FRM_NON_REF_PIC;
      dpb->numLongTermPics--;
    }
  }

  dpb->maxLongTermFrameIdx = maxLongTermFrmIdxPlus1 - 1;
}