/*!
 ***************************************************************************
 * \file
 *    pred_struct.c
 *
 * \author
 *    Athanasios Leontaris           <aleon@dolby.com>   
 *
 * \date
 *    June 8, 2009
 *
 * \brief
 *    Suurce file for prediction structure function headers
 **************************************************************************
 */

#include "pred_struct.h"
#include "explicit_seq.h"


#define DEBUG_PRED_STRUCT 0

static void init_gop_struct( InputParameters *p_Inp, SeqStructure *p_seq_struct, int *memory_size );
static void free_gop_struct( SeqStructure *p_seq_struct );
static void init_pred_struct( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int *memory_size );
static void free_pred_struct( SeqStructure *p_seq_struct );
static PicStructure * alloc_pic_struct( int num_slices, int *memory_size );
static void free_pic_struct( PicStructure *p_pic );
static FrameUnitStruct * alloc_frame_buffer( InputParameters *p_Inp, int num_frames, int num_slices, int *memory_size );
static void free_frame_buffer( FrameUnitStruct *p_frm_struct, int num_frames );

static inline int check_power_of_two( int number );
static int  establish_random_access( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames );
static int  establish_intra( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames );
static int  establish_sp( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames );
static int  get_fixed_frame( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames );
static int  get_prd_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames );
static int  get_idr_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames );
static void populate_frame( InputParameters *p_Inp, SeqStructure *p_seq_struct, FrameUnitStruct *p_frm_struct, PredStructAtom *p_cur_prd, 
                        int curr_frame, int pred_frame, int idx, int num_slices, int no_rnd_acc );
static void update_frame_indices( SeqStructure *p_seq_struct, FrameUnitStruct *p_frm_struct, int curr_frame, int pred_frame );
static int  populate_reg_pred_struct_atom( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, 
                                  int curr_frame, int proc_frames, int *terminate_pop );
static int  populate_rnd_acc_pred_struct_atom( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, 
                                      int curr_frame, int proc_frames, int *terminate_pop, int is_idr, int no_rnd_acc );

/*!
 ***********************************************************************
 * \brief
 *    Derivation of Picture Order Count Type 0
 * \param p_Vid
 *    pointer to the VideoParameters structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_frm_struct
 *    pointer to the cyclical frame unit buffer
 ***********************************************************************
 */

void get_poc_type_zero( VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct )
{
  FrameUnitStruct *p_cur = p_frm_struct; // + (curr_frm_idx % p_Vid->frm_struct_buffer);
  int last_disp_order;

  if ( p_Vid->last_idr_code_order > p_Vid->last_mmco_5_code_order )
  {
    last_disp_order = p_Vid->last_idr_disp_order;
  }
  else
  {
    last_disp_order = p_Vid->last_mmco_5_disp_order;
  }

  if ( p_cur->idr_flag )
  {
    p_Vid->toppoc = 0;
  }
  else
  {
    p_Vid->toppoc = (p_cur->frame_no - last_disp_order) << 1;
  }
  // frame_skip consideration
  p_Vid->toppoc = (1 + p_Inp->frame_skip) * p_Vid->toppoc;

  if ( p_Inp->PicInterlace == FRAME_CODING && p_Inp->MbInterlace == FRAME_CODING )
  {
    p_Vid->bottompoc = p_Vid->toppoc; // progressive
  }
  else
  {
    p_Vid->bottompoc = p_Vid->toppoc + 1; // hard coded
  }

  p_Vid->framepoc = imin( p_Vid->toppoc, p_Vid->bottompoc );
}

/*!
 ***********************************************************************
 * \brief
 *    Derivation of Picture Order Count Type 1
 * \param p_Vid
 *    pointer to the VideoParameters structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_frm_struct
 *    pointer to the cyclical frame unit buffer
 ***********************************************************************
 */

void get_poc_type_one( VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct )
{
  FrameUnitStruct *p_cur = p_frm_struct; // + curr_frm_idx;
  int i;
  int AbsFrameNum;
  int ExpectedDeltaPerPicOrderCntCycle;
  int PicOrderCntCycleCnt;
  int FrameNumInPicOrderCntCycle;
  int ExpectedPicOrderCnt;

  // we need to get type 0 values first and then estimate delta_pic_order_cnt with respect to those
  get_poc_type_zero( p_Vid, p_Inp, p_frm_struct );

  // next we use the decoder POC Type 1 code to determine the delta_pic_order_cnt

  // derive p_Vid->FrameNumOffset
  if ( p_cur->idr_flag )
  {
    p_Vid->FrameNumOffset = 0;
    p_Vid->delta_pic_order_cnt[0] = 0;
  }
  else
  {
    if ( p_Vid->last_has_mmco_5 )
    {
      p_Vid->prevFrameNumOffset = 0;
      p_Vid->prevFrameNum = 0;
    }
    if ( p_Vid->frame_num < p_Vid->prevFrameNum )
    {
      p_Vid->FrameNumOffset = p_Vid->prevFrameNumOffset + p_Vid->max_frame_num;
    }
    else
    {
      p_Vid->FrameNumOffset = p_Vid->prevFrameNumOffset;
    }
  }

  // derive AbsFrameNum
  if ( p_Vid->num_ref_frames_in_pic_order_cnt_cycle )
  {
    AbsFrameNum = p_Vid->FrameNumOffset + p_Vid->frame_num;
  }
  else
  {
    AbsFrameNum=0;
  }
  if ( !(p_cur->nal_ref_idc) && AbsFrameNum > 0 )
  {
    AbsFrameNum--;
  }

  // derive ExpectedPicOrderCnt
  ExpectedDeltaPerPicOrderCntCycle=0;
  if ( p_Vid->num_ref_frames_in_pic_order_cnt_cycle )
  {
    for( i = 0; i < (int) p_Vid->num_ref_frames_in_pic_order_cnt_cycle; i++ )
    {
      ExpectedDeltaPerPicOrderCntCycle += p_Vid->offset_for_ref_frame[i];
    }
  }

  if ( AbsFrameNum )
  {
    PicOrderCntCycleCnt = (AbsFrameNum - 1) / p_Vid->num_ref_frames_in_pic_order_cnt_cycle;
    FrameNumInPicOrderCntCycle = (AbsFrameNum - 1) % p_Vid->num_ref_frames_in_pic_order_cnt_cycle;

    ExpectedPicOrderCnt = PicOrderCntCycleCnt * ExpectedDeltaPerPicOrderCntCycle;
    for ( i = 0; i <= (int)FrameNumInPicOrderCntCycle; i++ )
    {
      ExpectedPicOrderCnt += p_Vid->offset_for_ref_frame[i];
    }
  }
  else
  {
    ExpectedPicOrderCnt = 0;
  }

  if ( !(p_cur->nal_ref_idc) )
  {
    ExpectedPicOrderCnt += p_Vid->offset_for_non_ref_pic;
  }

  p_Vid->delta_pic_order_cnt[0] = p_Vid->toppoc - ExpectedPicOrderCnt;
  p_Vid->delta_pic_order_cnt[1] = 0;

  // update parameters
  p_Vid->prevFrameNum = p_Vid->frame_num;
  p_Vid->prevFrameNumOffset = p_Vid->FrameNumOffset;
}

/*!
 ***********************************************************************
 * \brief
 *    Initializes POC type 0 and 1: POC type 1 simulates the decoder's behavior and sets values \n
 *    given the POCs calculated for POC type 0; needs to be tested
 * \param p_Vid
 *    pointer to the VideoParameters structure
 ***********************************************************************
 */

void init_poc(VideoParameters *p_Vid)
{
  InputParameters *p_Inp = p_Vid->p_Inp;
  // general parameters
  p_Vid->pic_order_cnt_type = p_Inp->pic_order_cnt_type;
  if ((p_Inp->PicInterlace == FRAME_CODING) && (p_Inp->MbInterlace == FRAME_CODING))
  {
    p_Vid->bottom_field_pic_order_in_frame_present_flag = FALSE;
  }
  else
  {
    p_Vid->bottom_field_pic_order_in_frame_present_flag = TRUE;
  }

  // POC type 0
  if ((p_Inp->PicInterlace==FRAME_CODING) && (p_Inp->MbInterlace==FRAME_CODING))
  {
    p_Vid->delta_pic_order_cnt_bottom = 0;
  }
  else
  {
    p_Vid->delta_pic_order_cnt_bottom = 1;
  }

  // POC type 1 (parameters need to be *optimized* for the prediction structure)
  // should be good enough for now
  p_Vid->delta_pic_order_always_zero_flag = FALSE;
  p_Vid->num_ref_frames_in_pic_order_cnt_cycle = 1;

  // these initialization have to depend more on the structure
  if (p_Inp->BRefPictures == 1)
  {
    p_Vid->offset_for_non_ref_pic  = 0;
    p_Vid->offset_for_ref_frame[0] = 2;
  }
  else
  {
    p_Vid->offset_for_non_ref_pic  = -2*(p_Inp->NumberBFrames);
    p_Vid->offset_for_ref_frame[0] =  2*(p_Inp->NumberBFrames + 1);
  }

  if ((p_Inp->PicInterlace==FRAME_CODING) && (p_Inp->MbInterlace==FRAME_CODING))
  {
    p_Vid->offset_for_top_to_bottom_field = 0;
  }
  else
  {
    p_Vid->offset_for_top_to_bottom_field = 1;
  }  
}

