
/*!
 *************************************************************************************
 * \file errdo.c
 *
 * \brief
 *    Contains functions that implement the "decoders in the encoder" concept for the
 *    rate-distortion optimization with losses.
 * \date
 *    October 22nd, 2001
 *
 * \author
 *    Main contributors (see contributors.h for copyright, address and
 *    affiliation details)
 *    - Dimitrios Kontopodis                    <dkonto@eikon.tum.de>
 *    Code revamped July 2008 by:
 *    - Peshala Pahalawatta (ppaha@dolby.com)
 *    - Alexis Tourapis (atour@dolby.com)
 *************************************************************************************
 */

#include "global.h"
#include "memalloc.h"
#include "refbuf.h"
#include "image.h"
#include "errdo.h"
#include "errdo_mc_prediction.h"
#include "md_common.h"

static StorablePicture* find_nearest_ref_picture(DecodedPictureBuffer *p_Dpb, int poc);
static void copy_conceal_mb (Macroblock *currMB, StorablePicture *enc_pic, int decoder, int mb_error, StorablePicture* refPic);
static void get_predicted_mb(Macroblock *currMB, StorablePicture *enc_pic, int decoder);
static void add_residue     (Macroblock *currMB, StorablePicture *enc_pic, int decoder, int pl, int block8x8, int x_size, int y_size);
static void Build_Status_Map(VideoParameters *p_Vid, InputParameters *p_Inp, byte **s_map);

extern void DeblockFrame(VideoParameters *p_Vid, imgpel **, imgpel ***);

/*!
**************************************************************************************
* \brief 
*      Allocate memory for error resilient RDO.  
**************************************************************************************
*/
int allocate_errdo_mem(VideoParameters *p_Vid, InputParameters *p_Inp)
{
  int memory_size = 0;

  p_Vid->p_decs   = (Decoders *) malloc(sizeof(Decoders));

  p_Vid->p_decs->dec_mb_pred         = NULL;
  p_Vid->p_decs->dec_mbY_best        = NULL;
  p_Vid->p_decs->dec_mb_pred_best8x8 = NULL;
  p_Vid->p_decs->dec_mbY_best8x8     = NULL;
  p_Vid->p_decs->res_img             = NULL;
  memory_size += get_mem3Dpel(&p_Vid->p_decs->dec_mb_pred, p_Inp->NoOfDecoders, MB_BLOCK_SIZE, MB_BLOCK_SIZE);
  memory_size += get_mem3Dpel(&p_Vid->p_decs->dec_mbY_best, p_Inp->NoOfDecoders, MB_BLOCK_SIZE, MB_BLOCK_SIZE);
  memory_size += get_mem4Dpel(&p_Vid->p_decs->dec_mbY_best8x8, 2, p_Inp->NoOfDecoders, MB_BLOCK_SIZE, MB_BLOCK_SIZE);
  memory_size += get_mem4Dpel(&p_Vid->p_decs->dec_mb_pred_best8x8, 2, p_Inp->NoOfDecoders, MB_BLOCK_SIZE, MB_BLOCK_SIZE);
  memory_size += get_mem3Dint(&p_Vid->p_decs->res_img, MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE);
  
  return memory_size;
}

/*!
**************************************************************************************
* \brief 
*      free memory of error resilient RDO.  
**************************************************************************************
*/
void free_errdo_mem(VideoParameters *p_Vid)
{
  if (p_Vid->p_decs->res_img)
  {
    free_mem3Dint(p_Vid->p_decs->res_img);
  }
  if (p_Vid->p_decs->dec_mb_pred)
  {
    free_mem3Dpel(p_Vid->p_decs->dec_mb_pred);
  }
  if (p_Vid->p_decs->dec_mbY_best)
  {
    free_mem3Dpel(p_Vid->p_decs->dec_mbY_best);
  }
  if (p_Vid->p_decs->dec_mbY_best8x8)
  {
    free_mem4Dpel(p_Vid->p_decs->dec_mbY_best8x8);
  }
  if (p_Vid->p_decs->dec_mb_pred_best8x8)
  {
    free_mem4Dpel(p_Vid->p_decs->dec_mb_pred_best8x8);
  }

  if ( p_Vid->p_decs != NULL )
    free( p_Vid->p_decs );
}

