/* #define DEBUG */

/* subspic.c, Frame buffer substitution routines */



/* Copyright (C) 1996, MPEG Software Simulation Group. All Rights Reserved. */



/*

 * Disclaimer of Warranty

 *

 * These software programs are available to the user without any license fee or

 * royalty on an "as is" basis.  The MPEG Software Simulation Group disclaims

 * any and all warranties, whether express, implied, or statuary, including any

 * implied warranties or merchantability or of fitness for a particular

 * purpose.  In no event shall the copyright-holder be liable for any

 * incidental, punitive, or consequential damages of any kind whatsoever

 * arising from the use of these programs.

 *

 * This disclaimer of warranty extends to the user of these programs and user's

 * customers, employees, agents, transferees, successors, and assigns.

 *

 * The MPEG Software Simulation Group does not represent or warrant that the

 * programs furnished hereunder are free of infringement of any third-party

 * patents.

 *

 * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,

 * are subject to royalty fees to patent holders.  Many of these patents are

 * general enough such that they are unavoidable regardless of implementation

 * design.

 *

 */



#include <stdio.h>

//#include <libc.h>

#include <stdlib.h>

#include <fcntl.h>



#include "config.h"

#include "global.h"



/* private prototypes*/

static void Read_Frame _ANSI_ARGS_((char *filename, 

  unsigned char *frame_buffer[], int framenum));

static void Copy_Frame _ANSI_ARGS_((unsigned char *src, unsigned char *dst, 

  int width, int height, int parity, int incr));

static int Read_Components _ANSI_ARGS_ ((char *filename, 

  unsigned char *frame[3], int framenum));

static int Read_Component _ANSI_ARGS_ ((char *fname, unsigned char *frame, 

  int width, int height));

static int Extract_Components _ANSI_ARGS_ ((char *filename,

  unsigned char *frame[3], int framenum));





/* substitute frame buffer routine */

void Substitute_Frame_Buffer (bitstream_framenum, sequence_framenum)

int bitstream_framenum;

int sequence_framenum;

{

  /* static tracking variables */

  static int previous_temporal_reference;

  static int previous_bitstream_framenum;

  static int previous_anchor_temporal_reference;

  static int previous_anchor_bitstream_framenum;

  static int previous_picture_coding_type;

  static int bgate;

  

  /* local temporary variables */

  int substitute_display_framenum;





#ifdef DEBUG

  printf("SUB: seq fn(%d) bitfn(%d) tempref(%d) picstr(%d) type(%d)\n", 

    sequence_framenum, bitstream_framenum, temporal_reference, 

    picture_structure, picture_coding_type);

#endif



  /* we don't substitute at the first picture of a sequence */

  if((sequence_framenum!=0)||(Second_Field))

  {

    /* only at the start of the frame */

    if ((picture_structure==FRAME_PICTURE)||(!Second_Field))

    {

      if(picture_coding_type==P_TYPE)

      {

        /* the most recently decoded reference frame needs substituting */

        substitute_display_framenum = bitstream_framenum - 1;

        

        Read_Frame(Substitute_Picture_Filename, forward_reference_frame, 

          substitute_display_framenum);

      }

      /* only the first B frame in a consequitve set of B pictures

         loads a substitute backward_reference_frame since all subsequent

         B frames predict from the same reference pictures */

      else if((picture_coding_type==B_TYPE)&&(bgate!=1))

      {

        substitute_display_framenum = 

          (previous_temporal_reference - temporal_reference) 

            + bitstream_framenum - 1;



        Read_Frame(Substitute_Picture_Filename, backward_reference_frame, 

          substitute_display_framenum);

      }

    } /* P fields can predict from the two most recently decoded fields, even

         from the first field of the same frame being decoded */

    else if(Second_Field && (picture_coding_type==P_TYPE))

    {

      /* our favourite case: the IP field picture pair */

      if((previous_picture_coding_type==I_TYPE)&&(picture_coding_type==P_TYPE))

      {

        substitute_display_framenum = bitstream_framenum;

      }

      else /* our more generic P field picture pair */

      {

        substitute_display_framenum = 

          (temporal_reference - previous_anchor_temporal_reference) 

            + bitstream_framenum - 1;

      }



      Read_Frame(Substitute_Picture_Filename, current_frame, substitute_display_framenum);

    }

#ifdef DEBUG

    else if((picture_coding_type!=B_TYPE)||(picture_coding_type!=D_TYPE))

    {

      printf("NO SUBS FOR THIS PICTURE\n");

    }

#endif

  }





  /* set b gate so we don't redundantly load next time around */

  if(picture_coding_type==B_TYPE)

    bgate = 1;

  else

    bgate = 0;



  /* update general tracking variables */

  if((picture_structure==FRAME_PICTURE)||(!Second_Field))

  {

    previous_temporal_reference  = temporal_reference;

    previous_bitstream_framenum  = bitstream_framenum;

  }

  

  /* update reference frame tracking variables */

  if((picture_coding_type!=B_TYPE) && 

    ((picture_structure==FRAME_PICTURE)||Second_Field))

  {

    previous_anchor_temporal_reference  = temporal_reference;

    previous_anchor_bitstream_framenum  = bitstream_framenum;

  }



  previous_picture_coding_type = picture_coding_type;



}





/* Note: fields are only read to serve as the same-frame reference for 

   a second field */

static void Read_Frame(fname,frame,framenum)

char *fname;

unsigned char *frame[];

int framenum;