/*!
 ***********************************************************************
 * \brief
 *    Allocate picture structure
 * \param num_slices
 *    max number of slices used to code the current picture
 * \param memory_size
 *    pointer to the memory_size variable to count the number of bytes allocated
 * \return
 *    pointer to the allocated PicStructure
 ***********************************************************************
 */

static PicStructure * alloc_pic_struct( int num_slices, int *memory_size )
{
  PicStructure *p_pic = (PicStructure *)malloc( sizeof( PicStructure ) );
  if ( p_pic == NULL )
  {
    fprintf(stderr, "\nNot enough memory or error during memory allocation: alloc_pic_struct()");
    exit(1);
  }
  *memory_size += sizeof( PicStructure );

  p_pic->num_slices = num_slices; // this could also be set on a frame basis
  p_pic->p_slice = (SliceStructure *)malloc( num_slices * sizeof( SliceStructure ) );
  if ( p_pic->p_slice == NULL )
  {
    fprintf(stderr, "\nNot enough memory or error during memory allocation: alloc_pic_struct()");
    exit(1);
  }
  *memory_size += (num_slices * sizeof( SliceStructure ));

  return p_pic;
}

/*!
 ***********************************************************************
 * \brief
 *    Free picture structure
 * \param p_pic
 *    pointer to the picture structure that we wish to deallocate
 ***********************************************************************
 */