/*!
**************************************************************************************
* \brief 
*      Decodes one macroblock for error resilient RDO.  
*    Currently does not support:
*    1) B coded pictures
*    2) Chroma components
*    3) Potential error propagation due to intra prediction
*    4) Field coding
**************************************************************************************
*/
void decode_one_mb (Macroblock* currMB, StorablePicture *enc_pic, int decoder)
{
  int i0, j;
  Slice *currSlice = currMB->p_slice;
  VideoParameters *p_Vid = currMB->p_Vid;
  imgpel** curComp;
  imgpel** oldComp;

  //printf("currMB->mb_type %d\n", currMB->mb_type);
  if (currMB->mb_type > P8x8) //Intra MB
  {
    curComp = &enc_pic->p_dec_img[0][decoder][currMB->pix_y];
    oldComp = &enc_pic->p_curr_img[currMB->pix_y];
    i0 = currMB->pix_x;
    copy_image_data_16x16(curComp, oldComp, i0, i0);
  }
  else if ((currMB->mb_type == 0) && (currSlice->slice_type != B_SLICE))
  {
    get_predicted_mb(currMB, enc_pic, decoder);
    curComp = &enc_pic->p_dec_img[0][decoder][currMB->pix_y];
    for(j = 0; j < p_Vid->mb_size[0][1]; j++)
    {
      memcpy(&(curComp[j][currMB->pix_x]), &(p_Vid->p_decs->dec_mb_pred[decoder][j][0]), p_Vid->mb_size[0][0] * sizeof(imgpel));
    }
  }
  else 
  {
    get_predicted_mb(currMB, enc_pic, decoder);
    add_residue(currMB, enc_pic, decoder, PLANE_Y, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE);
  }
}

/*!
**************************************************************************************
* \brief 
*      Finds predicted macroblock values 
*   and copies them to currSlice->mb_pred[0][][]
**************************************************************************************
*/
static void get_predicted_mb(Macroblock *currMB, StorablePicture *enc_pic, int decoder)
{
  Slice *currSlice = currMB->p_slice;
  int i,j,k;
  int block_size_x, block_size_y;
  int mv_mode, pred_dir;
  static const byte decode_block_scan[16] = {0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15};
  int block8x8;
  int k_start, k_end, k_inc;

  if (currSlice->slice_type == P_SLICE && !currMB->mb_type)
  {
    block_size_x = MB_BLOCK_SIZE;
    block_size_y = MB_BLOCK_SIZE;
    perform_mc(currMB, decoder, PLANE_Y, enc_pic, LIST_0, 0, 0, enc_pic->motion.ref_idx, 0, 0, block_size_x, block_size_y, 0);
  }
  else if (currMB->mb_type == 1)
  {
    block_size_x = MB_BLOCK_SIZE;
    block_size_y = MB_BLOCK_SIZE;
    pred_dir = currMB->b8x8[0].pdir;   
    perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 1, 1, enc_pic->motion.ref_idx, 0, 0, block_size_x, block_size_y, currMB->b8x8[0].bipred);
  }
  else if (currMB->mb_type == 2)
  {   
    block_size_x = MB_BLOCK_SIZE;
    block_size_y = 8;    

    for (block8x8 = 0; block8x8 < 4; block8x8 += 2)
    {
      pred_dir = currMB->b8x8[block8x8].pdir;
      perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 2, 2, enc_pic->motion.ref_idx, 0, block8x8, block_size_x, block_size_y, currMB->b8x8[block8x8].bipred);
    }
  }
  else if (currMB->mb_type == 3)
  {   
    block_size_x = 8;
    block_size_y = 16;

    for (block8x8 = 0; block8x8 < 2; block8x8++)
    {
      i = block8x8<<1;
      j = 0;      
      pred_dir = currMB->b8x8[block8x8].pdir;
      perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 3, 3, enc_pic->motion.ref_idx, i, j, block_size_x, block_size_y, currMB->b8x8[block8x8].bipred);
    }
  }
  else 
  {
    for (block8x8 = 0; block8x8 < 4; block8x8++)
    {
      mv_mode  = currMB->b8x8[block8x8].mode;
      pred_dir = currMB->b8x8[block8x8].pdir;

      k_start = (block8x8 << 2);
      k_inc = (mv_mode == 5) ? 2 : 1;

      if (mv_mode == 0)
      {
        k_end = k_start + 1;
        block_size_x = 8;
        block_size_y = 8;
      }
      else
      {
        k_end = (mv_mode == 4) ? k_start + 1 : ((mv_mode == 7) ? k_start + 4 : k_start + k_inc + 1);
        block_size_x = ( mv_mode == 5 || mv_mode == 4 ) ? 8 : 4;
        block_size_y = ( mv_mode == 6 || mv_mode == 4 ) ? 8 : 4;
      }
      
      for (k = k_start; k < k_end; k += k_inc)
      {
        i =  (decode_block_scan[k] & 3);
        j = ((decode_block_scan[k] >> 2) & 3);
        perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, mv_mode, mv_mode, enc_pic->motion.ref_idx, i, j, block_size_x, block_size_y, currMB->b8x8[block8x8].bipred);
      }
    }
  }
}


