
/* mpeg2dec.c, main(), initialization, option processing                    */

/* 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 "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#define GLOBAL
#include "mpeg-config.h"
#include "global.h"

#include "mpeg2.h"

/* private prototypes */
static int  video_sequence _ANSI_ARGS_((struct mpeg2obj *m, int *framenum));
static int Decode_Bitstream _ANSI_ARGS_((struct mpeg2obj *m));
static int  Headers _ANSI_ARGS_((struct mpeg2obj *m));


#if OLD
static int  Get_Val _ANSI_ARGS_((char *argv[]));
#endif

/* IMPLEMENTAION specific rouintes */
static void Initialize_Decoder(struct mpeg2obj *m)
{
  int i;

  /* Clip table */
  if (!(m->Clip=(unsigned char *)malloc(1024)))
    _mpeg2Error(m, "Clip[] malloc failed\n");

  m->Clip += 384;

  for (i=-384; i<640; i++)
    m->Clip[i] = (i<0) ? 0 : ((i>255) ? 255 : i);

  /* IDCT */
  if (m->Reference_IDCT_Flag)
    _mpeg2Initialize_Reference_IDCT(m);
  else
    _mpeg2Initialize_Fast_IDCT(m);

}

/* mostly IMPLEMENTAION specific rouintes */
static void Initialize_Sequence(struct mpeg2obj *m)
{
  int cc, size;
  static int Table_6_20[3] = {6,8,12};

  /* check scalability mode of enhancement layer */
  if (m->Two_Streams && (m->enhan.scalable_mode!=SC_SNR) && (m->base.scalable_mode!=SC_DP))
    _mpeg2Error(m,"unsupported scalability mode\n");

  /* force MPEG-1 parameters for proper decoder behavior */
  /* see ISO/IEC 13818-2 section D.9.14 */
  if (!m->base.MPEG2_Flag)
  {
    m->progressive_sequence = 1;
    m->progressive_frame = 1;
    m->picture_structure = FRAME_PICTURE;
    m->frame_pred_frame_dct = 1;
    m->chroma_format = CHROMA420;
    m->matrix_coefficients = 5;
  }

  /* round to nearest multiple of coded macroblocks */
  /* ISO/IEC 13818-2 section 6.3.3 sequence_header() */
  m->mb_width = (m->horizontal_size+15)/16;
  m->mb_height = (m->base.MPEG2_Flag && !m->progressive_sequence) ? 2*((m->vertical_size+31)/32)
                                        : (m->vertical_size+15)/16;

  m->Coded_Picture_Width = 16*m->mb_width;
  m->Coded_Picture_Height = 16*m->mb_height;

  /* ISO/IEC 13818-2 sections 6.1.1.8, 6.1.1.9, and 6.1.1.10 */
  m->Chroma_Width = (m->chroma_format==CHROMA444) ? m->Coded_Picture_Width
                                           : m->Coded_Picture_Width>>1;
  m->Chroma_Height = (m->chroma_format!=CHROMA420) ? m->Coded_Picture_Height
                                            : m->Coded_Picture_Height>>1;
  
  /* derived based on Table 6-20 in ISO/IEC 13818-2 section 6.3.17 */
  m->block_count = Table_6_20[m->chroma_format-1];

  for (cc=0; cc<3; cc++)
  {
    if (cc==0)
      size = m->Coded_Picture_Width*m->Coded_Picture_Height;
    else
      size = m->Chroma_Width*m->Chroma_Height;

    if (!(m->backward_reference_frame[cc] = (unsigned char *)malloc(size)))
      _mpeg2Error(m,"backward_reference_frame[] malloc failed\n");

    if (!(m->forward_reference_frame[cc] = (unsigned char *)malloc(size)))
      _mpeg2Error(m,"forward_reference_frame[] malloc failed\n");

    if (!(m->auxframe[cc] = (unsigned char *)malloc(size)))
      _mpeg2Error(m,"auxframe[] malloc failed\n");

    if(m->Ersatz_Flag)
      if (!(m->substitute_frame[cc] = (unsigned char *)malloc(size)))
        _mpeg2Error(m,"substitute_frame[] malloc failed\n");


    if (m->base.scalable_mode==SC_SPAT)
    {
      /* this assumes lower layer is 4:2:0 */
      if (!(m->llframe0[cc] = (unsigned char *)malloc((m->lower_layer_prediction_horizontal_size*m->lower_layer_prediction_vertical_size)/(cc?4:1))))
        _mpeg2Error(m,"llframe0 malloc failed\n");
      if (!(m->llframe1[cc] = (unsigned char *)malloc((m->lower_layer_prediction_horizontal_size*m->lower_layer_prediction_vertical_size)/(cc?4:1))))
        _mpeg2Error(m,"llframe1 malloc failed\n");
    }
  }

  /* SCALABILITY: Spatial */
  if (m->base.scalable_mode==SC_SPAT)
  {
    if (!(m->lltmp = (short *)malloc(m->lower_layer_prediction_horizontal_size*((m->lower_layer_prediction_vertical_size*m->vertical_subsampling_factor_n)/m->vertical_subsampling_factor_m)*sizeof(short))))
      _mpeg2Error(m,"lltmp malloc failed\n");
  }

#ifdef DISPLAY
  if (Output_Type==T_X11)
  {
    Initialize_Display_Process("");
    Initialize_Dither_Matrix();
  }
#endif /* DISPLAY */

}