static void free_pic_struct( PicStructure *p_pic )
{
  if ( p_pic != NULL )
  {
    if ( p_pic->p_slice != NULL )
    {
      free( p_pic->p_slice );
      p_pic->p_slice = NULL;
    }
    free( p_pic );
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Allocate frame buffer
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param num_frames
 *    number of frames/length of the cyclical frame buffer
 * \param num_slices
 *    max number of slices used to code each frame unit in the cyclical frame buffer
 * \param memory_size
 *    pointer to the memory_size variable to count the number of bytes allocated
 * \return
 *    pointer to the allocated FrameUnitStruct
 ***********************************************************************
 */

static FrameUnitStruct * alloc_frame_buffer( InputParameters *p_Inp, int num_frames, int num_slices, int *memory_size )
{
  int idx;

  // FrameUnitStruct
  FrameUnitStruct *p_frm_struct = (FrameUnitStruct *)malloc( num_frames * sizeof( FrameUnitStruct ) );
  FrameUnitStruct *p_frm_tmp;

  if ( p_frm_struct == NULL )
  {
    fprintf(stderr, "\nNot enough memory or error during memory allocation: alloc_frame_buffer()");
    exit(1);
  }
  *memory_size += num_frames * sizeof( FrameUnitStruct );

  for ( idx = 0; idx < num_frames; idx++ )
  {
    p_frm_tmp = p_frm_struct + idx;
    switch( p_Inp->PicInterlace )
    {
    default:
    case FRAME_CODING:
      p_frm_tmp->field_pic_flag = 0;
      p_frm_tmp->p_top_fld_pic = NULL;
      p_frm_tmp->p_bot_fld_pic = NULL;
      p_frm_tmp->p_frame_pic = alloc_pic_struct( num_slices, memory_size );
      break;
    case FIELD_CODING:
      p_frm_tmp->field_pic_flag = 1;
      p_frm_tmp->p_top_fld_pic = alloc_pic_struct( num_slices, memory_size );
      p_frm_tmp->p_bot_fld_pic = alloc_pic_struct( num_slices, memory_size );
      p_frm_tmp->p_frame_pic = NULL;
      break;
    case ADAPTIVE_CODING:
      p_frm_tmp->p_top_fld_pic = alloc_pic_struct( num_slices, memory_size );
      p_frm_tmp->p_bot_fld_pic = alloc_pic_struct( num_slices, memory_size );
      p_frm_tmp->p_frame_pic = alloc_pic_struct( num_slices, memory_size );
      break;
    }
  }

  return p_frm_struct;
}

/*!
 ***********************************************************************
 * \brief
 *    Free frame buffer
 * \param p_frm_struct
 *    pointer to the cyclical frame buffer
 * \param num_frames
 *    number of frames in the cyclical frame buffer
 ***********************************************************************
 */

static void free_frame_buffer( FrameUnitStruct *p_frm_struct, int num_frames )
{
  int idx;
  FrameUnitStruct *p_frm_tmp;

  if ( p_frm_struct != NULL )
  {
    for ( idx = 0; idx < num_frames; idx++ )
    {
      p_frm_tmp = p_frm_struct + idx;

      free_pic_struct( p_frm_tmp->p_top_fld_pic );
      p_frm_tmp->p_top_fld_pic = NULL;

      free_pic_struct( p_frm_tmp->p_bot_fld_pic );
      p_frm_tmp->p_bot_fld_pic = NULL;

      free_pic_struct( p_frm_tmp->p_frame_pic );
      p_frm_tmp->p_frame_pic = NULL;
    }

    free( p_frm_struct );
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Initialize sequence structure
 * \param p_Vid
 *    pointer to the VideoParameters structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param memory_size
 *    pointer to the memory_size variable that stores the number of bytes allocated so far
 * \return
 *    pointer to SeqStructure
 ***********************************************************************
 */

SeqStructure * init_seq_structure( VideoParameters *p_Vid, InputParameters *p_Inp, int *memory_size )
{
  SeqStructure *p_seq_struct = (SeqStructure *)malloc( sizeof( SeqStructure ) );

  if ( p_seq_struct == NULL )
  {
    fprintf(stderr, "\nNot enough memory or error during memory allocation: init_seq_structure()");
    exit(1);
  }

  // set length of frame buffer
  p_Inp->FrmStructBufferLength = imax( p_Inp->FrmStructBufferLength, p_Inp->NumberBFrames + p_Inp->intra_delay + 2 );
  p_Vid->frm_struct_buffer = p_Inp->FrmStructBufferLength;
  p_Vid->frm_struct_buffer = imin( p_Vid->frm_struct_buffer, p_Inp->no_frames );
  p_Inp->FrmStructBufferLength = imin( p_Inp->FrmStructBufferLength, p_Inp->no_frames ); 

  *memory_size += sizeof( SeqStructure );

  p_seq_struct->max_num_slices           = (p_Inp->slice_mode == 1) ? (p_Vid->FrameSizeInMbs / p_Inp->slice_argument) : 1;
  p_seq_struct->num_frames               = p_Vid->frm_struct_buffer;
  p_seq_struct->p_frm                    = alloc_frame_buffer( p_Inp, p_seq_struct->num_frames, p_seq_struct->max_num_slices, memory_size );
  p_seq_struct->last_random_access_frame = 0;
  p_seq_struct->last_idr_frame           = 0;
  p_seq_struct->last_intra_frame         = 0;
  p_seq_struct->last_mmco_5_frame        = -1;
  p_seq_struct->curr_num_to_populate     = p_Vid->frm_struct_buffer;
  p_seq_struct->pop_start_frame          = 0;
  p_seq_struct->last_rand_access_disp    = 0;
  p_seq_struct->last_idr_disp            = 0;
  p_seq_struct->last_intra_disp          = 0;
  p_seq_struct->last_bl_frm_disposable   = 0;
  p_seq_struct->last_sp_frame            = 0;
  p_seq_struct->last_sp_disp             = 0;

  // initialize prediction structures
  init_pred_struct( p_Vid, p_Inp, p_seq_struct, memory_size );
  init_gop_struct( p_Inp, p_seq_struct, memory_size );

  // populate frames
  populate_frm_struct( p_Vid, p_Inp, p_seq_struct, p_Vid->frm_struct_buffer, p_Inp->no_frames );

  return p_seq_struct;
}

/*!
 ***********************************************************************
 * \brief
 *    Free sequence structure
 * \param p_seq_struct
 *    pointer to the sequence structure we wish to deallocate
 ***********************************************************************
 */

void free_seq_structure( SeqStructure *p_seq_struct )
{
  free_frame_buffer( p_seq_struct->p_frm, p_seq_struct->num_frames );
  p_seq_struct->p_frm = NULL;
  free_gop_struct( p_seq_struct );
  p_seq_struct->p_gop = NULL;
  free_pred_struct( p_seq_struct );
  p_seq_struct->p_prd = NULL;

  if ( p_seq_struct != NULL )
  {
    free( p_seq_struct );
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Establish whether the frame with coding order "curr_frame" will be coded as an IDR picture
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param curr_frame
 *    coding order of the current frame
 * \param avail_frames
 *    frames available for population
 * \return
 *    returns 1 when current frame will be a random access frame \n
 *    if PreferDispOrder == 1 then it returns 1 + disp_offset (for the random access frame)
 ***********************************************************************
 */

static int establish_random_access( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames )
{
  int is_random_access = 0;

  if ( !curr_frame ) // first frame in coding order
  {
    if ( !(p_Inp->PreferDispOrder) ) // coding order
    {
      is_random_access = 1;
    }
    else
    {
      int idx;
      PredStructAtom *p_cur_gop;

      // test each random access prediction structure, starting from the longest one
      // if random access is established then break from this loop and prepare output parameter
      for ( idx = (p_seq_struct->num_gops - 1); idx >= 0; idx-- )
      {
        p_cur_gop = p_seq_struct->p_gop + idx;
        // check if length of structure overflows the available frame number
        if ( p_cur_gop->length <= avail_frames )
        {
          is_random_access = 1 + idx;
          break;
        }
      }
    }
  }
  else if ( p_Inp->idr_period )
  {
    if ( !(p_Inp->PreferDispOrder) ) // coding order
    {
      switch ( p_Inp->adaptive_idr_period )
      {
      default:
      case 0:
        if ( curr_frame % p_Inp->idr_period == 0 )
        {
          is_random_access = 1;
        }
        break;
      case 1:
        if ( (curr_frame - p_seq_struct->last_random_access_frame) % p_Inp->idr_period == 0 )
        {
          is_random_access = 1;
        }
        break;
      case 2:
        if ( (curr_frame - imax( p_seq_struct->last_random_access_frame, p_seq_struct->last_intra_frame)) % p_Inp->idr_period == 0 )
        {
          is_random_access = 1;
        }
        break;
      }
    }
    else // display order (insertion)
    {
      int idx;
      int offset;
      PredStructAtom *p_cur_gop;

      // test each random access prediction structure, starting from the longest one
      // if random access is established then break from this loop and prepare output parameter
      for ( idx = (p_seq_struct->num_gops - 1); idx >= 0; idx-- )
      {
        p_cur_gop = p_seq_struct->p_gop + idx;
        // check if length of structure overflows the available frame number
        if ( p_cur_gop->length > avail_frames )
        {
          continue;
        }
        // recall that frame "0" is the random access frame in those prediction structures
        offset = p_cur_gop->p_frm[0].disp_offset;

        // now test if random access conditions are satisfied
        switch ( p_Inp->adaptive_idr_period )
        {
        default:
        case 0:
          if ( !curr_frame || (curr_frame + offset) % p_Inp->idr_period == 0 )
          {
            is_random_access = 1 + idx;
          }
          break;
        case 1:
          if ( !curr_frame || ((curr_frame + offset) - p_seq_struct->last_rand_access_disp) % p_Inp->idr_period == 0 )
          {
            is_random_access = 1 + idx;
          }
          break;
        case 2:
          if ( !curr_frame || ((curr_frame + offset) - imax( p_seq_struct->last_rand_access_disp, p_seq_struct->last_intra_disp)) % p_Inp->idr_period == 0 )
          {
            is_random_access = 1 + idx;
          }
          break;
        }
        if ( is_random_access )
        {
          break;
        }
      }
    }
  }
  // additional case can be added to insert IDR based on *pre-analysis* and other "pre-scient" tools

  return is_random_access;
}

/*!
 ***********************************************************************
 * \brief
 *    Establish whether the frame with coding order "curr_frame" will be coded as an INTRA picture
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param curr_frame
 *    coding order of the current frame
 * \param avail_frames
 *    frames available for population
 * \return
 *    returns 1 when current frame will be an intra frame \n
 *    if PreferDispOrder == 1 then it returns 1 + disp_offset (for the intra frame)
 ***********************************************************************
 */

static int establish_intra( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames )
{
  int is_intra = 0;

  if ( !curr_frame ) // first frame in coding order
  {
    is_intra = 1;
  }
  else if ( p_Inp->intra_period )
  {
    if ( !(p_Inp->PreferDispOrder) ) // coding order
    {
      switch ( p_Inp->adaptive_intra_period )
      {
      default:
      case 0:
        if ( curr_frame % p_Inp->intra_period == 0 )
        {
          is_intra = 1;
        }
        break;
      case 1:
        if ( (curr_frame - imax( p_seq_struct->last_idr_frame, p_seq_struct->last_intra_frame)) % p_Inp->intra_period == 0 )
        {
          is_intra = 1;
        }
        break;
      }
    }
    else // display order (insertion)
    {
      int idx;
      int offset;
      PredStructAtom *p_cur_gop;

      // test each random access prediction structure, staring from the longest one
      // if random access is established then break from this loop and prepare output parameter
      for ( idx = (p_seq_struct->num_gops - 1); idx >= 0; idx-- )
      {
        p_cur_gop = p_seq_struct->p_gop + idx;
        // check if length of structure overflows the available frame number
        if ( p_cur_gop->length > avail_frames )
        {
          continue;
        }
        // recall that frame "0" is the random access frame in those prediction structures
        offset = p_cur_gop->p_frm[0].disp_offset;

        // now test if random access conditions are satisfied
        switch ( p_Inp->adaptive_intra_period )
        {
        default:
        case 0:
          if ( (curr_frame + offset) % p_Inp->intra_period == 0 )
          {
            is_intra = 1 + idx;
          }
          break;
        case 2:
          if ( ((curr_frame + offset) - imax( p_seq_struct->last_idr_disp, p_seq_struct->last_intra_disp)) % p_Inp->intra_period == 0 )
          {
            is_intra = 1 + idx;
          }
          break;
        }
        if ( is_intra )
        {
          break;
        }
      }
    }
  }
  // additional case can be added to insert IDR based on *pre-analysis* and other "pre-scient" tools

  return is_intra;
}

/*!
 ***********************************************************************
 * \brief
 *    Establish whether the frame with coding order "curr_frame" will be coded as an SP picture
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param curr_frame
 *    coding order of the current frame
 * \param avail_frames
 *    frames available for population
 * \return
 *    returns 1 when current frame will be an SP frame \n
 *    if PreferDispOrder == 1 then it returns 1 + disp_offset (for the SP frame)
 ***********************************************************************
 */

static int establish_sp( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames )
{
  int is_sp = 0;

  if ( p_Inp->sp_periodicity )
  {
    if ( !(p_Inp->PreferDispOrder) ) // coding order
    {
      if ( (curr_frame - p_seq_struct->last_sp_frame) % p_Inp->sp_periodicity == 0 )
      {
        is_sp = 1;
      }
    }
    else // display order (insertion)
    {
      int idx;
      int offset;
      PredStructAtom *p_cur_gop;

      // test each random access prediction structure, staring from the longest one
      // if random access is established then break from this loop and prepare output parameter
      for ( idx = (p_seq_struct->num_gops - 1); idx >= 0; idx-- )
      {
        p_cur_gop = p_seq_struct->p_gop + idx;
        // check if length of structure overflows the available frame number
        if ( p_cur_gop->length > avail_frames )
        {
          continue;
        }
        // recall that frame "0" is the random access frame in those prediction structures
        offset = p_cur_gop->p_frm[0].disp_offset;

        // now test if random access conditions are satisfied
        if ( (curr_frame + offset - p_seq_struct->last_sp_disp) % p_Inp->sp_periodicity == 0 )
        {
          is_sp = 1 + idx;
          break;
        }
      }
    }
  }

  return is_sp;
}

/*!
 ***********************************************************************
 * \brief
 *    Find closest fixed frame (IDR, intra, or slice set through some pre-analysis tool)
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param curr_frame
 *    coding order of the current frame
 * \param avail_frames
 *    frames available for population
 * \return
 *    returns the coding order of the first "fixed" frame, if none then -1
 ***********************************************************************
 */

static int get_fixed_frame( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames )
{
  int idx         = 0;
  int is_rnd_acc  = 0;
  int is_intra    = 0;
  int is_sp       = 0;

  while ( idx < avail_frames )
  {
    is_rnd_acc = establish_random_access( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx );
    is_intra   = establish_intra( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx );
    is_sp      = establish_sp( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx );
    if ( is_rnd_acc || is_intra || is_sp )
    {
      return idx;
    }
    idx++;
  }
  // signal that there is no fixed frame in the processed frames buffer (functions have to interpret the negative output here)
  return -1;
}

/*!
 ***********************************************************************
 * \brief
 *    Check if "number" is a power of two
 * \param number
 *    input value
 * \return
 *    1 if power of two, 0 otherwise
 ***********************************************************************
 */

static inline int check_power_of_two( int number )
{
  return (((~((~number) + 1)) & number ) == 0) ? 1 : 0;
}

/*!
 ***********************************************************************
 * \brief
 *    Select index to pred structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param num_frames
 *    frames available for population
 * \return
 *    returns the index to the selected prediction structure
 ***********************************************************************
 */

static int get_prd_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames )
{
  int curr_prd;
  PredStructAtom *p_cur_prd;

  // allocate atoms
  for ( curr_prd = (p_seq_struct->num_prds - 1); curr_prd >= 0; curr_prd-- )
  {
    p_cur_prd = p_seq_struct->p_prd + curr_prd;
    if ( p_Inp->PreferPowerOfTwo && curr_prd != (p_seq_struct->num_prds - 1) && !check_power_of_two( p_cur_prd->length ) )
    {
      continue;
    }
    if ( p_cur_prd->length <= num_frames )
    {
      return curr_prd;
    }
  }
  return 0;
}

/*!
 ***********************************************************************
 * \brief
 *    Select index to random access structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param num_frames
 *    frames available for population
 * \return
 *    returns the index to the selected prediction structure
 ***********************************************************************
 */

static int get_idr_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames )
{
  int curr_gop;
  PredStructAtom *p_cur_gop;

  // allocate atoms
  for ( curr_gop = (p_seq_struct->num_gops - 1); curr_gop >= 0; curr_gop-- )
  {
    p_cur_gop = p_seq_struct->p_gop + curr_gop;
    if ( p_Inp->PreferPowerOfTwo && curr_gop != (p_seq_struct->num_gops - 1) && !check_power_of_two( p_cur_gop->length ) )
    {
      continue;
    }
    if ( p_cur_gop->length <= num_frames )
    {
      return curr_gop;
    }
  }
  return 0;
}

/*!
 ***********************************************************************
 * \brief
 *    Create random access prediction structures
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param memory_size
 *    frames available for population
 * \return
 *    returns the index to the selected prediction structure
 ***********************************************************************
 */

static void init_gop_struct( InputParameters *p_Inp, SeqStructure *p_seq_struct, int *memory_size )
{
  int idx;
  int length;
  PredStructAtom *p_cur_gop, *p_cur_prd, *p_cur_prd_tail = NULL;
  PredStructFrm *p_dst, *p_src;

  p_seq_struct->num_gops = ( ( (p_Inp->idr_period != 1 && p_Inp->intra_period != 1) && (p_Inp->intra_delay || !p_Inp->EnableIDRGOP) ) ? 1 : 0 ) + 1;

  // we assume that intra_delay is ALWAYS lesser than the idr_period
  if ( p_Inp->idr_period )
  {
    assert( p_Inp->intra_delay < p_Inp->idr_period );
  }

  p_seq_struct->p_gop = (PredStructAtom *)malloc( p_seq_struct->num_gops * sizeof( PredStructAtom ) );
  *memory_size += p_seq_struct->num_gops * sizeof( PredStructAtom );

  // default IDR GOP
  length = 1;
  p_cur_gop = p_seq_struct->p_gop;
  p_cur_gop->length = length;
  p_cur_gop->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) );
  *memory_size += length * sizeof( PredStructFrm );

  // initialize atoms
  p_cur_gop = p_seq_struct->p_gop;
  p_dst = p_cur_gop->p_frm + 0;
  p_dst->slice_type    = I_SLICE;
  p_dst->nal_ref_idc   = p_Inp->EnableOpenGOP ? NALU_PRIORITY_HIGH : NALU_PRIORITY_HIGHEST;
  p_dst->disp_offset   = 0;
  p_dst->layer         = 0;
  p_dst->slice_qp      = 0;
  p_dst->random_access = 1;

  p_dst->temporal_layer = 0;

  // intra-delayed IDR GOP
  if ( p_Inp->intra_delay && p_Inp->intra_period != 1 && p_Inp->idr_period != 1 )
  {
    int proc_frames = 0;
    int start_frame = p_Inp->intra_delay - 1;
    int idr_idx = 0;
    int curr_prd;

    if ( p_Inp->EnableIDRGOP == 0 )
    {
      // longest structure
      p_cur_prd_tail = p_seq_struct->p_prd + p_seq_struct->num_prds - 1;
    }

    // allocate atoms
    length = p_Inp->intra_delay + 1 + ( !p_Inp->EnableIDRGOP ? (p_cur_prd_tail->length - 1) : 0 );
    p_cur_gop = p_seq_struct->p_gop + 1;
    p_cur_gop->length = length;
    p_cur_gop->gop_levels = 1;
    p_cur_gop->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) );
    *memory_size += length * sizeof( PredStructFrm );

    // IDR picture
    p_cur_gop = p_seq_struct->p_gop + 1;
    p_dst = p_cur_gop->p_frm + idr_idx;
    p_dst->slice_type     = I_SLICE;
    p_dst->nal_ref_idc    = p_Inp->EnableOpenGOP ? NALU_PRIORITY_HIGH : NALU_PRIORITY_HIGHEST;
    p_dst->disp_offset    = p_Inp->intra_delay;
    p_dst->layer          = 0;
    p_dst->slice_qp       = 0;
    p_dst->random_access  = 1;
    p_dst->temporal_layer = 0;

    idr_idx++;

    while ( proc_frames < p_Inp->intra_delay )
    {
      // given the available slots select the index to the pred structure to use for current segment
      curr_prd = get_prd_index( p_Inp, p_seq_struct, p_Inp->intra_delay - proc_frames );
      p_cur_prd = p_seq_struct->p_prd + curr_prd;
      // set gop_levels
      p_cur_gop->gop_levels = p_cur_prd->gop_levels;
      // populate gop structure from selected structure
      for ( idx = 0; idx < p_cur_prd->length; idx++, idr_idx++, proc_frames++ )
      {
        p_dst = p_cur_gop->p_frm + idr_idx;
        p_src = p_cur_prd->p_frm + idx;

        p_dst->slice_type     = p_src->slice_type;
        p_dst->nal_ref_idc    = p_src->nal_ref_idc;
        p_dst->disp_offset    = start_frame - p_src->disp_offset;
        p_dst->layer          = p_src->layer;
        p_dst->slice_qp       = p_src->slice_qp;
        p_dst->random_access  = 0;
        p_dst->temporal_layer = p_src->temporal_layer;
      }      
      // update start_frame
      start_frame -= p_cur_prd->length;      
    }

    // for not properly terminated GOPs...
    if ( p_Inp->EnableIDRGOP == 0 )
    {
      // add an offset to the display offsets
      for ( idr_idx = 0; idr_idx < (p_Inp->intra_delay + 1); idr_idx++ )
      {
        p_cur_gop->p_frm[idr_idx].disp_offset += (p_cur_prd_tail->length - 1);
      }
      p_cur_gop->gop_levels = imax( p_cur_gop->gop_levels, p_cur_prd_tail->gop_levels );
      // and now place the prd struct *before* them (set idx to 1 to avoid copying the I/P frame, we only seek the hierarchy if it exists)
      for ( idx = 1; idx < p_cur_prd_tail->length; idx++, idr_idx++ )
      {
        p_dst = p_cur_gop->p_frm + idr_idx;
        p_src = p_cur_prd_tail->p_frm + idx;

        p_dst->slice_type     = p_src->slice_type;
        p_dst->nal_ref_idc    = p_src->nal_ref_idc;
        p_dst->disp_offset    = p_src->disp_offset;
        p_dst->layer          = p_src->layer;
        p_dst->slice_qp       = p_src->slice_qp;
        p_dst->random_access  = 0;
        p_dst->temporal_layer = p_src->temporal_layer; 
      }
    }
  }
  else if ( !(p_Inp->intra_delay) && (!p_Inp->EnableIDRGOP && p_Inp->intra_period != 1 && p_Inp->idr_period != 1) )
  {
    int idr_idx = 0;

    // longest structure
    p_cur_prd = p_seq_struct->p_prd + p_seq_struct->num_prds - 1;

    // allocate atoms
    length = p_cur_prd->length;
    p_cur_gop = p_seq_struct->p_gop + 1;
    p_cur_gop->length = length;
    p_cur_gop->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) );
    *memory_size += length * sizeof( PredStructFrm );
    idr_idx = 0;

    // IDR picture
    p_cur_gop = p_seq_struct->p_gop + 1;
    p_cur_gop->gop_levels = p_cur_prd->gop_levels;
    p_dst = p_cur_gop->p_frm + idr_idx;
    p_dst->slice_type     = I_SLICE;
    p_dst->nal_ref_idc    = p_Inp->EnableOpenGOP ? NALU_PRIORITY_HIGH : NALU_PRIORITY_HIGHEST;
    p_dst->disp_offset    = p_cur_prd->p_frm[0].disp_offset;
    p_dst->layer          = 0;
    p_dst->slice_qp       = 0;
    p_dst->random_access  = 1;
    p_dst->temporal_layer = 0;

    idr_idx++;
    
    // populate gop structure from selected structure
    for ( idx = 1; idx < p_cur_prd->length; idx++, idr_idx++ )
    {
      p_dst = p_cur_gop->p_frm + idr_idx;
      p_src = p_cur_prd->p_frm + idx;

      p_dst->slice_type     = p_src->slice_type;
      p_dst->nal_ref_idc    = p_src->nal_ref_idc;
      p_dst->disp_offset    = p_src->disp_offset;
      p_dst->layer          = p_src->layer;
      p_dst->slice_qp       = p_src->slice_qp;
      p_dst->random_access  = 0;
      p_dst->temporal_layer = p_src->temporal_layer;
    }      
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Create non-random access prediction structures
 * \param p_Vid
 *    pointer to the VideoParameters structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param memory_size
 *    frames available for population
 ***********************************************************************
 */

