/*
 * readfile.c --
 *
 *       Procedures concerned with reading data and parsing 
 *       start codes from MPEG files.
 *
 */

/*
 * 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.
 */

#ifdef __STDC__
#include <stdlib.h>
#include <string.h>
#endif
#include <sys/types.h>
#include <signal.h>
#ifndef WIN32
#ifndef MIPS
#include <netinet/in.h>
#else
#include <bsd/netinet/in.h>
#endif
#else
#include <winsock.h>
#endif

#include "video.h"
#include "proto.h"
#include "util.h"
#include "dither.h"

/*
   Changes to make the code reentrant:
      deglobalized: totNumFrames, realTimeStart, stream id vars, Prase_done,
         swap, seekValue, input, EOF_flag, ReadPacket statics, sys_layer,
	 bitOffset, bitLength, bitBuffer, curVidStream
   removed: [aud,sys,vid]Bytes
   Additional changes:
      get rid on ANSI C complaints about shifting
   -lsh@cs.brown.edu (Loring Holden)
 */

/* Silly Constants.... */
#define PACK_START_CODE             ((unsigned int)0x000001ba)
#define SYSTEM_HEADER_START_CODE    ((unsigned int)0x000001bb)
#define PACKET_START_CODE_MASK      ((unsigned int)0xffffff00)
#define PACKET_START_CODE_PREFIX    ((unsigned int)0x00000100)
#define ISO_11172_END_CODE          ((unsigned int)0x000001b9)
  
#define PACK_HEADER_SIZE 8
  
#define STD_AUDIO_STREAM_ID ((unsigned char) 0xb8)
#define STD_VIDEO_STREAM_ID ((unsigned char) 0xb9)
#define MIN_STREAM_ID_ID    ((unsigned char) 0xbc)
#define RESERVED_STREAM_ID  ((unsigned char) 0xbc)
#define PRIVATE_STREAM_1_ID ((unsigned char) 0xbd)
#define PADDING_STREAM_ID   ((unsigned char) 0xbe)
#define PRIVATE_STREAM_2_ID ((unsigned char) 0xbf)
  
#define STD_SYSTEM_CLOCK_FREQ (unsigned long)90000
#define MUX_RATE_SCALE_FACTOR 50
#define MAX_STREAMS 8
#define NOT_PACKET_ID       ((unsigned char) 0xff)
#define KILL_BUFFER         ((unsigned char) 0xfe)
  

/*
 *--------------------------------------------------------------
 *
 * get_more_data --
 *
 *	Called by get_more_data to read in more data from
 *      video MPG files (non-system-layer)
 *
 * Results:
 *	Input buffer updated, buffer length updated.
 *      Returns 1 if data read, 0 if EOF, -1 if error.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */
