/*
 * main.c --
 *
 *      Main procedure
 *
 */

/*
 * Copyright (c) 1995 The Regents of the University of California.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
 * Portions of this software Copyright (c) 1995 Brown University.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement
 * is hereby granted, provided that the above copyright notice and the
 * following two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
 * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

#include "video.h"
#include "proto.h"
#ifndef NOCONTROLS
#include "ctrlbar.h"
#endif
#include <math.h>
#include <sys/types.h>
#include <signal.h>
#ifndef WIN32
#include <netinet/in.h>
#endif
#include <string.h> /* strtok */
#include "util.h"
#include "dither.h"

/*
   Changes to make the code reentrant:
     Got rid of setjmp, longjmp
     deglobalized: EOF_flag, FilmState, curVidStream, bitOffset, bitLength,
     bitBuffer, sys_layer, input, seekValue, window, X Windows globals (to
     xinfo), curBits, ditherType, matched_depth, totNumFrames, realTimeStart

   Additional changes:
     Ability to play >1 movie (w/out CONTROLS)
     Make sure we do a full frame for each movie
     DISABLE_DITHER #ifdef to avoid compiling dithering code
     Changes to deal with non-MPEG streams
     Now deals with NO_DITHER, PPM_DITHER and noDisplayFlag==1
     CONTROLS version now can deal with >1 movie
   -lsh@cs.brown.edu (Loring Holden)
 */

/* Make Ordered be the default dither */
#define DEFAULT_ORDERED_DITHER

/* Define buffer length. */
#define BUF_LENGTH 80000

/* Function return type declarations */
void usage();

/* Forward declaration of functions in this file. */
#ifndef P
# ifdef __STDC__
#   define        P(s) s
# else
#   define P(s) ()
# endif
#endif 

#ifndef SIG_ONE_PARAM
void int_handler P((void ));
void bad_handler P((void ));
#else
void int_handler P((int signum));
void bad_handler P((int signum));
#endif
void usage P((char *s ));

#ifdef DCPREC
/* Declaration of global variable to hold DC precision */
int dcprec = 0;
#endif

/* Global file pointer to incoming data. */
FILE **input;
char **inputName;

/* Loop flag. */
int loopFlag = 0;

/* Shared memory flag. */
int shmemFlag = 0;

/* Quiet flag. */
#ifdef QUIET
int quietFlag = 1;
#else
int quietFlag = 0;
#endif

/* "Press return" flag, requires return for each new frame */
int requireKeypressFlag = 0;

/* Display image on screen? */
int noDisplayFlag = 0;

/* Seek Value. 
   0 means do not seek.
   N (N>0) means seek to N after the header is parsed
   N (N<0) means the seek has beeen done to offset N
*/

/* Framerate, -1: specified in stream (default)
               0: as fast as possible
               N (N>0): N frames/sec  
               */
int framerate = -1;

/* Flags/values to control Arbitrary start/stop frames. */
int partialFlag = 0, startFrame = -1, endFrame = -1;

/* Flag for gamma correction */
int gammaCorrectFlag = 0;
double gammaCorrect = 1.0;

/* Flag for chroma correction */
int chromaCorrectFlag = 0;
double chromaCorrect = 1.0;

/* Flag for high quality at the expense of speed */
#ifdef QUALITY
int qualityFlag = 1;
#else
int qualityFlag = 0;
#endif

/* no further error messages */
static BOOLEAN exiting=FALSE;

/* global variable for interrupt handlers */
VidStream **curVidStream;

/* Brown - put X specific variables in xinfo struct */
#define NUMMOVIES 15
XInfo xinfo[NUMMOVIES];
int numInput=0;

/*
 #define Color16DitherImage ColorDitherImage
 #define Color32DitherImage ColorDitherImage
*/

/*
 *--------------------------------------------------------------
 *
 * int_handler --
 *
 *        Handles Cntl-C interupts..
 *      (two different ones for different OSes)
 * Results:    None.
 * Side effects:   None.
 *--------------------------------------------------------------
 */
#ifndef SIG_ONE_PARAM
void
int_handler()
#else
void
int_handler(signum)
int signum;
#endif
{
  int i,displayClosed=0;

  if (!quietFlag && !exiting) {
    fprintf(stderr, "Interrupted!\n");
  }
  exiting = TRUE;
  for (i = 0;  i < numInput;  i++) {
    if (curVidStream[i] != NULL)
      DestroyVidStream(curVidStream[i], &xinfo[i]);
    if ((xinfo[i].display != NULL) && !displayClosed) {
      XCloseDisplay(xinfo[i].display);
      displayClosed=1;
    }
  }
  exit(1);
}


/*
 *--------------------------------------------------------------
 *
 * bad_handler --
 *
 *        Handles Seg faults/bus errors...
 *      (two different ones for different OSes)
 * Results:    None.
 * Side effects:   None.
 *
 *--------------------------------------------------------------
 */