/*!
**************************************************************************************
* \brief 
*      Decodes one 8x8 partition for error resilient RDO.  
*    Currently does not support:
*    1) B coded pictures
*    2) Chroma components
*    3) Potential error propagation due to intra prediction
*    4) Field coding
**************************************************************************************
*/
void decode_one_b8block (Macroblock* currMB, StorablePicture *enc_pic, int decoder, int block8x8, short mv_mode, int pred_dir) 
{
  int i,j,k;
  int block_size_x, block_size_y;
  int i0 = (block8x8 & 0x01)<<3;
  int j0 = (block8x8 >> 1)<<3,   j1 = j0+8;
  char*** ref_idx_buf = enc_pic->motion.ref_idx;
  imgpel **curComp;
  imgpel **oldComp;
  int k_start, k_end, k_inc;
  static const byte decode_block_scan[16] = {0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15};
  Slice *currSlice = currMB->p_slice;

  if (mv_mode > 8)  //Intra
  {
    for(j = j0; j < j1; j++)
    {
      curComp = &enc_pic->p_dec_img[0][decoder][currMB->pix_y];
      oldComp = &enc_pic->p_curr_img[currMB->pix_y];
      memcpy(&(curComp[j][i0]), &(oldComp[j][i0]), sizeof(imgpel)*8);
    }
  }
  else
  {
    k_start = (block8x8 << 2);

    if (mv_mode == 0) //Direct
    {
      k_inc = 1;
      k_end = k_start+1;
      block_size_x = 8;
      block_size_y = 8;
      ref_idx_buf = currSlice->direct_ref_idx;
    }
    else
    {
      k_inc = (mv_mode == 5) ? 2 : 1;
      k_end = (mv_mode == 4) ? k_start + 1 : ((mv_mode == 7) ? k_start + 4 : k_start + k_inc + 1);

      block_size_x = ( mv_mode == 5 || mv_mode == 4 ) ? 8 : 4;
      block_size_y = ( mv_mode == 6 || mv_mode == 4 ) ? 8 : 4;
    }

    for (k = k_start; k < k_end; k += k_inc)
    {
      i =  (decode_block_scan[k] & 3);
      j = ((decode_block_scan[k] >> 2) & 3);
      perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, mv_mode, mv_mode, ref_idx_buf, i, j, block_size_x, block_size_y, currMB->b8x8[block8x8].bipred);
    }        

    add_residue(currMB, enc_pic, decoder, PLANE_Y, block8x8, 8, 8);
  }
}