int 
get_more_data(vid_stream)
      VidStream *vid_stream;
{
  unsigned int **bs_ptr=&vid_stream->buf_start; 
  int *max_length=&vid_stream->max_buf_length;
  int *length_ptr=&vid_stream->buf_length;
  unsigned int **buf_ptr=&vid_stream->buffer;
  int ioBytes, data, result;
  unsigned char byte;
  unsigned int *mark;
  int sys_layer= vid_stream->sys_layer;
  
  if (sys_layer == 0) {
    return pure_get_more_data(*bs_ptr,
			      *max_length,
			       length_ptr,
			       buf_ptr,
			       vid_stream);
  }

  if (sys_layer == -1) {
    /* Time to init ourselves */
    vid_stream->swap = (htonl(1) != 1);
    mark = *bs_ptr;
    ioBytes = fread(&data, 1, 4, vid_stream->input);

    if (ioBytes != 4) {
	  return 0;
    }

    data = ntohl(data);
    if ( (data == PACK_START_CODE) || (data == SYSTEM_HEADER_START_CODE) ) {
    got_sys:
      /* Yow, a System Layer Stream.  Much harder to parse.  Call in the
	     specialist.... */
      fprintf(stderr,"This is an MPEG System Layer Stream.  ");
      fprintf(stderr,"Audio is not played.\n");
      vid_stream->sys_layer = 1;
      result = read_sys(vid_stream,(unsigned int) data);
      return result;
    } else if (data ==  SEQ_START_CODE) {
    got_seq:
      /* No system Layer junk, just pretent we didn't peek,
	     and hereafter just call pure_get_more_data */
      vid_stream->sys_layer = 0;
      **bs_ptr = data;
      *length_ptr = 1;
      result = pure_get_more_data(*bs_ptr, *max_length, 
				 length_ptr, buf_ptr, vid_stream);
      *buf_ptr = *bs_ptr;
      return result;
    } else {
      int state;

      fprintf(stderr, "Junk at start of stream, searching for start code\n");
      state = 0;
      while (TRUE) {
	if ((ioBytes = fread(&byte, 1, 1, vid_stream->input)) != 1) return 0;
	if (byte == 0) {
	  if (state < 2) state++;
	} else if ((byte == 1) && (state == 2)) {
	  state++;
	} else {
	  state = 0;
	}
	if (state == 3) {
	  if ((ioBytes = fread(&byte, 1, 1, vid_stream->input)) != 1) return 0;
	  data = ((unsigned int) byte + 0x100);
	  switch (data) {
	  case SEQ_START_CODE:
	    goto got_seq;
	  case PACK_START_CODE:
	  case SYSTEM_HEADER_START_CODE:
	    goto got_sys;
	  default:
	    /* keep looking */
	    state=0;
	  }
	}
      }}
  }

  /* A system layer stream (called after the 1st time), call the specialist */
  result = read_sys(vid_stream,0);
  return result;
}


/*
 *-------------------------------------------------------------
 *
 * clear_data_stream
 *
 * Empties out internal buffers
 *
 *-------------------------------------------------------------
 */
void 
  clear_data_stream(vid_stream)
     VidStream *vid_stream;
{
  /* Only internal buffer is in ReadPacket */
  if (vid_stream->sys_layer) {
    ReadPacket(KILL_BUFFER, vid_stream);
  }
}

/*
 *-------------------------------------------------------------
 *
 * SeekStream
 *
 * Goto an offset in the steam
 *
 *-------------------------------------------------------------
 */
void
  SeekStream(vid_stream)
VidStream *vid_stream;
{
  int errno;
  int code;
  
  if (vid_stream->seekValue < 0) return; /* done seeking */
#ifdef SEEK_SET
  errno = fseek(vid_stream->input, vid_stream->seekValue, SEEK_SET);
#else
  errno = fseek(vid_stream->input, vid_stream->seekValue, 0);
#endif
  if (errno != 0) {
    fprintf(stderr,"Error in seek (%d)\n",errno);
    perror("mpeg_play");
  }
  vid_stream->seekValue = 0-vid_stream->seekValue;
  vid_stream->totNumFrames = 0;

  /* clear that buffer */
  vid_stream->buffer = vid_stream->buf_start;
  vid_stream->buf_length = 0;
  vid_stream->bit_offset = 0;

  /* Find a decent start code */
 restart:
 NO_ZEROS:
  switch(fgetc(vid_stream->input)) {
  case 0:    goto ONE_ZERO;
  case EOF:  goto EOF_FOUND;
  default:   goto NO_ZEROS;
  }
  
 ONE_ZERO:
  switch(fgetc(vid_stream->input)) {
  case 0:    goto TWO_ZEROS;
  case EOF:  goto EOF_FOUND;
  default:   goto NO_ZEROS;
  }
  
 TWO_ZEROS:
  switch(fgetc(vid_stream->input)) {
  case 0x01:  goto CODE_FOUND;
  case 0x00:  goto TWO_ZEROS;
  case EOF:   goto EOF_FOUND;
  default:    goto NO_ZEROS;
  }
  
 CODE_FOUND:
  code = 0x00000100+fgetc(vid_stream->input);
  if (vid_stream->sys_layer) {
    clear_data_stream(vid_stream);
    if (((code & PACKET_START_CODE_MASK) == PACKET_START_CODE_PREFIX) &&
	((code & 0xff) >= 0xbc)) {
      read_sys(vid_stream, code);
      while (TRUE) {
	next_start_code(vid_stream);
	show_bits32(code);
	if ((code == SEQ_START_CODE) ||
	    (code == GOP_START_CODE)) return;
	flush_bits32; 
      }
    }
  } else {
    if ((code == SEQ_START_CODE) ||
	(code == GOP_START_CODE)) {
      *vid_stream->buffer = code;
      vid_stream->buf_length = 1;
      return;
    }
  }
  goto restart;

 EOF_FOUND:   /* received EOF */
  fprintf(stderr, "Hit EOF after seeking (offset %ld)\n",
     ftell(vid_stream->input));
  exit(1);

}