static void init_pred_struct( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int *memory_size )
{
  int curr_prd, idx;
  int frm, sum, layer, order;
  int length, length2, pred_mode;
  PredStructAtom *p_cur_prd;
  PredStructFrm *p_dst;

  p_seq_struct->num_prds = (p_Inp->idr_period == 1 || p_Inp->intra_period == 1) ? 1 : (p_Inp->NumberBFrames + 1);

  p_seq_struct->p_prd = (PredStructAtom *)malloc( p_seq_struct->num_prds * sizeof( PredStructAtom ) );
  *memory_size += p_seq_struct->num_prds * sizeof( PredStructAtom );

  // allocate atoms
  for ( curr_prd = (p_seq_struct->num_prds - 1); curr_prd >= 0; curr_prd-- )
  {
    // length of prediction structure
    length = curr_prd + 1;

    p_cur_prd = p_seq_struct->p_prd + curr_prd;
    p_cur_prd->length = length;
    p_cur_prd->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) );
    *memory_size += length * sizeof( PredStructFrm );
  }

  // initialize atoms
  for ( curr_prd = (p_seq_struct->num_prds - 1); curr_prd >= 0; curr_prd-- )
  {
    // length of prediction structure
    length = curr_prd + 1;

    switch ( p_Inp->HierarchicalCoding )
    {
    default:
    case 0:
      pred_mode = 0;
      break;
    case 1:
      pred_mode = 1;
      break;
    case 2:
      // check if power of two
      pred_mode = check_power_of_two( length ) ? 2 : ( length > 4 ? 1 : 0 );
      break;
    case 3:
      // check again if power of two
      pred_mode = (length == (p_Inp->NumberBFrames + 1)) ? 3 : (check_power_of_two( length ) ? 2 : ( length > 4 ? 1 : 0 ));
      break;
    }

    p_cur_prd = p_seq_struct->p_prd + curr_prd;
    p_cur_prd->gop_levels = 0;
    switch ( pred_mode )
    {
    default:
    case 0: // disposable B-pics of equal priority
      p_dst = p_cur_prd->p_frm + 0;

      p_dst->slice_type     = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE;
      p_dst->nal_ref_idc    = NALU_PRIORITY_HIGH;
      p_dst->disp_offset    = length - 1;
      p_dst->layer          = 0;
      p_dst->slice_qp       = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[P_SLICE] - p_Inp->qp[B_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers)
      p_dst->random_access  = 0;
      p_dst->temporal_layer = 0;

      for ( idx = 1; idx < length; idx++ )
      {
        p_dst = p_cur_prd->p_frm + idx;

        p_dst->slice_type       = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE;
        p_dst->nal_ref_idc      = ( p_Inp->BRefPictures == 1 && p_dst->slice_type == B_SLICE ) ? NALU_PRIORITY_LOW : NALU_PRIORITY_DISPOSABLE;
        p_dst->disp_offset      = idx - 1;
        p_dst->layer            = 1;
        p_dst->slice_qp         = p_dst->nal_ref_idc ? p_Inp->qpBRSOffset : 0; //2; 2 when depends on QPPSlice (highest layer QP)
        p_dst->random_access    = 0;
          p_dst->temporal_layer = 0;
      }
      if ( length > 1 )
      {
        p_cur_prd->gop_levels = 1;
      }
      break;
    case 1: // even/odd hierarchy
      length2 = ((length - 1) >> 1) + 1;

      p_dst = p_cur_prd->p_frm + 0;

      p_dst->slice_type     = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE;
      p_dst->nal_ref_idc    = NALU_PRIORITY_HIGH;
      p_dst->disp_offset    = length - 1;
      p_dst->layer          = 0;
      p_dst->slice_qp       = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers)
      p_dst->random_access  = 0;
      p_dst->temporal_layer = 0;

      for ( idx = 1; idx < length2; idx++ )
      {
        p_dst = p_cur_prd->p_frm + idx;

        p_dst->slice_type     = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE;
        p_dst->nal_ref_idc    = NALU_PRIORITY_LOW;
        p_dst->disp_offset    = 2*(idx - 1) + 1;
        p_dst->layer          = 1;
        p_dst->slice_qp       = p_dst->slice_type == B_SLICE ? 
          (p_Inp->HierarchyLevelQPEnable ? -1: p_Inp->qpBRSOffset) : (p_Inp->HierarchyLevelQPEnable ? 1 : 0); // 1
        p_dst->random_access  = 0;
        p_dst->temporal_layer = 0;
      }
      for ( idx = length2; idx < length; idx++ )
      {
        p_dst = p_cur_prd->p_frm + idx;

        p_dst->slice_type     = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE;
        p_dst->nal_ref_idc    = NALU_PRIORITY_DISPOSABLE;
        p_dst->disp_offset    = (idx - length2) * 2;
        p_dst->layer          = 2;
        p_dst->slice_qp       = p_dst->slice_type == B_SLICE ? 0 : 2;//2;
        p_dst->random_access  = 0;
        p_dst->temporal_layer = 0;
      }
      if ( length > 1 )
      {
        p_cur_prd->gop_levels = 2;
      }
      break;
    case 2: // full binary decomposition
      p_dst = p_cur_prd->p_frm + 0;

      p_dst->slice_type     = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE;
      p_dst->nal_ref_idc    = NALU_PRIORITY_HIGH;
      p_dst->disp_offset    = length - 1;
      p_dst->layer          = 0;
      p_dst->slice_qp       = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers)
      p_dst->random_access  = 0;
      p_dst->temporal_layer = 0;

      if ( length > 3 )
      {
        int GOPlevels = 1;

        // determine number of levels
        while ((length >> GOPlevels) > 1)
        {
          GOPlevels ++;
        }
        p_cur_prd->gop_levels = GOPlevels;

        // hierarchical layer (from highest priority to lowest priority)
        for ( layer = 1, order = 1, sum = 0, frm = 1; sum < (length - 1); frm = (frm << 1), sum += frm, layer++ )
        {
          // layer frames (in *coding* order)
          for ( idx = 0; idx < frm; idx++, order++ )
          {
            p_dst = p_cur_prd->p_frm + order;

            p_dst->slice_type     = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE;
            p_dst->nal_ref_idc    = (frm == (length >> 1)) ? NALU_PRIORITY_DISPOSABLE : NALU_PRIORITY_LOW;
            p_dst->disp_offset    = ((length - 1) >> layer) + idx * (length >> (layer - 1));
            p_dst->layer          = layer;
            p_dst->slice_qp       = p_dst->slice_type == B_SLICE ? 
              (p_Inp->HierarchyLevelQPEnable ? (-GOPlevels + layer): (p_dst->nal_ref_idc != NALU_PRIORITY_DISPOSABLE ? p_Inp->qpBRSOffset : 0)) : (p_Inp->HierarchyLevelQPEnable ? layer : 0); // layer
            p_dst->random_access  = 0;
            p_dst->temporal_layer = 0;
          }
        }
      }
      else
      {
        for ( idx = 1; idx < length; idx++ )
        {
          p_dst = p_cur_prd->p_frm + idx;

          p_dst->slice_type     = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE;
          p_dst->nal_ref_idc    = NALU_PRIORITY_DISPOSABLE;
          p_dst->disp_offset    = idx - 1;
          p_dst->layer          = 1;
          p_dst->slice_qp       = 0; //2;
          p_dst->random_access  = 0;
          p_dst->temporal_layer = 0;
        }
        if ( length > 1 )
        {
          p_cur_prd->gop_levels = 1;
        }
      }
      break;
    case 3: // explicit structure
      {
        int max_layer = 0;

        if ( p_Inp->LowDelay) 
        {
          for ( idx = 0; idx < length-1; idx++ )
          {
            p_dst = p_cur_prd->p_frm + idx;

            p_dst->slice_type    = p_Vid->gop_structure[idx].slice_type;
            p_dst->nal_ref_idc   = p_Vid->gop_structure[idx].reference_idc;
            p_dst->disp_offset   = p_Vid->gop_structure[idx].display_no;
            p_dst->layer         = ((p_Vid->gop_structure[idx].hierarchy_layer == 1) ? 0 : 1) + 1; // invert in new notation
            p_dst->slice_qp      = p_Vid->gop_structure[idx].slice_qp; // warning: the latter SLICE_QP may be the actual QP and *NOT* a modifier!
            p_dst->random_access = 0;

            p_dst->temporal_layer = p_Vid->gop_structure[idx].temporal_layer; 
       
            if ( p_Vid->gop_structure[idx - 1].hierarchy_layer > max_layer )
            {
              max_layer = p_Vid->gop_structure[idx - 1].hierarchy_layer;
            }
          }
          p_dst = p_cur_prd->p_frm + idx;
          p_dst->slice_type    = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE;
          p_dst->nal_ref_idc   = NALU_PRIORITY_HIGH;
          p_dst->disp_offset   = length - 1;
          p_dst->layer         = 0;
          p_dst->slice_qp      = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers)
          p_dst->random_access = 0;

          p_dst->temporal_layer = 0;
        } 
        else 
        {
          p_dst = p_cur_prd->p_frm + 0;

          p_dst->slice_type    = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE;
          p_dst->nal_ref_idc   = NALU_PRIORITY_HIGH;
          p_dst->disp_offset   = length - 1;
          p_dst->layer         = 0;
          p_dst->slice_qp      = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers)
          p_dst->random_access = 0;

          p_dst->temporal_layer = 0; // danny@vidyo.com

          for ( idx = 1; idx < length; idx++ )
          {
            p_dst = p_cur_prd->p_frm + idx;

            p_dst->slice_type    = p_Vid->gop_structure[idx - 1].slice_type;
            p_dst->nal_ref_idc   = p_Vid->gop_structure[idx - 1].reference_idc;
            p_dst->disp_offset   = p_Vid->gop_structure[idx - 1].display_no;
            p_dst->layer         = ((p_Vid->gop_structure[idx - 1].hierarchy_layer == 1) ? 0 : 1) + 1; // invert in new notation
            p_dst->slice_qp      = p_Vid->gop_structure[idx - 1].slice_qp; // warning: the latter SLICE_QP may be the actual QP and *NOT* a modifier!
            p_dst->random_access = 0;

            p_dst->temporal_layer = 0; // danny@vidyo.com

            if ( p_Vid->gop_structure[idx - 1].hierarchy_layer > max_layer )
            {
              max_layer = p_Vid->gop_structure[idx - 1].hierarchy_layer;
            }
          }
        }
        p_cur_prd->gop_levels = max_layer + 1;
      }
      break;
    }
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Populate frame structure
 * \param p_Vid
 *    pointer to the VideoParameters structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param num_to_populate
 *    number of frame unit structures to populate
 ***********************************************************************
 */