{

  int parity;

  int rerr = 0;

  int field_mode;



  if(framenum<0)

    printf("ERROR: framenum (%d) is less than zero\n", framenum);





  if(Big_Picture_Flag)

    rerr = Extract_Components(fname, substitute_frame, framenum);

  else

    rerr = Read_Components(fname, substitute_frame, framenum);



  if(rerr!=0)

  {

    printf("was unable to substitute frame\n");

  }



  /* now copy to the appropriate buffer */

  /* first field (which we are attempting to substitute) must be

     of opposite field parity to the current one */

  if((Second_Field)&&(picture_coding_type==P_TYPE))

  {

    parity      = (picture_structure==TOP_FIELD ? 1:0);      

    field_mode  = (picture_structure==FRAME_PICTURE ? 0:1);

  }

  else

  {

    /* Like frame structued pictures, B pictures only substitute an entire frame 

       since both fields always predict from the same frame (with respect 

       to forward/backwards directions) */

    parity = 0;

    field_mode = 0;

  }





  Copy_Frame(substitute_frame[0], frame[0], Coded_Picture_Width, 

    Coded_Picture_Height, parity, field_mode);

  

  Copy_Frame(substitute_frame[1], frame[1], Chroma_Width, Chroma_Height, 

    parity, field_mode);

  

  Copy_Frame(substitute_frame[2], frame[2], Chroma_Width, Chroma_Height,

    parity, field_mode);



#ifdef VERBOSE

  if(Verbose_Flag > NO_LAYER)

    printf("substituted %s %d\n",

      (field_mode ? (parity?"bottom field":"bottom field"):"frame"), framenum);

#endif

}









static int Read_Components(filename, frame, framenum) 

char *filename;

unsigned char *frame[3];

int framenum;

{

  int err = 0;

  char outname[FILENAME_LENGTH];

  char name[FILENAME_LENGTH];



  sprintf(outname,filename,framenum);





  sprintf(name,"%s.Y",outname);

  err += Read_Component(name, frame[0], Coded_Picture_Width, 

    Coded_Picture_Height);



  sprintf(name,"%s.U",outname);

  err += Read_Component(name, frame[1], Chroma_Width, Chroma_Height);



  sprintf(name,"%s.V",outname);

  err += Read_Component(name, frame[2], Chroma_Width, Chroma_Height);



  return(err);

}





static int Read_Component(Filename, Frame, Width, Height)

char *Filename;

unsigned char *Frame;

int Width;

int Height;

{

  int Size;

  int Bytes_Read;

  int Infile;



  Size = Width*Height;



#ifdef DEBUG

  printf("SUBS: reading %s\n", filename);

#endif



  if(!(Infile=open(Filename,O_RDONLY|O_BINARY))<0)

  {

    printf("ERROR: unable to open reference filename (%s)\n", Filename);

	return(-1);

  }



  Bytes_Read = read(Infile, Frame, Size);

  

  if(Bytes_Read!=Size)

  {

    printf("was able to read only %d bytes of %d of file %s\n",

      Bytes_Read, Size, Filename);

  }

 

  close(Infile); 

  return(0);

}





/* optimization: do not open the big file each time. Open once at start

   of decoder, and close at the very last frame */



/* Note: "big" files were used in E-mail exchanges almost exclusively by the 

   MPEG Committee's syntax validation and conformance ad-hoc groups from 

   the year 1993 until 1995 */

static int Extract_Components(filename, frame, framenum) 

char *filename;

unsigned char *frame[3];

int framenum;

{

  int err = 0;

  FILE *fd;

  int line;

  int size, offset;





  if (!(fd = fopen(filename,"rb")))

  {

    sprintf(Error_Text,"Couldn't open %s\n",filename);

    return(-1);

  }



  /* compute size of each frame (in bytes) */

  size = (Coded_Picture_Width*Coded_Picture_Height);



  if(chroma_format==CHROMA444)

    size = (size * 3);

  else if(chroma_format==CHROMA422)

    size = (size * 2);

  else if(chroma_format==CHROMA420)

    size = ((size*3)>>1);

  else

    printf("ERROR: chroma_format (%d) not recognized\n", chroma_format);





  /* compute distance into "big" file */

  offset = size*framenum;



#ifdef DEBUG

  printf("EXTRACTING: frame(%d) offset(%d), size (%d) from %s\n", 

    framenum, offset, size, filename);

#endif



  /* seek to location in big file where desired frame begins */

  /* note: this offset cannot exceed a few billion bytes due to the */

  /*       obvious limitations of 32-bit integers */

  fseek(fd, offset, 0);



  /* Y  */

  for (line=0; line<Coded_Picture_Height; line++)

  {

    fread(frame[0]+(line*Coded_Picture_Width),1,Coded_Picture_Width,fd);

  }



  /* Cb */

  for (line=0; line<Chroma_Height; line++)

  {

    fread(frame[1]+(line*Chroma_Width),1,Chroma_Width,fd);

  }



  /* Cr */

  for (line=0; line<Chroma_Height; line++)

  {

    fread(frame[2]+(line*Chroma_Width),1,Chroma_Width,fd);

  }





  fclose(fd);

  return(0);

}





static void Copy_Frame(src, dst, width, height, parity, field_mode)

unsigned char *src;

unsigned char *dst;

int width;

int height;

int parity;        /* field parity (top or bottom) to overwrite */

int field_mode;    /* 0 = frame, 1 = field                      */

{

  int row, col;

  int s, d;

  int incr;



  s = d = 0;



#ifdef DEBUG

  printf("COPYING (w=%d, h=%d, parity=%d, field_mode=%d)\n",

    width,height,parity,field_mode);

#endif /* DEBUG */



  if(field_mode)

  {

    incr = 2;



    if(parity==0)

      s += width;

  }

  else

  {

    incr = 1;

  }



  for(row=0; row<height; row+=incr) 

  {

    for(col=0; col<width; col++)

    {

      dst[d+col] = src[s+col];

    }

    

    d += (width*incr);

    s += (width*incr);

  }



}