/*
 *--------------------------------------------------------------
 *
 * pure_get_more_data --
 *      (get_more_data from ver 2.0 with swap added)
 *
 *	Called by get_more_data to read in more data from
 *      video MPG files (non-system-layer)
 *
 * Results:
 *	Input buffer updated, buffer length updated.
 *      Returns 1 if data read, 0 if EOF, -1 if error.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

int 
pure_get_more_data(buf_start, max_length, length_ptr, buf_ptr, vid_stream)
     unsigned int *buf_start;
     int max_length;
     int *length_ptr;
     unsigned int **buf_ptr;
     VidStream *vid_stream;
{
  
  int length, num_read, i;
  unsigned int request;
  unsigned char *buffer, *mark;
  unsigned int *lmark;
  BOOLEAN swap=vid_stream->swap;
  
  if (vid_stream->EOF_flag) return 0;
  
  length = *length_ptr;
  buffer = (unsigned char *) *buf_ptr;
  
  if (length > 0) {
    memcpy((unsigned char *) buf_start, buffer, (unsigned int) (length*4));
    mark = ((unsigned char *) (buf_start + length));
  }
  else {
    mark = (unsigned char *) buf_start;
    length = 0;
  }
  
  request = (max_length-length)*4;
  
  
  num_read = fread(mark, 1, request, vid_stream->input);
  
  /* Paulo Villegas - 26/1/1993: Correction for 4-byte alignment */
  {
    int num_read_rounded;
    unsigned char *index;
    
    num_read_rounded = 4*(num_read/4);
    
    /* this can happen only if num_read<request; i.e. end of file reached */
    if ( num_read_rounded < num_read ) { 
 	  num_read_rounded = 4*( num_read/4+1 );

 	    /* fill in with zeros */
 	  for( index=mark+num_read; index<mark+num_read_rounded; *(index++)=0 );

 	  /* advance to the next 4-byte boundary */
 	  num_read = num_read_rounded;
    }
  }
  
  if (num_read < 0) {
    return -1;
  } else if (num_read == 0) {
    *buf_ptr = buf_start;
    
    /* Make 32 bits after end equal to 0 and 32
     * bits after that equal to seq end code
     * in order to prevent messy data from infinite
     * recursion.
     */
    
    *(buf_start + length) = 0x0;
    *(buf_start + length+1) = SEQ_END_CODE;
    
    vid_stream->EOF_flag = 1;
    return 0;
  }
  
  lmark = (unsigned int *) mark;
  
  num_read = num_read/4;
  
  if (swap) {
    for (i = 0; i < num_read; i++) {
      *lmark = htonl(*lmark);
      lmark++;
    }
  }
  
  *buf_ptr = buf_start;
  *length_ptr = length + num_read;
  
  return 1;
}




/* 
  Here is the specialist.... 
  Code is adapted from our program demux....
  A bunch of this needs to be #ifdef ANALYSIS'ed
  define __SYSREAD_LOGGING_ON__ to get  an output file for debugging
  */

/* Brown - removed StreamID global variables */
#ifdef ANALYSIS
/* Statistics */
static int gNumAudioPackets;
static int gNumVideoPackets;
static int gNumPaddingPackets;
static int gNumReservedPackets;
static int gNumPrivate_1_Packets;
static int gNumPrivate_2_Packets;
#endif