void populate_frm_struct( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_to_populate, int init_frames_to_code )
{
  // case 5: read explicit seq info and store it in the frm_struct
  int curr_frame; // in coding order
  int proc_frames;
  int is_idr;
  int is_intra;
  int is_sp;
  int terminate_pop = 0;

  p_seq_struct->curr_num_to_populate = imin( num_to_populate, init_frames_to_code - p_seq_struct->pop_start_frame );
  curr_frame  = p_seq_struct->pop_start_frame;
  proc_frames = 0;

  while ( proc_frames < p_seq_struct->curr_num_to_populate )
  {
    is_idr   = 0;
    is_intra = 0;
    is_sp    = 0;

    // we have to check here for inserted frames (e.g. due to pre-analysis) and assign them to the structure (future work)

    // establish if it is an IDR frame
    is_idr = establish_random_access( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames );
    if ( is_idr )
    {
      // random access structure
      int gop_frame = populate_rnd_acc_pred_struct_atom( p_Vid, p_Inp, p_seq_struct, curr_frame, proc_frames, &terminate_pop, is_idr, 0 );
      // update counters
      curr_frame += gop_frame;
      proc_frames += gop_frame;
      if ( terminate_pop )
      {
        terminate_pop = 0;
        break; // the proc_frames main loop
      }
    }
    else if ( !is_idr )
    {
      is_intra = establish_intra( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames );
      if ( is_intra )
      {
        // intra structure (not random access)
        int gop_frame = populate_rnd_acc_pred_struct_atom( p_Vid, p_Inp, p_seq_struct, curr_frame, proc_frames, &terminate_pop, is_intra, 1 );
        // update counters
        proc_frames += gop_frame;
        curr_frame  += gop_frame;
        if ( terminate_pop )
        {
          terminate_pop = 0;
          break; // the proc_frames main loop
        }
      }
      else // SP frames check
      {
        is_sp = establish_sp( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames );
        if ( is_sp )
        {
          // intra structure (not random access)
          int gop_frame = populate_rnd_acc_pred_struct_atom( p_Vid, p_Inp, p_seq_struct, curr_frame, proc_frames, &terminate_pop, is_sp, 2 );
          // update counters
          proc_frames += gop_frame;
          curr_frame  += gop_frame;
          if ( terminate_pop )
          {
            terminate_pop = 0;
            break; // the proc_frames main loop
          }
        }
      }
    }    
    if ( !is_idr && !is_intra && !is_sp )
    {
      int pred_frame = populate_reg_pred_struct_atom( p_Vid, p_Inp, p_seq_struct, curr_frame, proc_frames, &terminate_pop );
      // update counters
      curr_frame += pred_frame;
      proc_frames += pred_frame;
      if ( terminate_pop )
      {
        terminate_pop = 0;
        break; // the proc_frames main loop
      }
    } // is_idr is_intra is_sp
  } // proc_frames loop
  // update pop_start_frame for subsequent run of this function ;)
  p_seq_struct->pop_start_frame = curr_frame;
}