void _mpeg2Error(m,text)
  struct mpeg2obj *m;
const char *text;
{
  fprintf(stderr,text);
  exit(1);
}

/* Trace_Flag output */
void _mpeg2Print_Bits(m,code,bits,len)
  struct mpeg2obj *m;
int code,bits,len;
{
  int i;
  for (i=0; i<len; i++)
    printf("%d",(code>>(bits-1-i))&1);
}

static int Headers (struct mpeg2obj *m)
{
  int ret;

  m->ld = &m->base;
  

  /* return when end of sequence (0) or picture
     header has been parsed (1) */

  ret = _mpeg2Get_Hdr(m);


  if (m->Two_Streams)
  {
    m->ld = &m->enhan;
    if (_mpeg2Get_Hdr(m)!=ret && !_mpeg2Quiet_Flag)
      fprintf(stderr,"streams out of sync\n");
    m->ld = &m->base;
  }

  return ret;
}



static int Decode_Bitstream(struct mpeg2obj *m)
{
  int ret;
  int Bitstream_Framenum;

  Bitstream_Framenum = 0;

  for(;;)
  {

    ret = Headers(m);
    
    if(ret==1)
    {
      ret = video_sequence(m,&Bitstream_Framenum);
    }
    else
      return(ret);
  }

}


static void Deinitialize_Sequence(struct mpeg2obj *m)
{
  int i;

  /* clear flags */
  m->base.MPEG2_Flag=0;

  for(i=0;i<3;i++)
  {
    free(m->backward_reference_frame[i]);
    free(m->forward_reference_frame[i]);
    free(m->auxframe[i]);

    if (m->base.scalable_mode==SC_SPAT)
    {
     free(m->llframe0[i]);
     free(m->llframe1[i]);
    }
  }

  if (m->base.scalable_mode==SC_SPAT)
    free(m->lltmp);
}


static int video_sequence(struct mpeg2obj *m, int *Bitstream_Framenumber)
{
  int Bitstream_Framenum;
  int Sequence_Framenum;
  int Return_Value;

  Bitstream_Framenum = *Bitstream_Framenumber;
  Sequence_Framenum=0;

  Initialize_Sequence(m);

  /* decode picture whose header has already been parsed in 
     Decode_Bitstream() */


  _mpeg2Decode_Picture(m,Bitstream_Framenum, Sequence_Framenum);

  /* update picture numbers */
  if (!m->Second_Field)
  {
    Bitstream_Framenum++;
    Sequence_Framenum++;
  }

  /* loop through the rest of the pictures in the sequence */
  while ((Return_Value=Headers(m)))
  {
    _mpeg2Decode_Picture(m,Bitstream_Framenum, Sequence_Framenum);

    if (!m->Second_Field)
    {
      Bitstream_Framenum++;
      Sequence_Framenum++;
    }
  }

  /* put last frame */
  if (Sequence_Framenum!=0)
  {
    _mpeg2Output_Last_Frame_of_Sequence(m,Bitstream_Framenum);
  }

  Deinitialize_Sequence(m);

  *Bitstream_Framenumber = Bitstream_Framenum;
  return(Return_Value);
}

static void Clear_Options(struct mpeg2obj *m)
{
  _mpeg2Verbose_Flag = 0;
  m->hiQdither  = 0;
  m->Frame_Store_Flag = 0;
  m->Spatial_Flag = 0;
  m->Lower_Layer_Picture_Filename = " ";
  m->Reference_IDCT_Flag = 0;
  m->Ersatz_Flag = 0;
  m->Substitute_Picture_Filename  = " ";
  m->Two_Streams = 0;
  m->Enhancement_Layer_Bitstream_Filename = " ";
  m->Big_Picture_Flag = 0;
  m->Main_Bitstream_Filename = " ";
  m->Verify_Flag = 0;
  m->Stats_Flag  = 0;
  m->User_Data_Flag = 0; 
}


/*----------------------------------------------------------------------*/

/* Program-level initializations. */
void
mpeg2Init ()
{
}