/*
 *----------------------------------------------------------
 *
 *  read_sys
 *
 *      Parse out a packet of the system layer MPEG file.
 *
 *  Results:  Returns 0 if error or EOF
 *            Returns 1 if more data read (could be just one int)
 *
 *  Side Effects:  ReadPacket can change *bs_ptr to be a new buffer
 *                 buf_ptr will remain pointing at *length_ptr (at input)
 *                         into the buffer
 *                 *length_ptr will be changed to the new size
 *                 *max_length can be changed if a new buffer is alloc'd
 *
 *----------------------------------------------------------
 */
int read_sys(vid_stream, start)
     VidStream *vid_stream;
     unsigned int start;  
     /* start is either a start code or 0 to indicate continued parsing */
{
  unsigned int **bs_ptr=&vid_stream->buf_start;
  int *max_length = &vid_stream->max_buf_length;
  int *length_ptr=&vid_stream->buf_length;
  unsigned int **buf_ptr=&vid_stream->buffer;
  unsigned int startCode;
  int errorCode, PacketReply;
  unsigned char packetID;
  double systemClockTime;
  unsigned long muxRate;
  /* Statistics */
#ifdef ANALYSIS
  static int numPacks = 0;
  static int numPackets = 0;
  static int numSystemHeaders = 0;
#endif
  BOOLEAN match;
  
  if (!start) {
    errorCode = ReadStartCode(&startCode,vid_stream);
    if (vid_stream->EOF_flag) return 0;
    if (errorCode != 0) {
      fprintf(stderr, "Unable to read initial pack start code\n");
      return 0;
    }}
  else {
    errorCode = 0;
    startCode = start;
  }
  
  while (1) {
    match=FALSE;
    if (startCode == PACK_START_CODE) {
#ifdef ANALYSIS
      ++numPacks; 
#endif
      match = TRUE;
      errorCode = ReadPackHeader( &systemClockTime, &muxRate, vid_stream);
      if (errorCode != 0) {
        fprintf(stderr, "Error in reading pack header\n");
        return 0;
      }
      errorCode = ReadStartCode( &startCode, vid_stream );
      if (errorCode != 0) {
        fprintf(stderr, "Error in reading start code\n");
        return 0;
      }
    }
    if (startCode == SYSTEM_HEADER_START_CODE) {
#ifdef ANALYSIS
      ++numSystemHeaders; 
#endif
      match = TRUE;
      errorCode = ReadSystemHeader(vid_stream);
      if (errorCode != 0) {
        fprintf(stderr, "Error in reading system header\n");
        return 0;
      }
      errorCode = ReadStartCode( &startCode, vid_stream );
      if (errorCode != 0) {
        fprintf(stderr,"Error in reading start code after system header\n");
        return 0;
      }
    }
    packetID = startCode & 0xff;
    while (((startCode & PACKET_START_CODE_MASK) == PACKET_START_CODE_PREFIX) &&
	   (packetID >= 0xbc)) {
#ifdef ANALYSIS
      ++numPackets; 
#endif
      match = TRUE;
      packetID = startCode & 0xff;
      PacketReply = ReadPacket(packetID, vid_stream);
      switch (PacketReply) {
      case 2: 
        return 1;
      case 1: 
        return 0;
      default: /* do nothing */
        break;
      }
      errorCode = ReadStartCode( &startCode, vid_stream );
      if (errorCode != 0) {
        fprintf(stderr,"Error in start code after packet\n");
        return 0;
      }
      if (startCode == PACK_START_CODE || startCode == ISO_11172_END_CODE) {
        break;
      }
    }
    
    if (startCode == ISO_11172_END_CODE) {
      match = TRUE;
      if (vid_stream->Parse_done) {
	return 1;
      }
#ifdef ANALYSIS
      fprintf(stderr, "Successful parse of MPEG system level\n");
      fprintf(stderr, "%d system headers, %d packs, %d packets\n",
	      numSystemHeaders, numPacks, numPackets);
      fprintf(stderr, "%d audio packets, %d video packets, %d padding packets\n",
	      gNumAudioPackets, gNumVideoPackets, gNumPaddingPackets);
      fprintf(stderr, "%d reserved packets, %d/%d private type 1/2 packets\n",
	      gNumReservedPackets, gNumPrivate_1_Packets, gNumPrivate_2_Packets);
#endif
      ReadPacket(NOT_PACKET_ID, vid_stream);
      vid_stream->Parse_done = TRUE;
      return 1;
    }
    if (errorCode != 0)
      return 1;
    if (! match) {
      fprintf(stderr,"\nNo match found for start code %08x in system layer, skipping\n",startCode);
      startCode = find_start_code(vid_stream->input);
      if (startCode == EOF) {
        vid_stream->EOF_flag = 1;
        return 0;
      }
    }
  }
}