#ifndef SIG_ONE_PARAM
void
  bad_handler()
#else
void
  bad_handler(signum)
int signum;
#endif
{
  if (!exiting) {
    fprintf(stderr, "Bad MPEG?  Giving up.\ntry 'mpeg_stat -verify' to see if the stream is valid.\n");
  }
  exit(0);
}


/*
 *--------------------------------------------------------------
 *
 * getposition --
 *
 *--------------------------------------------------------------
 */
void getposition(arg, xpos, ypos)
char *arg;
int *xpos, *ypos;
{
  char *pos;

  if ((pos = strtok(arg, "+-")) != NULL) {
    *xpos = atoi(pos);
    if ((pos = strtok(NULL, "+-")) != NULL) {
      *ypos = atoi(pos);
      return;
    }
  }
  if (!quietFlag) {
    fprintf(stderr, "Illegal position... Warning: argument ignored! (-position +x+y)\n");
  }
  return;
}


/*
 *--------------------------------------------------------------
 *
 * main --
 *
 *        Parses command line, starts decoding and displaying.
 *
 * Results:
 *        None.
 *
 * Side effects:
 *        None.
 *
 *--------------------------------------------------------------
 */

#ifndef __STDC__
void
#endif
main(argc, argv)
     int argc;
     char **argv;
{

  char *name;
  static VidStream **theStream;
  int mark;
  int i, mult, largy, y, lastStream, firstStream=-1, workToDo=TRUE;
  int doDisplay=0; /* Current movie is displaying on screen */ 
  long seekValue=0;/* holds value before it is put in vid_stream */
  int  owncmFlag=0;   /* holds value before it is put in xinfo  */
  BOOLEAN firstRead=FALSE;
  int ppm_width = -1,  ppm_height = -1, ppm_modulus = -1;

  mark = 1;
  argc--;

  input = (FILE **) malloc(NUMMOVIES*sizeof(FILE *));
  inputName = (char **) malloc(NUMMOVIES *sizeof(char *));
  theStream = (VidStream **) malloc(NUMMOVIES *sizeof(VidStream *));
  curVidStream = (VidStream **) malloc(NUMMOVIES *sizeof(VidStream *));
  for (i = 0; i < NUMMOVIES; i++) {
     input[i] = NULL;
     inputName[i] = "stdin";
     theStream[i] = NULL;
     curVidStream[i] = NULL;
     xinfo[i].hints.x = -1;
     xinfo[i].hints.y = -1;
     xinfo[i].ExistingWindow = 0;
  }
  name = (char *) "";

#ifndef DISABLE_DITHER
#ifndef DEFAULT_ORDERED_DITHER
  xinfo[0].ditherType = FULL_COLOR_DITHER;
#else
  xinfo[0].ditherType = ORDERED_DITHER;
#endif
#endif

  LUM_RANGE = 8;
  CR_RANGE = CB_RANGE = 4;
  noDisplayFlag = 0;

#ifdef SH_MEM
  shmemFlag = 1;
#endif

  while (argc) {
    if (strcmp(argv[mark], "-nop") == 0) {
      SetPFlag(TRUE);
      SetBFlag(TRUE);
      argc--; mark++;
    } else if (strcmp(argv[mark], "-nob") == 0) {
      SetBFlag(TRUE);
      argc--; mark++;
    } else if (strcmp(argv[mark], "-display") == 0) {
      name = argv[++mark];
      argc -= 2; mark++;
    } else if (strcmp(argv[mark], "-position") == 0) {
      argc--; mark++;
      getposition(argv[mark], &xinfo[numInput].hints.x, &xinfo[numInput].hints.y);
      argc--; mark++;
    } else if (strcmp(argv[mark], "-xid") == 0) {
      xinfo[numInput].ExistingWindow = atoi(argv[++mark]);
      argc -= 2; mark++;
    } else if (strcmp(argv[mark], "-start") == 0) {
      if (argc < 2) usage(argv[0]);
      partialFlag = TRUE;
      if (seekValue != 0) {
	fprintf(stderr, "Cannot use -start with -seek (ignored)\n");
      } else {
	startFrame = atoi(argv[++mark]);
      }
      argc -= 2; mark++;
    } else if (strcmp(argv[mark], "-seek") == 0) {
      if (argc < 2) usage(argv[0]);
      seekValue = atoi(argv[++mark]);
      if (startFrame != -1) startFrame = 0;
      argc -= 2; mark++;
    } else if (strcmp(argv[mark], "-end") == 0) {
      if (argc < 2) usage(argv[0]);
      endFrame = atoi(argv[++mark]);
      partialFlag = TRUE;
      argc -= 2; mark++;
    } else if (strcmp(argv[mark], "-gamma") == 0) {
      if (argc < 2) usage(argv[0]);
      gammaCorrectFlag = 1;
      sscanf(argv[++mark], "%lf", &gammaCorrect);
      if (gammaCorrect <= 0.0) {
        fprintf(stderr, "ERROR: Gamma correction must be greater than 0.\n");
        gammaCorrect = 1.0;
      }
      if (!quietFlag) {
        printf("Gamma Correction set to %4.2f.\n", gammaCorrect);
      }
      argc -= 2; mark++;
    } else if (strcmp(argv[mark], "-chroma") == 0) {
      if (argc < 2) usage(argv[0]);
      chromaCorrectFlag = 1;
      sscanf(argv[++mark], "%lf", &chromaCorrect);
      if (chromaCorrect <= 0.0) {
        fprintf(stderr, "ERROR: Chroma correction must be greater than 0.\n");
        chromaCorrect = 1.0;
      }
      if (!quietFlag) {
        printf("Chroma Correction set to %4.2f.\n",chromaCorrect);
      }
      argc -= 2; mark++;
#ifdef DCPREC
    } else if (strcmp(argv[mark], "-dc") == 0) {
      argc--; mark++;
      if (argc < 1) {
        perror("Must specify dc precision after -dc flag");
        usage(argv[0]);
      }
      dcprec = atoi(argv[mark]) - 8;
      if ((dcprec > 3) || (dcprec < 0)) {
        perror("DC precision must be at least 8 and at most 11");
        usage(argv[0]);
      }
      argc--; mark++;
#endif
    } else if (strcmp(argv[mark], "-quality") == 0) {
      argc--; mark++;
      if (argc < 1) {
        perror("Must specify on or off after -quality flag");
        usage(argv[0]);
      }
      if (strcmp(argv[mark], "on") == 0) {
        argc--; mark++;
        qualityFlag = 1;
      }
      else if (strcmp(argv[mark], "off") == 0) {
        argc--; mark++;
        qualityFlag = 0;
      }
      else {
        perror("Must specify on or off after -quality flag");
        usage(argv[0]);
      }
    } else if (strcmp(argv[mark], "-framerate") == 0) {
      argc--; mark++;
      if (argc < 1) {
        perror("Must specify framerate after -framerate flag");
        usage(argv[0]);
      }
      framerate = atoi(argv[mark]);
      argc--; mark++;
    } else if (strcmp(argv[mark], "-ppmwidth") == 0) {
      argc--; mark++;
      if (argc < 1) {
	perror("Must specify PPM file width after -ppmwidth flag");
	usage(argv[0]);
      }
      ppm_width = atoi(argv[mark]);
      argc--; mark++;
    } else if (strcmp(argv[mark], "-ppmheight") == 0) {
      argc--; mark++;
      if (argc < 1) {
	perror("Must specify PPM file height after -ppmheight flag");
	usage(argv[0]);
      }
      ppm_height = atoi(argv[mark]);
      argc--; mark++;
    } else if (strcmp(argv[mark], "-ppmskip") == 0) {
      argc--; mark++;
      if (argc < 1) {
	perror("Must specify PPM file height after -ppmheight flag");
	usage(argv[0]);
      }
      ppm_modulus = atoi(argv[mark]);
      argc--; mark++;
#ifndef DISABLE_DITHER
    } else if (strcmp(argv[mark], "-dither") == 0) {
      argc--; mark++;
      if (argc < 1) {
        perror("Must specify dither option after -dither flag");
        usage(argv[0]);
      }
      if (strcmp(argv[mark], "hybrid") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = HYBRID_DITHER;
      } else if (strcmp(argv[mark], "hybrid2") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = HYBRID2_DITHER;
      } else if (strcmp(argv[mark], "fs4") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = FS4_DITHER;
      } else if (strcmp(argv[mark], "fs2") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = FS2_DITHER;
      } else if (strcmp(argv[mark], "fs2fast") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = FS2FAST_DITHER;
      } else if (strcmp(argv[mark], "hybrid2") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = HYBRID2_DITHER;
      } else if (strcmp(argv[mark], "2x2") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = Twox2_DITHER;
      } else if ((strcmp(argv[mark], "gray256") == 0) ||
                 (strcmp(argv[mark], "grey256") == 0)) {
        argc--; mark++;
        xinfo[0].ditherType = GRAY256_DITHER;
      } else if ((strcmp(argv[mark], "gray") == 0) ||
                 (strcmp(argv[mark], "grey") == 0)) {
        argc--; mark++;
        xinfo[0].ditherType = GRAY_DITHER;
      } else if ((strcmp(argv[mark], "gray256x2") == 0) ||
                  (strcmp(argv[mark], "grey256x2") == 0)) {
        argc--; mark++;
        xinfo[0].ditherType = GRAY2562_DITHER;
      } else if ((strcmp(argv[mark], "gray") == 0) ||
                   (strcmp(argv[mark], "grey") == 0)) {
        argc--; mark++;
        xinfo[0].ditherType = GRAY_DITHER;
      } else if ((strcmp(argv[mark], "gray2") == 0) ||
                  (strcmp(argv[mark], "grey2") == 0)) {
        argc--; mark++;
        xinfo[0].ditherType = GRAY2_DITHER;
      } else if (strcmp(argv[mark], "color") == 0 ||
                 strcmp(argv[mark], "colour") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = FULL_COLOR_DITHER;
      } else if (strcmp(argv[mark], "color2") == 0 ||
                 strcmp(argv[mark], "colour2") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = FULL_COLOR2_DITHER;
      } else if (strcmp(argv[mark], "none") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = NO_DITHER;
      } else if (strcmp(argv[mark], "ppm") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = PPM_DITHER;
      } else if (strcmp(argv[mark], "ordered") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = ORDERED_DITHER;
      } else if (strcmp(argv[mark], "ordered2") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = ORDERED2_DITHER;
      } else if (strcmp(argv[mark], "mbordered") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = MBORDERED_DITHER;
      } else if (strcmp(argv[mark], "mono") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = MONO_DITHER;
      } else if (strcmp(argv[mark], "threshold") == 0) {
        argc--; mark++;
        xinfo[0].ditherType = MONO_THRESHOLD;
      } else {
        perror("Illegal dither option.");
        usage(argv[0]);
      }
#endif
    } 
    else if (strcmp(argv[mark], "-eachstat") == 0) {
      argc--; mark++;
#ifdef ANALYSIS
      showEachFlag = 1;
#else
      fprintf(stderr, "To use -eachstat, recompile with -DANALYSIS in CFLAGS\n");
      exit(1);
#endif
    }
    else if (strcmp(argv[mark], "-shmem_off") == 0) {
      argc--; mark++;
      shmemFlag = 0;
    }
#ifdef QUIET
    else if (strcmp(argv[mark], "-quiet") == 0) { 
      argc--; mark++;
    }
    else if (strcmp(argv[mark], "-noisy") == 0) {
#else
    else if (strcmp(argv[mark], "-noisy") == 0) {
      argc--; mark++;
    }
    else if (strcmp(argv[mark], "-quiet") == 0) { 
#endif
      argc--; mark++;
      quietFlag = !quietFlag;
    }
    else if (strcmp(argv[mark], "-owncm") == 0) {
      argc--; mark++;
      owncmFlag = 1;
    }
    else if (strcmp(argv[mark], "-step") == 0) {
      argc--; mark++;
      requireKeypressFlag = 1;
    }
    else if (strcmp(argv[mark], "-loop") == 0) {
      argc--; mark++;
      loopFlag = 1;
    }
    else if (strcmp(argv[mark], "-no_display") == 0) {
      argc--; mark++;
      noDisplayFlag = 1;
      shmemFlag = 0;
    }
    else if (strcmp(argv[mark], "-l_range") == 0) {
      argc--; mark++;
      LUM_RANGE = atoi(argv[mark]);
      if (LUM_RANGE < 1) {
        fprintf(stderr, "Illegal luminance range value: %d\n", LUM_RANGE);
        exit(1);
      }
      argc--; mark++;
    }
    else if (strcmp(argv[mark], "-cr_range") == 0) {
      argc--; mark++;
      CR_RANGE = atoi(argv[mark]);
      if (CR_RANGE < 1) {
        fprintf(stderr, "Illegal cr range value: %d\n", CR_RANGE);
        exit(1);
      }
      argc--; mark++;
    }
    else if (strcmp(argv[mark], "-cb_range") == 0) {
      argc--; mark++;
      CB_RANGE = atoi(argv[mark]);
      if (CB_RANGE < 1) {
        fprintf(stderr, "Illegal cb range value: %d\n", CB_RANGE);
        exit(1);
      }
      argc--; mark++;
    } 
#ifndef NOCONTROLS
    else if (strcmp(argv[mark], "-controls") == 0 || 
	     strcmp(argv[mark], "-controlbar") == 0 || 
	     strcmp(argv[mark], "-control_bar") == 0) {
      argc--; mark++;
      if (argc < 1) {
        perror("Must specify on, off, or none after -controls flag");
        usage(argv[0]);
      }
      if (strcmp(argv[mark], "on") == 0) {
        argc--; mark++;
        ControlShow = CTRLBAR_ON;
      }
      else if (strcmp(argv[mark], "off") == 0) {
        argc--; mark++;
        ControlShow = CTRLBAR_OFF;
      }
      else if (strcmp(argv[mark], "none") == 0) {
        argc--; mark++;
        ControlShow = CTRLBAR_NONE;
      }
      else {
        perror("Must specify on, off, or none after -controls flag");
        usage(argv[0]);
      }
    }
#endif /* !NOCONTROLS */
    else if ((strcmp(argv[mark], "-?") == 0) ||
               (strcmp(argv[mark], "-Help") == 0) ||
               (strcmp(argv[mark], "-help") == 0)) {
      usage(argv[0]);
    }
    else if (argv[mark][0] == '-' && argv[mark][1]==0) {
      fflush(stdout);
      if (numInput<NUMMOVIES) {
        input[numInput]=stdin;
        inputName[numInput++] = "stdin";
      } else {
          fprintf(stderr, "Can't load file %s - too many\n", "stdin");
      }
      argc--; mark++;
    }
    else if (argv[mark][0] == '-') {
      fprintf(stderr, "Un-recognized flag %s\n",argv[mark]);
      usage(argv[0]);
    }
    else {
      fflush(stdout);
      if (numInput<NUMMOVIES) {
        input[numInput]=fopen(argv[mark], "rb");
        if (input[numInput] == NULL) {
          fprintf(stderr, "Could not open file %s\n", argv[mark]);
          usage(argv[0]);
        }
        inputName[numInput++] = argv[mark];
      } else {
          fprintf(stderr, "Can't load file %s - too many\n", argv[mark]);
      }
      argc--; mark++;
    }
  }

  lum_values = (int *) malloc(LUM_RANGE*sizeof(int));
  cr_values = (int *) malloc(CR_RANGE*sizeof(int));
  cb_values = (int *) malloc(CB_RANGE*sizeof(int));

  signal(SIGINT, int_handler);
#ifndef DEBUG
  signal(SIGSEGV, bad_handler);
#ifndef WIN32
  signal(SIGBUS,  bad_handler);
#endif
#endif
  if ((startFrame != -1) && (endFrame != -1) &&
      (endFrame < startFrame)) {
    usage(argv[0]);
  }

  init_tables();
  for (i = 0;  i < numInput;  i++) {
    xinfo[i].owncmFlag = owncmFlag;
    xinfo[i].display = NULL;       /* xinfo.ximage is set to null later */
    if (xinfo[i].hints.x == -1) {
      xinfo[i].hints.x = 200;
      xinfo[i].hints.y = 300;
    }
    xinfo[i].hints.width = 150;
    xinfo[i].hints.height = 150;
    xinfo[i].visual = NULL;
    xinfo[i].name = inputName[i];
    xinfo[i].cmap = 0;
    xinfo[i].gc = 0;
  }

#ifndef DISABLE_DITHER
  if (xinfo[0].ditherType == MONO_DITHER ||
      xinfo[0].ditherType == MONO_THRESHOLD)
    xinfo[0].depth= 1;

  switch (xinfo[0].ditherType) {
    
  case HYBRID_DITHER:
    InitColor();
    InitHybridDither();
    InitDisplay(name, &xinfo[0]);
    break;
    
  case HYBRID2_DITHER:
    InitColor();
    InitHybridErrorDither();
    InitDisplay(name, &xinfo[0]);
    break;
    
  case FS4_DITHER:
    InitColor();
    InitFS4Dither();
      InitDisplay(name, &xinfo[0]);
    break;
    
  case FS2_DITHER:
    InitColor();
    InitFS2Dither();
    InitDisplay(name, &xinfo[0]);
    break;
    
  case FS2FAST_DITHER:
    InitColor();
    InitFS2FastDither();
    InitDisplay(name, &xinfo[0]);
    break;
    
  case Twox2_DITHER:
    InitColor();
    Init2x2Dither();
    InitDisplay(name, &xinfo[0]);
    PostInit2x2Dither();
    break;

  case GRAY_DITHER:
  case GRAY2_DITHER:
    InitGrayDisplay(name, &xinfo[0]);
    break;

  case GRAY256_DITHER:
  case GRAY2562_DITHER:
    InitGray256Display(name, &xinfo[0]);
    break;

  case FULL_COLOR_DITHER:
  case FULL_COLOR2_DITHER:
    InitColorDisplay(name, &xinfo[0]);
    InitColorDither(xinfo[0].depth>=24);
#else
    InitColorDisplay(name, &xinfo[0]);
    InitColorDither(xinfo[0].depth>=24);
#endif
#ifndef DISABLE_DITHER
    break;

  case NO_DITHER:
    shmemFlag = 0;
    break;

  case PPM_DITHER:
    shmemFlag = 0;
    wpixel[0] = 0xff;
    wpixel[1] = 0xff00;
    wpixel[2] = 0xff0000;
    xinfo[0].depth = 24;
    InitColorDither(1);
    break;

  case ORDERED_DITHER:
    InitColor();
    InitOrderedDither();
    InitDisplay(name, &xinfo[0]);
    break;

  case MONO_DITHER:
  case MONO_THRESHOLD:
    InitMonoDisplay(name, &xinfo[0]);
    break;

  case ORDERED2_DITHER:
    InitColor();
    InitDisplay(name, &xinfo[0]);
    InitOrdered2Dither();
    break;

  case MBORDERED_DITHER:
    InitColor();
    InitDisplay(name, &xinfo[0]);
    InitMBOrderedDither();
    break;
  }
#endif

#ifdef SH_MEM
    if (shmemFlag && (xinfo[0].display != NULL)) {
      if (!XShmQueryExtension(xinfo[0].display)) {
        shmemFlag = 0;
        if (!quietFlag) {
          fprintf(stderr, "Shared memory not supported\n");
          fprintf(stderr, "Reverting to normal Xlib.\n");
        }
      }
    }
#endif

  InitCrop();

  y=300;
  largy=0;
  for (i=0;i<numInput;i++) {
    doDisplay=!noDisplayFlag;
#ifndef DISABLE_DITHER
    if ((xinfo[i].ditherType == NO_DITHER) ||
        (xinfo[i].ditherType == PPM_DITHER))
       doDisplay = FALSE;
#endif
    lastStream = i-1;
    while ((lastStream>=0) && (theStream[lastStream]==NULL)) {
       lastStream--;
    }
    if ((i != 0) && doDisplay) {
       if (lastStream > -1) {
         xinfo[i].hints.x =
            xinfo[lastStream].hints.x+10 + theStream[lastStream]->h_size;
         if (theStream[lastStream]->v_size>largy)
	   largy = theStream[lastStream]->v_size;
         if (xinfo[i].hints.x > DisplayWidth(xinfo[firstStream].display,
			       XDefaultScreen(xinfo[firstStream].display)) -80) {

		y += largy + 30;
		largy = 0;
		xinfo[i].hints.x = 0;
         }
         xinfo[i].hints.y = y;
         xinfo[i].visual = xinfo[firstStream].visual;
         xinfo[i].cmap = xinfo[firstStream].cmap;
         xinfo[i].gc = xinfo[firstStream].gc;
       }
       xinfo[i].display = xinfo[0].display;
       xinfo[i].depth = xinfo[0].depth;
       xinfo[i].ditherType = xinfo[0].ditherType;
       InitColorDisplay(name, &xinfo[i]);
    }
    curVidStream[i] = theStream[i] = NewVidStream((unsigned int) BUF_LENGTH);
    theStream[i]->ppm_width = ppm_width;
    theStream[i]->ppm_height = ppm_height;
    theStream[i]->ppm_modulus = ppm_modulus;
    theStream[i]->input = input[i];
    theStream[i]->seekValue = seekValue;
    theStream[i]->filename = inputName[i];
    theStream[i]->ditherType = xinfo[i].ditherType;
    theStream[i]->matched_depth = xinfo[i].depth;
    mark = quietFlag;
    quietFlag=1;
    if (mpegVidRsrc(0, theStream[i], 1, &xinfo[i])==NULL) {
       if (doDisplay) {
         XDestroyWindow(xinfo[i].display, xinfo[i].window);
       }
       /* stream has already been destroyed */
       curVidStream[i] = theStream[i]=NULL;
       fprintf(stderr, "Skipping movie %d, \"%s\" - not an MPEG stream\n",
	  i, inputName[i]);
       fclose(input[i]);
       if (i+1 == numInput) numInput--;
    } else if (firstStream == -1) firstStream=i;
    quietFlag = mark;

#ifndef DISABLE_DITHER
    if (IS_2x2_DITHER(xinfo[i].ditherType)) {
      mult = 2;
    }
    else {
      mult = 1;
    }
#else
    mult = 1;
#endif

    if (doDisplay && (theStream[i]!=NULL)) {
      ResizeDisplay((unsigned int) theStream[i]->h_size* mult,
                    (unsigned int) theStream[i]->v_size* mult,
		  &xinfo[i]);
    }
  }

  if (numInput > 1) {
    loopFlag = TRUE;
    framerate = 0;
  }

#ifndef NOCONTROLS
  if (xinfo[0].display == NULL) {
    ControlShow = CTRLBAR_NONE;  /* no display => no controls */
  }

  if (ControlShow != CTRLBAR_NONE) {
    MakeControlBar(&xinfo[0]);
    ControlBar(theStream, xinfo, numInput);
  }
  for (i = 0; i < numInput; i++) {
     if (theStream[i] != NULL) theStream[i]->realTimeStart = ReadSysClock();
  }
#else
  /* Start time for each movie - do after windows are mapped */
  for (i = 0; i < numInput; i++) {
     if (theStream[i] != NULL) theStream[i]->realTimeStart = ReadSysClock();
  }
#endif


#ifndef NOCONTROLS
  if (ControlShow == CTRLBAR_NONE) {
    while (TRUE) {
      for (i=0;i < numInput; i++) {
        while (theStream[i]->film_has_ended != TRUE) {
          mpegVidRsrc(0, theStream[i], 0, &xinfo[i]);
        }
        if (loopFlag) {
          rewind(theStream[i]->input);
          ResetVidStream(theStream[i]); /* Reinitialize vid_stream pointers */
          if (theStream[i]->seekValue < 0) {
            theStream[i]->seekValue = 0 - theStream[i]->seekValue;
          }
          mpegVidRsrc(0, theStream[i], 1, &xinfo[i]); /* Process start codes */
        } else if (doDisplay) break;
        else goto done;
      }
     }
   }
  else {
    ControlLoop(theStream, xinfo, numInput);
  }

done:
  mark=0;
  for (i=0;i < numInput; i++) {
    DestroyVidStream(theStream[i], &xinfo[i]);
    if ((xinfo[i].display != NULL) && !mark) {
#ifndef WIN32
      /* this causes exceed to seg fault */
      XCloseDisplay(xinfo[i].display);
#endif
	  mark=1;
    }
  }
  exit(0);
#else /* !NOCONTROLS */
  if (!numInput) {
     fprintf(stderr, "Must enter MPEG file to play\n");
     usage(argv[0]);
  }
  while (workToDo) {
     workToDo = FALSE;
     for (i = 0; i < numInput; i++) {
       if (theStream[i] != NULL) {
         mark = theStream[i]->totNumFrames;
         /* make sure we do a whole frame */
         while (mark == theStream[i]->totNumFrames) {
             mpegVidRsrc(0, theStream[i], 0, &xinfo[i]);
         }
         if (theStream[i]->film_has_ended) {
           if (loopFlag) {
             clear_data_stream(theStream[i]);
             /* Reinitialize vid_stream pointers */
             ResetVidStream(theStream[i]);
             rewind(theStream[i]->input);
             if (theStream[i]->seekValue < 0) {
               theStream[i]->seekValue = 0 - theStream[i]->seekValue;
             }
#ifdef ANALYSIS
             init_stats();
#endif
             /* Process start codes */
             if (mpegVidRsrc(0, theStream[i], 1, &xinfo[i])==NULL) {
	       /* print something sensible here,
		  but we only get here if the file is changed while we
		  are decoding, right?
		*/
	     }
           } /* loopFlag */
	 }   /* film_has_ended */
         workToDo = workToDo || (!theStream[i]->film_has_ended);
       } /* theStream[i]!=NULL */
     }   /* for (i.. */
  }      /* while workToDo */
  sleep(1000);
    /* freeze on the last frame */
#endif /* NOCONTROLS */
}


/*
 *--------------------------------------------------------------
 *
 * usage --
 *
 *        Print mpeg_play usage
 *
 * Results:
 *        None.
 *
 * Side effects:
 *        exits with a return value -1
 *
 *--------------------------------------------------------------
 */

void
usage(s)
char *s;        /* program name */
{
    fprintf(stderr, "Usage:\n");
#ifndef NOCONTROLS
    fprintf(stderr, "mpeg_play [options] [filename]\n");
#else
    fprintf(stderr, "mpeg_play [[options] [filename]]  [[options] [filename]]  [..]\n");
#endif
    fprintf(stderr, "Options :\n");
   fprintf(stderr, "      [-display X_display]\t[-no_display]\n");
#ifndef DISABLE_DITHER
    fprintf(stderr, "      [-dither {ordered|ordered2|mbordered|fs4|fs2|fs2fast|hybrid|\n");
    fprintf(stderr, "                hybrid2|2x2|gray|gray256|color|color2|none|mono|threshold|ppm|\n");
    fprintf(stderr, "                gray2|gray256x2}]\n");
#endif
    fprintf(stderr, "      [-loop]\n");
    fprintf(stderr, "      [-start frame_num]\t[-end frame_num]\t[-seek file_offset]\n");
    fprintf(stderr, "      [-gamma gamma_correction_value]\t[-chroma chroma_correction_value]\n");
    fprintf(stderr, "      [-framerate num_frames_per_sec]  (0 means as fast as possible)\n");
    fprintf(stderr, "      [-position +x+y]\t[-quality {on|off}] (compiled default is ");
#ifdef QUALITY
    fprintf(stderr, "ON)\n");
#else
    fprintf(stderr, "OFF)\n");
#endif
#ifdef QUIET
    fprintf(stderr, "      [-noisy] (turns on all program output)\n");
#else
    fprintf(stderr, "      [-quiet] (turns off all program output)\n");
#endif
#ifndef NOCONTROLS
    fprintf(stderr, "      [-controls {on|off|none}] (default is on)\n");
#endif
    fprintf(stderr, "      [-?]\t[-help] for help (this message)\n");
    fprintf(stderr, "Rare options:\n");
    fprintf(stderr, "      [-nob]\t[-nop]\n");
#ifdef ANALYSIS
    fprintf(stderr, "      [-eachstat]\t[-owncm]\t[-shmem_off]\n");
#else
    fprintf(stderr, "      [-owncm]\t[-shmem_off]\n");
#endif
    fprintf(stderr, "      [-l_range num]\t[-cr_range num]\t[-cb_range num]\n");
/*    fprintf(stderr, "      [-xid xid]\n"); */
#ifdef DCPREC
    fprintf(stderr, "      [-dc {8|9|10|11}] (defaults to 8)\n");
#endif
    fprintf(stderr, "      with -dither ppm:\n");
    fprintf(stderr, "          [-ppmwidth num]\t[-ppmheight num]\t[-ppmskip num]\n");
    exit (-1);
}



/*
 *--------------------------------------------------------------
 *
 * DoDitherImage --
 *
 *      Called when image needs to be dithered. Selects correct
 *      dither routine based on info in xinfo[0].ditherType.
 *
 * Results:
 *        None.
 *
 * Side effects:
 *        None.
 *
 *--------------------------------------------------------------
 */

void
DoDitherImage(vid_stream)
VidStream *vid_stream;
{
 unsigned char *l=vid_stream->current->luminance,
               *Cr=vid_stream->current->Cr,
               *Cb=vid_stream->current->Cb,
               *disp=vid_stream->current->display;
 int h=(int) vid_stream->mb_height * 16;
 int w=(int) vid_stream->mb_width * 16;
 int ditherType=vid_stream->ditherType;
 int matched_depth=vid_stream->matched_depth;

#ifndef DISABLE_DITHER
  switch(ditherType) {
  case HYBRID_DITHER:
    HybridDitherImage(l, Cr, Cb, disp, h, w);
    break;

  case HYBRID2_DITHER:
    HybridErrorDitherImage(l, Cr, Cb, disp, h, w);
    break;

  case FS2FAST_DITHER:
    FS2FastDitherImage(l, Cr, Cb, disp, h, w);
    break;

  case FS2_DITHER:
    FS2DitherImage(l, Cr, Cb, disp, h, w);
    break;

  case FS4_DITHER:
    FS4DitherImage(l, Cr, Cb, disp, h, w);
    break;

  case Twox2_DITHER:
    Twox2DitherImage(l, Cr, Cb, disp, h, w);
    break;

  case FULL_COLOR2_DITHER:
    if (matched_depth >= 24)
      Twox2Color32DitherImage(l, Cr, Cb, disp, h, w);
    else
      Twox2Color16DitherImage(l, Cr, Cb, disp, h, w);
    break;
  case FULL_COLOR_DITHER:
    if (matched_depth >= 24)
#endif

      Color32DitherImage(l, Cr, Cb, disp, h, w);

#ifndef DISABLE_DITHER
    else
      Color16DitherImage(l, Cr, Cb, disp, h, w);
    break;

  case GRAY_DITHER:
  case GRAY256_DITHER:
    if (matched_depth == 8) 
      GrayDitherImage(l, Cr, Cb, disp, h, w);
    else if (matched_depth == 16) 
      Gray16DitherImage(l, Cr, Cb, disp, h, w);
    else if (matched_depth == 32 || matched_depth == 24)
      Gray32DitherImage(l, Cr, Cb, disp, h, w);
    break;

  case GRAY2_DITHER:
  case GRAY2562_DITHER:
    if (matched_depth == 8) 
      Gray2DitherImage(l, Cr, Cb, disp, h, w);
    else if (matched_depth == 16) 
      Gray216DitherImage(l, Cr, Cb, disp, h, w);
    else if (matched_depth == 32 || matched_depth == 24)
      Gray232DitherImage(l, Cr, Cb, disp, h, w);
    break;

  case NO_DITHER:
    break;

  case PPM_DITHER:
    Color32DitherImage(l, Cr, Cb, disp, h, w);
    break;

  case ORDERED_DITHER:
    OrderedDitherImage(l, Cr, Cb, disp, h, w);
    break;

  case MONO_DITHER:
    MonoDitherImage(l, Cr, Cb, disp, h, w);
    break;

  case MONO_THRESHOLD:
    MonoThresholdImage(l, Cr, Cb, disp, h, w);
    break;

  case ORDERED2_DITHER:
    Ordered2DitherImage(l, Cr, Cb, disp, h, w);
    break;

  case MBORDERED_DITHER:
    MBOrderedDitherImage(l, Cr, Cb, disp, h, w, vid_stream->ditherFlags);
    break;
  }
#endif
}