/*!
 ***********************************************************************
 * \brief
 *    Set parameters for a single coded picture (frame/field)
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_pic
 *    pointer to the current picture structure
 * \param p_frm_struct
 *    pointer to the current frame unit structure
 * \param num_slices
 *    number of slices for the current picture
 * \param is_bot_fld
 *    bottom field flag
 ***********************************************************************
 */

void populate_reg_pic( InputParameters *p_Inp, PicStructure *p_pic, FrameUnitStruct *p_frm_struct, int num_slices, int is_bot_fld )
{
  int slice;
  int slice_type    = (is_bot_fld && p_frm_struct->type == I_SLICE) ? ( p_Inp->IntraBottom ? I_SLICE : (p_Inp->BRefPictures == 2 ? B_SLICE : P_SLICE) ) : p_frm_struct->type;
  int deblock       = p_Inp->DFSendParameters ? 
    (!(p_Inp->DFDisableIdc[p_frm_struct->nal_ref_idc][p_frm_struct->type])) : 1;
  int weighted_pred = (slice_type == P_SLICE) ?
    p_Inp->WeightedPrediction : ((slice_type == P_SLICE) ? 
    p_Inp->WeightedBiprediction : 0);
  SliceStructure *p_slice;

  // if bottom field picture idr_flag has to be 0 (even when an IDR top field pic precedes it)
  p_pic->idr_flag    = !is_bot_fld ? p_frm_struct->idr_flag : 0;
  p_pic->nal_ref_idc = p_frm_struct->nal_ref_idc;
  p_pic->num_slices  = num_slices;

  for ( slice = 0; slice < p_pic->num_slices; slice++ )
  {
    // Slice Structure
    p_slice = p_pic->p_slice + slice;

    p_slice->type          = slice_type;
    p_slice->deblock       = deblock;
    p_slice->weighted_pred = weighted_pred;
    p_slice->num_refs      = p_frm_struct->num_refs;
    p_slice->qp            = p_frm_struct->qp;
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Set parameters for a single frame unit
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param p_frm_struct
 *    pointer to the current frame unit structure
 * \param p_cur_prd
 *    pointer to the current prediction structure
 * \param curr_frame
 *    global (sequence-level) coding order of the first frame (in coding order) in the PredStructAtom
 * \param pred_frame
 *    coding order with respect to "curr_frame" (pred_frame + curr_frame + idx yields the absolute coding order of the current frame)
 * \param idx
 *    local (PredStructAtom-level) coding order with respect to the start of the current PredStructAtom
 * \param num_slices
 *    number of slices for the current picture
 * \param no_rnd_acc
 *    if 1 the first frame in the Atom is not a random access frame: Intra \n
 *    if 2 it is an SP frame
 ***********************************************************************
 */

static void populate_frame( InputParameters *p_Inp, SeqStructure *p_seq_struct, FrameUnitStruct *p_frm_struct, PredStructAtom *p_cur_prd, 
                        int curr_frame, int pred_frame, int idx, int num_slices, int no_rnd_acc )
{
  PredStructFrm *p_src = p_cur_prd->p_frm + idx;

  // Frame Unit Structure
  p_frm_struct->type           = p_src->slice_type;
  p_frm_struct->random_access  = p_src->random_access;
  p_frm_struct->num_refs       = p_Inp->num_ref_frames;
  p_frm_struct->layer          = p_src->layer;
  p_frm_struct->mod_qp         = p_src->slice_qp;  
  p_frm_struct->field_pic_flag = 0; // need to add better support for field coded pictures
  p_frm_struct->frame_no       = curr_frame + pred_frame + p_src->disp_offset;
  p_frm_struct->temporal_layer = p_src->temporal_layer;

  if ( !(curr_frame + pred_frame + idx) )
  {
    // first frame in the sequence
    p_frm_struct->type        = I_SLICE;
    p_frm_struct->idr_flag    = 1;
    p_frm_struct->nal_ref_idc = NALU_PRIORITY_HIGHEST;
  }
  // disposable P/B frames
  else if ( p_Inp->DisposableP && p_frm_struct->type != I_SLICE && !p_frm_struct->layer ) // this way it will also apply to B_SLICEs in layer 0
  {
    if ( !(p_seq_struct->last_bl_frm_disposable) )
    {
      p_frm_struct->nal_ref_idc = NALU_PRIORITY_DISPOSABLE;
      p_frm_struct->mod_qp     += p_Inp->DispPQPOffset;
    }
    else
    {
      p_frm_struct->nal_ref_idc = p_src->nal_ref_idc;
    }
    p_frm_struct->idr_flag = (p_src->nal_ref_idc == NALU_PRIORITY_HIGHEST) ? 1 : 0;    
  }
  else if ( no_rnd_acc == 1 && !idx ) // no random access frame and frame first in the struct (hence I_SLICE)
  {
    p_frm_struct->idr_flag          = 0;
    p_frm_struct->nal_ref_idc       = NALU_PRIORITY_HIGH;
    p_frm_struct->random_access     = 0;
  }
  else if ( no_rnd_acc == 2 && !idx ) // no random access frame and frame first in the struct (hence SP_SLICE)
  {
    p_frm_struct->type          = p_Inp->si_frame_indicator ? SI_SLICE : SP_SLICE;
    p_frm_struct->idr_flag      = 0;
    p_frm_struct->nal_ref_idc   = NALU_PRIORITY_HIGH;
    p_frm_struct->random_access = 0;
  }
  else
  {
    p_frm_struct->idr_flag    = (p_src->nal_ref_idc == NALU_PRIORITY_HIGHEST) ? 1 : 0;
    p_frm_struct->nal_ref_idc = p_src->nal_ref_idc;
  }
  // final QP (w/o RC) set here to ensure "mod_qp" and "type" have been *finalized*
  p_frm_struct->qp             = p_frm_struct->mod_qp + p_Inp->qp[p_frm_struct->type];
#if (DEBUG_PRED_STRUCT)
  fprintf( stderr, "\n\n Frame Population Stats for Frame %5d in coding order", curr_frame + pred_frame + idx );
  switch ( p_frm_struct->type )
  {
  default:
  case I_SLICE:
    fprintf( stderr, "\n Slice Type:            I_SLICE " );
    break;
  case P_SLICE:
    fprintf( stderr, "\n Slice Type:            P_SLICE " );
    break;
  case B_SLICE:
    fprintf( stderr, "\n Slice Type:            B_SLICE " );
    break;
  case SP_SLICE:
    fprintf( stderr, "\n Slice Type:            SP_SLICE " );
    break;
  case SI_SLICE:
    fprintf( stderr, "\n Slice Type:            SI_SLICE " );
    break;
  }
  fprintf( stderr, "\n IDR picture:           %d ", p_frm_struct->idr_flag );
  switch( p_frm_struct->nal_ref_idc )
  {
  default:
  case NALU_PRIORITY_HIGHEST:
    fprintf( stderr, "\n NAL_reference_idc:     NALU_PRIORITY_HIGHEST ");
    break;
  case NALU_PRIORITY_HIGH:
    fprintf( stderr, "\n NAL_reference_idc:     NALU_PRIORITY_HIGH ");
    break;
  case NALU_PRIORITY_LOW:
    fprintf( stderr, "\n NAL_reference_idc:     NALU_PRIORITY_LOW ");
    break;
  case NALU_PRIORITY_DISPOSABLE:
    fprintf( stderr, "\n NAL_reference_idc:     NALU_PRIORITY_DISPOSABLE ");
    break;
  }
  fprintf( stderr, "\n Random Access:         %d ", p_frm_struct->random_access );
  fprintf( stderr, "\n Number of References: %2d ", p_frm_struct->num_refs );
  fprintf( stderr, "\n Hierarchy Layer:       %d ", p_frm_struct->layer );
  fprintf( stderr, "\n QP Modifier:          %2d ", p_frm_struct->qp );
  fprintf( stderr, "\n Field Coding:          %d ", p_frm_struct->field_pic_flag );
  fprintf( stderr, "\n Display Order:     %5d ", p_frm_struct->frame_no );
#endif

  // store a pointer to the prediction structure
  p_frm_struct->p_atom   = p_cur_prd;
  p_frm_struct->atom_idx = idx;

  // Picture Structure
  switch( p_Inp->PicInterlace )
  {
  default:
  case FRAME_CODING:
    // frame
    populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 );
    break;
  case FIELD_CODING:
    // top field
    populate_reg_pic( p_Inp, p_frm_struct->p_top_fld_pic, p_frm_struct, num_slices, 0 );
    // bottom field
    populate_reg_pic( p_Inp, p_frm_struct->p_bot_fld_pic, p_frm_struct, num_slices, 1 );
    break;
  case ADAPTIVE_CODING:
    // frame
    populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 );
    // top field
    populate_reg_pic( p_Inp, p_frm_struct->p_top_fld_pic, p_frm_struct, num_slices, 0 );
    // bottom field
    populate_reg_pic( p_Inp, p_frm_struct->p_bot_fld_pic, p_frm_struct, num_slices, 1 );
    break;
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Update frame indices
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param p_frm_struct
 *    pointer to the current frame unit structure
 * \param curr_frame
 *    global (sequence-level) coding order of the first frame (in coding order) in the PredStructAtom
 * \param pred_frame
 *    coding order with respect to "curr_frame" (pred_frame + curr_frame yields the absolute coding order of the current frame)
 ***********************************************************************
 */

static void update_frame_indices( SeqStructure *p_seq_struct, FrameUnitStruct *p_frm_struct, int curr_frame, int pred_frame )
{
  if ( p_frm_struct->idr_flag )
  {
    // coding order
    p_seq_struct->last_intra_frame = curr_frame + pred_frame;
    p_seq_struct->last_idr_frame   = curr_frame + pred_frame;
    p_seq_struct->last_random_access_frame = curr_frame + pred_frame;
    // display order
    p_seq_struct->last_intra_disp  = p_frm_struct->frame_no;
    p_seq_struct->last_idr_frame   = p_frm_struct->frame_no;
    p_seq_struct->last_rand_access_disp = p_frm_struct->frame_no;
    // sp frame counter (no need to send SP frames if random access frames)
    // coding order
    p_seq_struct->last_sp_frame = curr_frame + pred_frame;
    // display order
    p_seq_struct->last_sp_disp  = p_frm_struct->frame_no;
  }
  else if ( p_frm_struct->type == I_SLICE )
  {
    // coding order
    p_seq_struct->last_intra_frame = curr_frame + pred_frame;
    // display order
    p_seq_struct->last_intra_disp  = p_frm_struct->frame_no;
    if ( p_frm_struct->random_access )
    {
      // coding order
      p_seq_struct->last_random_access_frame = curr_frame + pred_frame;
      // display order
      p_seq_struct->last_rand_access_disp  = p_frm_struct->frame_no;
    }
  }
  else if ( p_frm_struct->type == SP_SLICE || p_frm_struct->type == SI_SLICE )
  {
    // coding order
    p_seq_struct->last_sp_frame = curr_frame + pred_frame;
    // display order
    p_seq_struct->last_sp_disp  = p_frm_struct->frame_no;
  }
  if ( p_frm_struct->layer == 0 ) // support for p_Inp->DisposableP
  {
    if ( p_frm_struct->nal_ref_idc == NALU_PRIORITY_DISPOSABLE )
    {
      p_seq_struct->last_bl_frm_disposable = 1;
    }
    else
    {
      p_seq_struct->last_bl_frm_disposable = 0;
    }
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Populate regular prediction structure atom
 * \param p_Vid
 *    pointer to the VideoParameters structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param curr_frame
 *    global (sequence-level) coding order of the first frame (in coding order) in the PredStructAtom
 * \param proc_frames
 *    frames that have been populated so far out of the "num_to_populate" frames
 * \param terminate_pop
 *    pointer to the terminate_pop variable that signals termination of the frame population loop
 * \return
 *    number of frames that have been populated by this function run
 ***********************************************************************
 */

static int populate_reg_pred_struct_atom( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, 
                                  int curr_frame, int proc_frames, int *terminate_pop )
{
  int idx;
  int fixed_idx;
  int avail_frames;
  int pred_frame = 0;
  int pred_idx;

  FrameUnitStruct *p_frm_struct;
  PredStructAtom *p_cur_prd;

  // regular prediction structure (*in-between* idr and intra frames though)
  // first determine how many frames are available to be used to place our prediction structures
  fixed_idx = get_fixed_frame( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames );
  if ( fixed_idx == -1 )
  {
    avail_frames = p_seq_struct->curr_num_to_populate - proc_frames;
  }
  else
  {
    avail_frames = fixed_idx; // frames including "curr_frame"
  }
  // in the future we have to take care of the avail_frames = 0 case

  // loop through these frames and apply the appropriate prediction structure (p_prd)
  while ( pred_frame < avail_frames )
  {
    pred_idx = get_prd_index( p_Inp, p_seq_struct, avail_frames - pred_frame );
    // check here whether the prediction structure does not fit even if there is no fixed frame detected; if we proceed we will allocate an inefficient pred structure; better to terminate the frame population here
    if ( fixed_idx == -1 && (curr_frame + avail_frames) < p_Inp->no_frames )
    {
      if ( (p_seq_struct->num_prds - 1) != pred_idx )
      {
        *terminate_pop = 1;
        break; // the pred_frame loop
      }
    }        
    // prediction structure pointer
    p_cur_prd = p_seq_struct->p_prd + pred_idx;
    // populate gop structure from selected structure
    // "idx" is internal pred struct idx
    // "pred_frame" is general index for the current frame starting from "curr_frame" (it is zero for "curr_frame")
    for ( idx = 0; idx < p_cur_prd->length; idx++ )//, pred_frame++ )
    {
      // set pointer
      p_frm_struct = p_seq_struct->p_frm + ((curr_frame + pred_frame + idx) % p_Vid->frm_struct_buffer);

      // assign values
      populate_frame( p_Inp, p_seq_struct, p_frm_struct, p_cur_prd, curr_frame, pred_frame, idx, p_seq_struct->max_num_slices, 0 );

      // update IDR and intra frame counters
      update_frame_indices( p_seq_struct, p_frm_struct, curr_frame, pred_frame + idx );
    }
    pred_frame += idx;
  }
  return pred_frame; // number of frames that have been populated by this function run
}

/*!
 ***********************************************************************
 * \brief
 *    Populate intra-coded/random access prediction structure atom
 * \param p_Vid
 *    pointer to the VideoParameters structure
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_seq_struct
 *    pointer to the sequence structure
 * \param curr_frame
 *    global (sequence-level) coding order of the first frame (in coding order) in the PredStructAtom
 * \param proc_frames
 *    frames that have been populated so far out of the "num_to_populate" frames
 * \param terminate_pop
 *    pointer to the terminate_pop variable that signals termination of the frame population loop
 * \param is_idr
 *    first frame of the atom will be IDR if > 0. is_idr - 1 is equal to the index of the Atom used to populate the frames
 * \param no_rnd_acc
 *    if 1, then the first frame in the (given) Atom is not a random access frame: Intra \n
 *    if 2, then it is an SP frame
 * \return
 *    number of frames that have been populated by this function run
 ***********************************************************************
 */

static int populate_rnd_acc_pred_struct_atom( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, 
                                      int curr_frame, int proc_frames, int *terminate_pop, int is_idr, int no_rnd_acc )
{
  // insert IDR pred structure (can be *more* than one frame)
  int idx;
  int fixed_idx;
  int avail_frames;
  int gop_frame = 0;
  int gop_idx;

  FrameUnitStruct *p_frm_struct;
  PredStructAtom *p_cur_gop;

  // regular prediction structure (*in-between* idr and intra frames though)
  // first determine how many frames are available to be used to place our prediction structures
  fixed_idx = get_fixed_frame( p_Inp, p_seq_struct, curr_frame + 1, p_seq_struct->curr_num_to_populate - proc_frames - 1 );
  if ( fixed_idx == -1 )
  {
    avail_frames = p_seq_struct->curr_num_to_populate - proc_frames;
  }
  else
  {
    avail_frames = fixed_idx + 1; // frames including "curr_frame"
  }
  // in the future we have to take care of the avail_frames = 0 case

  // select prediction structure for random access
  if ( !curr_frame && !(p_Inp->EnableIDRGOP) && !(p_Inp->intra_delay) )
  {
    gop_idx = 0; // force shortest IDR pred struct for first frame
  }
  else
  {
    if ( !(p_Inp->PreferDispOrder) ) // coding order
    {
      // bias in favor of longest *available* and *possible* structure
      gop_idx = get_idr_index( p_Inp, p_seq_struct, avail_frames );
    }
    else // display order
    {
      // selection here is *fixed* and is signaled within is_idr
      gop_idx = is_idr - 1;
    }
    // check here whether the prediction structure does not fit even if there is no fixed frame detected; if we proceed we will allocate an inefficient pred structure; better to terminate the frame population here
    if ( fixed_idx == -1 && (curr_frame + avail_frames) < p_Inp->no_frames )
    {
      if ( (p_seq_struct->num_gops - 1) != gop_idx )
      {
        *terminate_pop = 1;
        return 0;
      }
    }
  }
  // prediction structure pointer
  p_cur_gop = p_seq_struct->p_gop + gop_idx;
  // populate gop structure from selected structure
  for ( idx = 0; idx < p_cur_gop->length; idx++ )//, gop_frame++ )
  {
    // set pointer
    p_frm_struct = p_seq_struct->p_frm + ((curr_frame + gop_frame + idx) % p_Vid->frm_struct_buffer);
    
    // assign values
    populate_frame( p_Inp, p_seq_struct, p_frm_struct, p_cur_gop, curr_frame, gop_frame, idx, p_seq_struct->max_num_slices, no_rnd_acc );

    // update IDR and intra frame counters
    update_frame_indices( p_seq_struct, p_frm_struct, curr_frame, gop_frame + idx );
  }
  gop_frame += idx;

  return gop_frame; // populated frames
}

/*!
 ***********************************************************************
 * \brief
 *    Free random access prediction structures
 * \param p_seq_struct
 *    pointer to the sequence structure
 ***********************************************************************
 */

static void free_gop_struct( SeqStructure *p_seq_struct )
{
  int idx;
  PredStructAtom *p_cur_gop;

  // populate gop structure from selected structure
  for ( idx = 0; idx < p_seq_struct->num_gops; idx++ )
  {
    p_cur_gop = p_seq_struct->p_gop + idx;
    free( p_cur_gop->p_frm );
  }
  free( p_seq_struct->p_gop );
}

/*!
 ***********************************************************************
 * \brief
 *    Free non-random access prediction structures
 * \param p_seq_struct
 *    pointer to the sequence structure
 ***********************************************************************
 */

static void free_pred_struct( SeqStructure *p_seq_struct )
{
  int idx;
  PredStructAtom *p_cur_prd;

  // free pred structures
  for ( idx = 0; idx < p_seq_struct->num_prds; idx++ )
  {
    p_cur_prd = p_seq_struct->p_prd + idx;
    free( p_cur_prd->p_frm );
  }
  free( p_seq_struct->p_prd );
}

/*!
 ***********************************************************************
 * \brief
 *    Set parameters for a single frame from the explicit structure
 * \param info
 *    pointer to the Explicit Structure information for the current frame
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_frm_struct
 *    pointer to the current frame structure
 * \param num_slices
 *    number of slices used to code this picture
 ***********************************************************************
 */

void populate_frame_explicit( ExpFrameInfo *info, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct, int num_slices )
{
  // Frame Unit Structure
  p_frm_struct->idr_flag       = info->is_idr;
  p_frm_struct->nal_ref_idc    = info->reference_idc;
  p_frm_struct->type           = info->slice_type;
  p_frm_struct->random_access  = info->is_idr;
  p_frm_struct->num_refs       = p_Inp->num_ref_frames;
  p_frm_struct->layer          = info->reference_idc ? 0 : 1;
  p_frm_struct->mod_qp         = 0;
  p_frm_struct->field_pic_flag = 0;
  p_frm_struct->frame_no       = info->seq_number;
  p_frm_struct->qp             = p_frm_struct->mod_qp + p_Inp->qp[p_frm_struct->type];

  // Picture Structure
  switch( p_Inp->PicInterlace )
  {
  default:
  case FRAME_CODING:
    // frame
    populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 );
    break;
  case FIELD_CODING:
    // top field
    populate_reg_pic( p_Inp, p_frm_struct->p_top_fld_pic, p_frm_struct, num_slices, 0 );
    // bottom field
    populate_reg_pic( p_Inp, p_frm_struct->p_bot_fld_pic, p_frm_struct, num_slices, 1 );
    break;
  case ADAPTIVE_CODING:
    // frame
    populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 );
    // top field
    populate_reg_pic( p_Inp, p_frm_struct->p_top_fld_pic, p_frm_struct, num_slices, 0 );
    // bottom field
    populate_reg_pic( p_Inp, p_frm_struct->p_bot_fld_pic, p_frm_struct, num_slices, 1 );
    break;
  }
}

/*!
 ***********************************************************************
 * \brief
 *    Set parameters for a single frame during RD-picture decision
 * \param p_Inp
 *    pointer to the InputParameters structure
 * \param p_frm_struct
 *    pointer to the current frame structure
 * \param slice_type
 *    NEW slice type that is set
 * \param num_slices
 *    number of slices used to code this picture
 ***********************************************************************
 */

void populate_frame_slice_type( InputParameters *p_Inp, FrameUnitStruct *p_frm_struct, int slice_type, int num_slices )
{
  p_frm_struct->type = slice_type;

  // frame (RDPictureDecision only called for frame pictures so no need to check here)
  populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 );
}