/*
 *-----------------------------------------------------------
 *
 *  ReadStartCode
 *
 *      Parses a start code out of the stream
 *
 *  Results/Side Effects:  Sets *startCode to the code, returns
 *     1 on error, 0 on success
 *
 *-----------------------------------------------------------
 */
int ReadStartCode(startCode, vid_stream)
     unsigned int *startCode;
     VidStream *vid_stream;
{
  int numRead;
  
  numRead = fread((unsigned char *)startCode, 1, 4, vid_stream->input);
  *startCode = htonl(*startCode);
  
  if (numRead < 4) {
    vid_stream->EOF_flag = 1;
    return 1;
  }

  if ((*startCode&0xfffffe00) != 0) {
    fprintf(stderr,"Problem with system layer parse, skipping to start code\n");
    *startCode = find_start_code(vid_stream->input);
    if (*startCode == EOF) {
       vid_stream->EOF_flag = TRUE;
      return 0;
    }
  }

  return 0;
}


/*
 *-----------------------------------------------------------
 *
 *  find_start_code
 *
 *      Parses a start code out of the stream by tossing bytes until it gets one
 *
 *  Results/Side Effects:  Parses bytes of the stream, returns code
 *                         Returns EOF in case of end of file
 *
 *-----------------------------------------------------------
 */
int find_start_code(input)
    FILE *input;
{
 NO_ZEROS:
  switch(fgetc(input)) {
  case 0:    goto ONE_ZERO;
  case EOF:  goto EOF_FOUND;
  default:   goto NO_ZEROS;
  }

 ONE_ZERO:
  switch(fgetc(input)) {
  case 0:    goto TWO_ZEROS;
  case EOF:  goto EOF_FOUND;
  default:   goto NO_ZEROS;
  }

 TWO_ZEROS:
  switch(fgetc(input)) {
  case 0x01:  goto CODE_FOUND;
  case 0x00:  goto TWO_ZEROS;
  case EOF:  goto EOF_FOUND;
  default:    goto NO_ZEROS;
  }

 CODE_FOUND:
  return 0x00000100+fgetc(input);

 EOF_FOUND:   /* received EOF */
  return EOF;
}




/*
 *-----------------------------------------------------------------
 *
 *  ReadPackHeader
 *
 *      Parses out the PACK header
 *
 *  Returns: 1 on error, 0 on success
 *
 *-------------------------------------------------------------------
 */
int ReadPackHeader(systemClockTime,muxRate, vid_stream)
     double *systemClockTime;
     unsigned long *muxRate;
     VidStream *vid_stream;
{
  int numRead;
  unsigned char inputBuffer[PACK_HEADER_SIZE];
  unsigned long systemClockRef;
  unsigned char systemClockRefHiBit;
  int errorCode;
  
  numRead = fread(inputBuffer, 1, PACK_HEADER_SIZE, vid_stream->input);
  if (numRead < PACK_HEADER_SIZE) {
    vid_stream->EOF_flag = 1;
    return 1;
  }
  ReadTimeStamp(inputBuffer, &systemClockRefHiBit, &systemClockRef);
  errorCode = MakeFloatClockTime(systemClockRefHiBit, systemClockRef, 
				 systemClockTime);
  ReadRate(&inputBuffer[5], muxRate);
  *muxRate *= MUX_RATE_SCALE_FACTOR;
  return 0;
}


/*
 *------------------------------------------------------------------
 *
 *   ReadSystemHeader
 *
 *      Parse out the system header, setup out stream IDs for parsing packets
 *
 *   Results:  Returns 1 on error, 0 on success.
 *             Sets gAudioStreamID and gVideoStreamID
 *
 *------------------------------------------------------------------
 */