/* Load a file ready for decoding it. */
void *
mpeg2OpenFile (const char *filename)
{
  int code;
  struct mpeg2obj *m;

  m = malloc (sizeof (struct mpeg2obj));
  if (m == 0) { return 0; }

  /* Set options as if called from the command line. */
  Clear_Options (m);
  _mpeg2Quiet_Flag = 1;

  Initialize_Decoder (m);

  /* Here we copy what the old main() function did. */
  m->Main_Bitstream_Filename = filename;
  m->base.Infile = open (m->Main_Bitstream_Filename, O_RDONLY);

  if (m->base.Infile < 0) { free (m); return 0; }

  m->ld = &m->base;

  _mpeg2Initialize_Buffer (m);

  if (_mpeg2Show_Bits (m, 8) == 0x47)
    {
      close (m->base.Infile);
      m->base.Infile = -1;
      free (m);
      return 0;
    }

  _mpeg2next_start_code (m);
  code = _mpeg2Show_Bits (m, 32);

  switch(code)
    {
    case SEQUENCE_HEADER_CODE:
      break;
    case PACK_START_CODE:
      m->System_Stream_Flag = 1;
    case VIDEO_ELEMENTARY_STREAM:
      m->System_Stream_Flag = 1;
      break;
    default:
      close (m->base.Infile);
      m->base.Infile = -1;
      free (m);
      return 0;
    }

  lseek (m->base.Infile, 0l, 0);

  _mpeg2Initialize_Buffer (m);

  /* Initialize local variables. */
  m->bitstream_framenum = m->sequence_framenum = 0;
  m->state = STATE_FIRST_IN_SEQUENCE;
  m->scale_width_up = 1;
  m->scale_height_up = 1;
  m->scale_width_down = 0;
  m->scale_height_down = 0;
  m->output_buffer = 0;
  m->Temporal_Reference_Base = 0;
  m->True_Framenum_max  = -1;
  m->Temporal_Reference_GOP_Reset = 0;

  /* Read the header. */
  if (_mpeg2Get_Hdr (m) != 1)
    {
      close (m->base.Infile);
      m->base.Infile = -1;
      free (m);
      return 0;
    }

  return (void *) m;
}

/* Unload a file. */
void
mpeg2CloseFile (void *mv)
{
  struct mpeg2obj *m = (struct mpeg2obj *)mv;

  if (m->base.Infile >= 0)
    close (m->base.Infile);

  free (m);
}

/* Get the next frame. */
int
mpeg2ReadNextFrame (void *mv)
{
  struct mpeg2obj *m = (struct mpeg2obj *)mv;
  int r;

  /* For those following the source, this is Decode_Bitstream() and
   * video_sequence(). The logic is a bit convoluted.
   */
  switch (m->state)
    {
    case STATE_FIRST_IN_SEQUENCE:
      {
        Initialize_Sequence (m);

        _mpeg2Decode_Picture (m, m->bitstream_framenum, m->sequence_framenum);

        if (!m->Second_Field)
          {
            m->bitstream_framenum ++;
            m->sequence_framenum ++;
          }

        m->state = STATE_NEXT_IN_SEQUENCE;
        break;
      }
    case STATE_NEXT_IN_SEQUENCE:
      {
        r = _mpeg2Get_Hdr (m);

        if (r)
          {
            _mpeg2Decode_Picture (m, m->bitstream_framenum, m->sequence_framenum);

            if (!m->Second_Field)
              {
                m->bitstream_framenum ++;
                m->sequence_framenum ++;
              }
          }
        else
          {
            if (m->sequence_framenum != 0)
              {
                _mpeg2Output_Last_Frame_of_Sequence (m, m->bitstream_framenum);
              }

            Deinitialize_Sequence (m);

	    /* Start next sequence. */
	    r = _mpeg2Get_Hdr (m);

	    if (r != 1) { return 0; }

            m->state = STATE_FIRST_IN_SEQUENCE;
          }
        break;
      }
    } /* switch (state) */

  return 1;
}

/* Read the width and height. */
int
mpeg2GetWidth (void *mv)
{
  struct mpeg2obj *m = (struct mpeg2obj *) mv;

  return m->horizontal_size;
}

int
mpeg2GetHeight (void *mv)
{
  struct mpeg2obj *m = (struct mpeg2obj *) mv;

  return m->vertical_size;
}

/* Set the scale factors. */
void
mpeg2ScaleWidthUp (void *mv, int f)
{
  struct mpeg2obj *m = (struct mpeg2obj *) mv;

  m->scale_width_down = 0;
  m->scale_width_up = f;
}

void
mpeg2ScaleWidthDown (void *mv, int f)
{
  struct mpeg2obj *m = (struct mpeg2obj *) mv;

  m->scale_width_down = f;
  m->scale_width_up = 0;
}

void
mpeg2ScaleHeightUp (void *mv, int f)
{
  struct mpeg2obj *m = (struct mpeg2obj *) mv;

  m->scale_height_down = 0;
  m->scale_height_up = f;
}

void
mpeg2ScaleHeightDown (void *mv, int f)
{
  struct mpeg2obj *m = (struct mpeg2obj *) mv;

  m->scale_height_down = f;
  m->scale_height_up = 0;
}

void
mpeg2SetOutputFormat (void *mv, int f)
{
  /* Do nothing. */
}

void
mpeg2SetOutputBuffer (void *mv, void *output_buffer)
{
  struct mpeg2obj *m = (struct mpeg2obj *) mv;

  m->output_buffer = output_buffer;
}