/*!
**************************************************************************************
* \brief 
*      Add residual to motion predicted block
**************************************************************************************
*/
static void add_residue (Macroblock *currMB, StorablePicture *enc_pic, int decoder, int pl, int block8x8, int x_size, int y_size) 
{
  VideoParameters *p_Vid = currMB->p_Vid;
  int max_pel_value = currMB->p_Vid->max_pel_value_comp[pl];
  int i,j;
  int i0 = (block8x8 & 0x01)<<3, i1 = i0 + x_size;
  int j0 = (block8x8 >> 1)<<3,   j1 = j0 + y_size;

  imgpel **p_dec_img = &enc_pic->p_dec_img[pl][decoder][currMB->pix_y];
  int    **res_img   = p_Vid->p_decs->res_img[0];
  imgpel** mpr       = p_Vid->p_decs->dec_mb_pred[decoder];

  for (j = j0; j < j1; j++)
  {
    for (i = i0; i < i1; i++)
    {
      p_dec_img[j][currMB->pix_x + i] = (imgpel) iClip3(0, max_pel_value, (mpr[j][i] + res_img[j][i])); 
    } 
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Performs the simulation of the packet losses, calls the error concealment funcs
 *    and deblocks the error concealed pictures
 *
 *************************************************************************************
 */
void UpdateDecoders(VideoParameters *p_Vid, InputParameters *p_Inp, StorablePicture *enc_pic)
{
  int k;
  for (k = 0; k < p_Inp->NoOfDecoders; k++)
  {
    Build_Status_Map(p_Vid, p_Inp, enc_pic->mb_error_map[k]); // simulates the packet losses
    p_Vid->error_conceal_picture(p_Vid, enc_pic, k); 
    DeblockFrame (p_Vid, enc_pic->p_dec_img[0][k], NULL);
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Initialize error concealment function
 *    (Currently only copy concealment is implemented. Can extend to other concealment
 *    types when available.)
 *
 *************************************************************************************
 */
void init_error_conceal(VideoParameters *p_Vid, int concealment_type)
{
  p_Vid->error_conceal_picture = copy_conceal_picture;
}

/*!
**************************************************************************************
* \brief 
*      Finds predicted macroblock values for error concealment
*   
*   Requires enc_pic->motion.mv and enc_pic->motion.ref_idx to be correct for 
*   current picture.
**************************************************************************************
*/
static void get_predicted_concealment_mb(Macroblock* currMB, StorablePicture* enc_pic, int decoder)
{
  Slice *currSlice = currMB->p_slice;
  int i,j,k;
  int block_size_x, block_size_y;
  int mv_mode, pred_dir;
  static const byte decode_block_scan[16] = {0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15};
  int block8x8;
  int k_start, k_end, k_inc;

  if (currSlice->slice_type == P_SLICE && !currMB->mb_type)
  {
    block_size_x = MB_BLOCK_SIZE;
    block_size_y = MB_BLOCK_SIZE;
    perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, LIST_0, 0, 0, enc_pic->motion.ref_idx, 0, 0, block_size_x, block_size_y);
  }
  else if (currMB->mb_type == 1)
  {
    block_size_x = MB_BLOCK_SIZE;
    block_size_y = MB_BLOCK_SIZE;
    pred_dir = currMB->b8x8[0].pdir;   
    perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 1, 1, enc_pic->motion.ref_idx, 0, 0, block_size_x, block_size_y);
  }
  else if (currMB->mb_type == 2)
  {   
    block_size_x = MB_BLOCK_SIZE;
    block_size_y = 8;    

    for (block8x8 = 0; block8x8 < 4; block8x8 += 2)
    {
      pred_dir = currMB->b8x8[block8x8].pdir;
      perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 2, 2, enc_pic->motion.ref_idx, 0, block8x8, block_size_x, block_size_y);
    }
  }
  else if (currMB->mb_type == 3)
  {   
    block_size_x = 8;
    block_size_y = 16;

    for (block8x8 = 0; block8x8 < 2; block8x8++)
    {
      i = block8x8<<1;
      j = 0;      
      pred_dir = currMB->b8x8[block8x8].pdir;
      perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 3, 3, enc_pic->motion.ref_idx, i, j, block_size_x, block_size_y);
    }
  }
  else 
  {
    for (block8x8 = 0; block8x8 < 4; block8x8++)
    {
      mv_mode  = currMB->b8x8[block8x8].mode;
      pred_dir = currMB->b8x8[block8x8].pdir;

      k_start = (block8x8 << 2);
      k_inc = (mv_mode == 5) ? 2 : 1;

      if (mv_mode == 0)
      {
        k_end = k_start + 1;
        block_size_x = 8;
        block_size_y = 8;
      }
      else
      {
        k_end = (mv_mode == 4) ? k_start + 1 : ((mv_mode == 7) ? k_start + 4 : k_start + k_inc + 1);
        block_size_x = ( mv_mode == 5 || mv_mode == 4 ) ? 8 : 4;
        block_size_y = ( mv_mode == 6 || mv_mode == 4 ) ? 8 : 4;
      }
      
      for (k = k_start; k < k_end; k += k_inc)
      {
        i =  (decode_block_scan[k] & 3);
        j = ((decode_block_scan[k] >> 2) & 3);
        perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, pred_dir, mv_mode, mv_mode, enc_pic->motion.ref_idx, i, j, block_size_x, block_size_y);
      }
    }
  }
}