int ReadSystemHeader(vid_stream)
   VidStream *vid_stream;
{ 
  unsigned char *inputBuffer = NULL;
  int numRead;
  int pos;
  unsigned short headerSize;
  unsigned char streamID;
  
  numRead = fread((char *)&headerSize, 1, 2, vid_stream->input); 
  headerSize = ntohs(headerSize);
  if (numRead != 2) {
    vid_stream->EOF_flag = 1;
    return 1;
  }
  inputBuffer = (unsigned char *) malloc((unsigned int) headerSize+1);
  if (inputBuffer == NULL) {
    return 1;
  }
  inputBuffer[headerSize]=0;
  numRead = fread(inputBuffer, 1, headerSize, vid_stream->input); 
  /* Brown - get rid of Ansi C complaints */
  if (numRead < (int) headerSize) {
    vid_stream->EOF_flag = 1;
    return 1;
  }
  
  pos = 6;
  while ((inputBuffer[pos] & 0x80) == 0x80) {
    streamID = inputBuffer[pos];
    switch (streamID) {
    case STD_VIDEO_STREAM_ID: 
      break;
    case STD_AUDIO_STREAM_ID: 
      break;
    case RESERVED_STREAM_ID: 
      break;
    case PADDING_STREAM_ID: 
      break;
    case PRIVATE_STREAM_1_ID: 
      break;
    case PRIVATE_STREAM_2_ID: 
      break;
    default:
      if (streamID < MIN_STREAM_ID_ID) {
	return 1;
      }
      switch (streamID >> 4) {
      case 0xc:
      case 0xd:
	vid_stream->gAudioStreamID = streamID;
	break;
      case 0xe:
	if ((vid_stream->gVideoStreamID != 0) &&
	    (vid_stream->gVideoStreamID!=streamID)) {
	  break;
	}
	vid_stream->gVideoStreamID = streamID;
	break;
      case 0xf:
/*Brown - deglobalized gReservedStreamID */
	vid_stream->gReservedStreamID = streamID;
	break;
      }
      break;
    }
    pos += 3;
  }
  if (inputBuffer != NULL)
    free(inputBuffer);
  return 0;
}


/*
 *-----------------------------------------------------------------
 *
 *  ReadPacket
 *
 *      Reads a single packet out of the stream, and puts it in the
 *      buffer if it is video.
 *
 *  Results:
 *      Changes the value of *length_ptr to be the new length (plus old)
 *      If the buffer is too small, can change *bs_ptr, *max_length, and 
 *      buf_ptr to be correct for a newly allocated buffer.
 *
 *  State:  
 *      The buffer is in ints, but the packets can be an arbitrary number
 *      of bytes, so leftover bytes are kept in the VidStream variable and
 *      are added on the next call.
 *
 *-----------------------------------------------------------------
 */   
#ifdef __STDC__
int ReadPacket(unsigned char packetID, VidStream *vid_stream)
#else
int ReadPacket(packetID, vid_stream)
     unsigned char packetID;
     VidStream *vid_stream;
