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

/* Copyright (C) 1994, 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 <stdlib.h>
#include <ctype.h>
#include <fcntl.h>

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

/* private prototypes */
static void initdecoder _ANSI_ARGS_((void));
static void options _ANSI_ARGS_((int *argcp, char **argvp[]));
static int getval _ANSI_ARGS_((char *argv[]));

int main(argc,argv)
int argc;
char *argv[];
{
  int first, framenum;

  options(&argc,&argv);

  /* pointer to name of output files */
#ifdef DISPLAY
  if (outtype==T_X11)
    outputname = "";
  else
#endif
    outputname = argv[argc-1];

  ld = &base; /* select base layer context */

  /* open MPEG input file(s) */
  if ((base.infile=open(argv[1],O_RDONLY|O_BINARY))<0)
  {
    sprintf(errortext,"Input file %s not found\n",argv[1]);
    error(errortext);
  }

  initbits();

  if (argc==4)
  {
    ld = &enhan; /* select enhancement layer context */

    if ((enhan.infile = open(argv[2],O_RDONLY|O_BINARY))<0)
    {
      sprintf(errortext,"Input file %s not found\n",argv[2]);
      error(errortext);
    }

    twostreams = 1;
    initbits();
    ld = &base;
  }

  first = 1;
  framenum = 0;

  while (getheader())
  {
    if (first)
    {
      initdecoder();
      first = 0;
    }

    getpicture(framenum);

    if (!secondfield)
      framenum++;
  }

  if (framenum!=0)
  {
    /* put last frame */
    putlast(framenum);
  }

  close(base.infile);

  if (twostreams)
    close(enhan.infile);

#ifdef DISPLAY
  if (outtype==T_X11)
    exit_display();
#endif

  return 0;
}

static void initdecoder()
{
  int i, cc, size;
  static int blk_cnt_tab[3] = {6,8,12};

  /* check scalability mode of enhancement layer */
  if (twostreams && enhan.scalable_mode!=SC_SNR &&
     !(base.scalable_mode==SC_DP && base.scalable_mode==SC_DP))
    error("unsupported scalability mode\n");

  /* clip table */
  if (!(clp=(unsigned char *)malloc(1024)))
    error("malloc failed\n");

  clp += 384;

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

  /* force MPEG-1 parameters */
  if (!base.mpeg2)
  {
    prog_seq = 1;
    prog_frame = 1;
    pict_struct = FRAME_PICTURE;
    frame_pred_dct = 1;
    chroma_format = CHROMA420;
    matrix_coefficients = 5;
  }

  /* round to nearest multiple of coded macroblocks */
  mb_width = (horizontal_size+15)/16;
  mb_height = (base.mpeg2 && !prog_seq) ? 2*((vertical_size+31)/32)
                                        : (vertical_size+15)/16;
  coded_picture_width = 16*mb_width;
  coded_picture_height = 16*mb_height;

  chrom_width = (chroma_format==CHROMA444) ? coded_picture_width
                                           : coded_picture_width>>1;
  chrom_height = (chroma_format!=CHROMA420) ? coded_picture_height
                                            : coded_picture_height>>1;
  blk_cnt = blk_cnt_tab[chroma_format-1];

  for (cc=0; cc<3; cc++)
  {
    if (cc==0)
      size = coded_picture_width*coded_picture_height;
    else
      size = chrom_width*chrom_height;

    if (!(refframe[cc] = (unsigned char *)malloc(size)))
      error("malloc failed\n");

    if (!(oldrefframe[cc] = (unsigned char *)malloc(size)))
      error("malloc failed\n");

    if (!(auxframe[cc] = (unsigned char *)malloc(size)))
      error("malloc failed\n");

    if (base.scalable_mode==SC_SPAT)
    {
      /* this assumes lower layer is 4:2:0 */
      if (!(llframe0[cc] = (unsigned char *)malloc((llw*llh)/(cc?4:1))))
        error("malloc failed\n");
      if (!(llframe1[cc] = (unsigned char *)malloc((llw*llh)/(cc?4:1))))
        error("malloc failed\n");
    }
  }

  if (base.scalable_mode==SC_SPAT)
  {
    if (!(lltmp = (short *)malloc(llw*((llh*vn)/vm)*sizeof(short))))
      error("malloc failed\n");
  }

  /* IDCT */
  if (refidct)
    init_idctref();
  else
    init_idct();

#ifdef DISPLAY
  if (outtype==T_X11)
  {
    init_display("");
    init_dither();
  }
#endif
}

void error(text)
char *text;
{
  fprintf(stderr,text);
  exit(1);
}

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

/* option processing */
static void options(argcp,argvp)
int *argcp;
char **argvp[];
{
  while (*argcp>1 && (*argvp)[1][0]=='-')
  {
    while ((*argvp)[1][1])
    {
      switch (toupper((*argvp)[1][1]))
      {
      case 'V':
        verbose = getval(*argvp);
        break;
      case 'O':
        outtype = getval(*argvp);
#ifdef DISPLAY
        if (outtype==T_X11HIQ)
        {
          hiQdither = 1;
          outtype=T_X11;
        }
#endif
        break;
      case 'F':
        framestoreflag = 1;
        break;
      case 'S':
        sflag = 1;
        break;
      case 'R':
        refidct = 1;
        break;
      case 'T':
        trace = 1;
        break;
      case 'Q':
        quiet = 1;
        break;
      default:
        fprintf(stderr,"undefined option -%c ignored\n",(*argvp)[1][1]);
      }

      (*argvp)[1]++;
    }

    (*argvp)++;
    (*argcp)--;
  }

  if (sflag)
  {
    /* input file for spatial prediction */
    llinputname = (*argvp)[1];
    (*argvp)++;
    (*argcp)--;
  }

#ifdef DISPLAY
  if (outtype==T_X11)
  {
    framestoreflag = 1; /* two avoid calling dither() twice */
    (*argcp)++; /* fake outfile parameter */
  }
#endif

  if (*argcp!=3 && *argcp!=4)
  {
    printf("\n%s, %s\n",version,author);
    printf("Usage:   mpeg2decode {options} input.m2v {upper.m2v} {outfile}\n\
Options: -vn  verbose output (n: level)\n\
         -on  output format (0: YUV, 1: SIF, 2: TGA, 3:PPM, 4:X11, 5:X11 HiQ)\n\
         -f   store interlaced video in frame format\n\
         -q   disable warnings to stderr\n\
         -r   use double precision reference IDCT\n\
         -s infile  spatial scalable sequence\n\
         -t   enable low level tracing\n");
    exit(0);
  }
}

static int getval(argv)
char *argv[];
{
  int val;

  if (sscanf(argv[1]+2,"%d",&val)!=1)
    return 0;

  while (isdigit(argv[1][2]))
    argv[1]++;

  return val;
}