/*!
 *************************************************************************************
 * \brief
 *    Performs copy error concealment for macroblocks with errors.
 *  Note: Currently assumes that the reference picture lists remain the same for all 
 *        slices of a picture. 
 *  
 *************************************************************************************
 */
void copy_conceal_picture(VideoParameters *p_Vid, StorablePicture *enc_pic, int decoder)
{
  unsigned int mb;
  Macroblock* currMB;
  int mb_error;
  byte** mb_error_map = enc_pic->mb_error_map[decoder];
  StorablePicture* refPic;

  refPic = find_nearest_ref_picture(p_Vid->p_Dpb, enc_pic->poc); //Used for concealment if actual reference pic is not known.

  for (mb = 0; mb < p_Vid->PicSizeInMbs; mb++)
  {
    currMB = &p_Vid->mb_data[mb];
    currMB->mb_x = PicPos[mb][0];
    currMB->mb_y = PicPos[mb][1];
    mb_error = mb_error_map[currMB->mb_y][currMB->mb_x];
    if (mb_error)
    {      
      currMB->block_x = currMB ->mb_x << 2;
      currMB->block_y = currMB->mb_y << 2;
      currMB->pix_x   = currMB->block_x << 2;
      currMB->pix_y   = currMB->block_y << 2;
      copy_conceal_mb(currMB, enc_pic, decoder, mb_error, refPic);
    }
  }
}

/******************************************************************************************
*
* Perform copy error concealment for macroblock.
*   
*******************************************************************************************
*/
static void copy_conceal_mb(Macroblock* currMB, StorablePicture *enc_pic, int decoder, int mb_error, StorablePicture* refPic)
{
  Slice *currSlice = currMB->p_slice;
  int j, i0 = currMB->pix_x;
  imgpel** concealed_img = &(enc_pic->p_dec_img[0][decoder][currMB->pix_y]);
  imgpel** ref_img;

  if (mb_error == 1 || (mb_error != 3 && currMB->mb_type > P8x8)) //All partitions lost, or intra mb lost
  {
    if (refPic != NULL) //Use nearest reference picture for concealment
    {
      ref_img = &(refPic->p_dec_img[0][decoder][currMB->pix_y]);
      for (j = 0; j < MB_BLOCK_SIZE; j++)
      {
        memcpy(&(concealed_img[j][i0]), &(ref_img[j][i0]), sizeof(imgpel)*MB_BLOCK_SIZE);
      }
    }
    else //No ref picture available
    {
      for (j = 0; j < MB_BLOCK_SIZE; j++)
      {
        memset(&(concealed_img[j][i0]), 128, sizeof(imgpel)*MB_BLOCK_SIZE); //Only reliable if sizeof(imgpel) = 1
      }
    }
  }
  else if (mb_error != 2 && currSlice->slice_type != I_SLICE && currMB->mb_type < P8x8) //Only partition 3 lost, and P/B macroblock
  {
    VideoParameters *p_Vid = currMB->p_Vid;
    get_predicted_concealment_mb(currMB, enc_pic, decoder);
    for(j = 0; j < MB_BLOCK_SIZE; j++)
    {  
      memcpy(&(concealed_img[j][i0]), &(p_Vid->p_decs->dec_mb_pred[decoder][j][0]), MB_BLOCK_SIZE * sizeof(imgpel));
    }
  }
}