#endif

     /* Returns:
	0 - no error, but not video packet we want
	1 - error
	2 - got video packet into buffer
	*/
{   
  unsigned int **bs_ptr=&vid_stream->buf_start;
  int *max_length = &vid_stream->max_buf_length;
  int *length_ptr=&vid_stream->buf_length;
  unsigned int **buf_ptr=&vid_stream->buffer;
  int ioBytes;
  unsigned char nextByte;
  unsigned short packetLength;
  unsigned char *packetBuffer = NULL;
  int pos;
  int numStuffBytes = 0;
  unsigned int packetDataLength;
  int byte_length;
  unsigned char scratch[10];
  /* Leftovers from previous video packets */
  
  if (packetID == NOT_PACKET_ID) {
    /* Gross hack to handle unread bytes before end of stream */
    if (vid_stream->num_left != 0) {
      /* Sigh, deal with previous leftovers */
      *(*buf_ptr+*length_ptr) = vid_stream->leftover_bytes;
      *(*buf_ptr+*length_ptr+1) = ISO_11172_END_CODE;
      *length_ptr += 2;
    } else {
      *(*buf_ptr+*length_ptr) = ISO_11172_END_CODE;
      *length_ptr += 1;
    }
    return 1;
  } else if (packetID==KILL_BUFFER) {
    vid_stream->num_left=0;
    vid_stream->leftover_bytes=0;
    return 0;
  }
  
  ioBytes = fread(&packetLength, 1, 2, vid_stream->input);
  packetLength = htons(packetLength);
  if (ioBytes < 2) {
    return 1;
  }
  if (packetID == vid_stream->gAudioStreamID) {
#ifdef ANALYSIS
    ++gNumAudioPackets;
#endif
  }
  else if (packetID == vid_stream->gVideoStreamID) {
#ifdef ANALYSIS     
    ++gNumVideoPackets;
#endif
  }
  else {
    switch (packetID) {
    case PADDING_STREAM_ID:
#ifdef ANALYSIS
      ++gNumPaddingPackets;
#endif
      break;
    case RESERVED_STREAM_ID:
#ifdef ANALYSIS
      ++gNumReservedPackets;
#endif
      break;
    case PRIVATE_STREAM_1_ID:
#ifdef ANALYSIS
      ++gNumPrivate_1_Packets;
#endif
      break;
    case PRIVATE_STREAM_2_ID:
#ifdef ANALYSIS
      ++gNumPrivate_2_Packets;
#endif
      break;
    default:
      fprintf(stderr, "\nUnknown packet type encountered. P'bly audio? (%x) at %d\n",
	      packetID,(int) ftell(vid_stream->input));
    }
    if (packetID != vid_stream->gVideoStreamID) {/* changed by jim */
      fseek(vid_stream->input, packetLength, 1);
      return 0;
    }
  }

  fread(&nextByte,1,1,vid_stream->input);
  pos = 0;
  while (nextByte & 0x80) {
    ++numStuffBytes;
    ++pos;
    fread(&nextByte,1,1,vid_stream->input);
  }
  if ((nextByte >> 6) == 0x01) {
    pos += 2;
    fread(&nextByte,1,1,vid_stream->input);
    fread(&nextByte,1,1,vid_stream->input);
  } 
  if ((nextByte >> 4) == 0x02) {
    scratch[0] = nextByte;                      /* jim */
    fread(&scratch[1],1,4,vid_stream->input);   /* jim */
    fread(&nextByte,1,1,vid_stream->input);
    pos += 5;
  }
  else if ((nextByte >> 4) == 0x03) {
    scratch[0] = nextByte;                      /* jim */
    fread(&scratch[1],1,9,vid_stream->input);   /* jim */
    fread(&nextByte,1,1,vid_stream->input);
    pos += 10;
  } 
  else {
    fread(&nextByte,1,1,vid_stream->input);
    pos += 1;
  }
  /* Read all the headers, now make room for packet */
  if (*bs_ptr + *max_length < *buf_ptr+ packetLength/4 + *length_ptr) {
     /* Brown - get rid of Ansi C complaints */
    if (*max_length - *length_ptr < (int) packetLength/4) {
      /* Buffer too small for a packet (plus whats there), 
	   * time to enlarge it! 
	   */
      unsigned int *old = *bs_ptr;
      *max_length = *length_ptr + packetLength/2;
      *bs_ptr=(unsigned int *)malloc(*max_length*4);
      if (*bs_ptr == NULL) {
        return 1;
      }
      memcpy((unsigned char *)*bs_ptr, *buf_ptr, (unsigned int) *length_ptr*4);
      free(old);
      *buf_ptr = *bs_ptr;
    } else {
      memcpy((unsigned char *)*bs_ptr, *buf_ptr, (unsigned int) *length_ptr*4);
      *buf_ptr = *bs_ptr;
    }}
  byte_length = *length_ptr*4;
  if (vid_stream->num_left != 0) {
    /* Sigh, deal with previous leftovers */
    byte_length += vid_stream->num_left;
    *(*buf_ptr+*length_ptr) = vid_stream->leftover_bytes;
  }
  packetBuffer=((unsigned char *)*buf_ptr)+byte_length;
  packetDataLength = packetLength - pos;
  *packetBuffer++ = nextByte;
/* Brown - deglobalize gVideoStreamID */
  if (packetID == vid_stream->gVideoStreamID) {
    ioBytes = fread(packetBuffer, 1, packetDataLength-1, vid_stream->input);
    if (ioBytes != packetDataLength-1) {
      vid_stream->EOF_flag = 1;
      return 1;
    }
    if (1 != ntohl(1)) {
      unsigned int *mark = *buf_ptr+*length_ptr;
      int i;
      
      for (i=0; i < ((packetDataLength+
			 vid_stream->num_left)&0xfffffffc); i+=4) {
        *mark=ntohl(*mark);
        mark++;
      }
    }
    byte_length = byte_length + packetDataLength;
    vid_stream->num_left = byte_length % 4;
    *length_ptr = byte_length / 4;
    vid_stream->leftover_bytes = *(*buf_ptr + *length_ptr);
    return 2;
  }
  else if (packetID == vid_stream->gAudioStreamID) { 
    packetBuffer = (unsigned char *)(*buf_ptr + *length_ptr + 1);
    fread(packetBuffer, 1, packetDataLength - 1, vid_stream->input);
  }
  else /* Donno what it is, just nuke it */ {
    /* This code should be unreachable */
    packetBuffer = (unsigned char *)(*buf_ptr + *length_ptr + 1);
    fread(packetBuffer, 1, packetDataLength - 1, vid_stream->input);
  }
  return 0; 
}