/******************************************************************************************
*
*  Finds reference picture with nearest POC to current picture to use for error concealment
*   
*******************************************************************************************
*/
static StorablePicture* find_nearest_ref_picture(DecodedPictureBuffer *p_Dpb, int poc)
{
  unsigned int i;
  int min_poc_diff = 1000;
  int poc_diff;
  StorablePicture* refPic = NULL;

  for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++)
  {
    if (p_Dpb->fs_ref[i]->is_used==3)
    {
      if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term))
      {
        poc_diff = iabs(p_Dpb->fs_ref[i]->frame->poc - poc);
        if (poc_diff < min_poc_diff)
        {
          refPic = p_Dpb->fs_ref[i]->frame;
          min_poc_diff = poc_diff;
        }
      }
    }
  }
  return refPic;
}

/*!
 *************************************************************************************
 * \brief
 *    Gives the prediction residue for a block
 *************************************************************************************
 */
void compute_residue_block (Macroblock *currMB, imgpel **imgY, int **res_img, imgpel **mb_pred, int b8block, int block_size) 
{
  int i,j;
  int i0 = (b8block & 0x01)<<3,   i1 = i0+block_size;
  int j0 = (b8block >> 1)<<3,     j1 = j0+block_size;

  for (i = i0; i < i1; i++)
  {
    for (j = j0; j < j1; j++)
    {
      res_img[j][i] = (int)imgY[j][currMB->pix_x + i] - mb_pred[j][i];
    } 
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Stores the pel values for the current best mode.
 *************************************************************************************
 */
void errdo_store_best_block(InputParameters *p_Inp, imgpel*** mbY, imgpel*** dec_img, int block_i, int block_j, int img_i, int img_j, int block_size)

{
  int j, k;
  int j1 = block_j + block_size;
  
  for (k = 0; k < p_Inp->NoOfDecoders; k++)
  {
    for (j = block_j; j < j1; j++)
    {
      memcpy(&mbY[k][j][block_i], &dec_img[k][img_j+j][img_i], block_size * sizeof(imgpel));
    }
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Restores the pel values from the current best mode.
 *************************************************************************************
 */
void errdo_get_best_block(Macroblock *currMB, imgpel*** dec_img, imgpel*** mbY, int j0, int block_size)
{
  int j, k;
  int j1 = j0 + block_size;

  for (k = 0; k < currMB->p_Inp->NoOfDecoders; k++)
  {
    for (j = j0; j < j1; j++)
    {
      memcpy(&dec_img[k][currMB->pix_y + j][currMB->pix_x], mbY[k][j], block_size * sizeof(imgpel));
    }
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Builds a random status map showing whether each MB is received or lost, based
 *    on the packet loss rate and the slice structure.
 *
 * \param p_Vid
 *    VideoParameters structure for encoding
 * \param p_Inp
 *    InputParameters for configurations
 * \param s_map
 *    The status map to be filled
 *************************************************************************************
 */
static void Build_Status_Map(VideoParameters *p_Vid, InputParameters *p_Inp, byte **s_map)
{
  int i, j, slice = -1, mb = 0, jj, ii;
  byte packet_lost = 0;

  jj = p_Vid->height / MB_BLOCK_SIZE;
  ii = p_Vid->width / MB_BLOCK_SIZE;

  for (j = 0; j < jj; j++)
  {
    for (i = 0; i < ii; i++)
    {
      if (!p_Inp->slice_mode || p_Vid->mb_data[mb].slice_nr != slice) /* new slice */
      {
        packet_lost=0;
        if ((double)rand()/(double)RAND_MAX*100 < p_Inp->LossRateC)   packet_lost += 3;
        if ((double)rand()/(double)RAND_MAX*100 < p_Inp->LossRateB)   packet_lost += 2;
        if ((double)rand()/(double)RAND_MAX*100 < p_Inp->LossRateA)   packet_lost  = 1;
        slice++;
      }
      if (!packet_lost)
      {
        s_map[j][i]=0;  //! Packet OK
      }
      else
      {
        s_map[j][i] = packet_lost;
        if(p_Inp->partition_mode == 0)  s_map[j][i]=1;
      }
      mb++;
    }
  }
}