/*
 * The remaining procedures are formatting utility procedures.
 */
void ReadTimeStamp(inputBuffer,hiBit,low4Bytes)
     unsigned char *inputBuffer, *hiBit;
     unsigned long *low4Bytes;
{
  *hiBit = ((unsigned long)inputBuffer[0] >> 3) & 0x01;
  *low4Bytes = (((unsigned long)inputBuffer[0] >> 1) & 0x03) << 30; 
  *low4Bytes |= (unsigned long)inputBuffer[1] << 22; 
  *low4Bytes |= ((unsigned long)inputBuffer[2] >> 1) << 15; 
  *low4Bytes |= (unsigned long)inputBuffer[3] << 7; 
  *low4Bytes |= ((unsigned long)inputBuffer[4]) >> 1; 
}

void ReadSTD(inputBuffer,stdBufferScale,stdBufferSize) 
unsigned char *inputBuffer;
unsigned char *stdBufferScale;
unsigned long *stdBufferSize;
{
  /* Brown - get rid of ANSI C complaints */
  *stdBufferScale = ((int)(inputBuffer[0] & 0x20) >> 5); 
  *stdBufferSize = ((unsigned long)inputBuffer[0] & 0x1f) << 8;
  *stdBufferSize |= (unsigned long)inputBuffer[1];
}


void ReadRate(inputBuffer,rate)
     unsigned char *inputBuffer;
     unsigned long *rate;
{
  *rate = (inputBuffer[0] & 0x7f) << 15;
  *rate |= inputBuffer[1] << 7;
  /* Brown - get rid of ANSI C complaints */
  *rate |= (int) (inputBuffer[2] & 0xfe) >> 1;
}

#define FLOAT_0x10000 (double)((unsigned long)1 << 16)

#ifdef __STDC__
int MakeFloatClockTime(unsigned char hiBit, unsigned long low4Bytes,
		       double * floatClockTime)
#else
int MakeFloatClockTime(hiBit,low4Bytes,floatClockTime)
     unsigned char hiBit;
     unsigned long low4Bytes;
     double *floatClockTime;
#endif
{
  if (hiBit != 0 && hiBit != 1) {
    *floatClockTime = 0.0;
    return 1;
  }
  *floatClockTime 
    = (double)hiBit*FLOAT_0x10000*FLOAT_0x10000 + (double)low4Bytes;
  *floatClockTime /= (double)STD_SYSTEM_CLOCK_FREQ;
  return 0;
}
