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

/* This file contains C code that implements
 * the video decoder model.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <unistd.h>

#ifndef MIPS
#include <sys/time.h>
#else
#include <sys/types.h>
#include <sys/system.h>
#endif

#include "decoders.h"
#include "video.h"
#include "util.h"
#include "proto.h"
#include "tcl.h"
#include "tk.h"


#define SKIP_NONE   0
#define SKIP_REF    1


/* Declarations of functions. */
static void ReconIMBlock();
static void ReconPMBlock();
static void ReconBMBlock();
static void ReconBiMBlock();
static void ReconSkippedBlock();
static int DoPictureDisplay();
static int ParseSeqHead();
static int ParseGOP();
static int ParsePicture();
static int ParseSlice();
static int ParseMacroBlock();
static void ProcessSkippedPFrameMBlocks();
static void ProcessSkippedBFrameMBlocks();
 
extern int ditherType;
char *ditherFlags;
int handlingError = 0;
int first = 1;

extern int numSkipped;
extern float percentPlay;

extern FILE        * input;
extern int           startedGOP;
extern int           justRewound;
extern int           EOF_flag;

void recordFrameNo( VidStream * vid_stream);

/* Macro for returning 1 if num is positive, -1 if negative, 0 if 0. */

#define Sign(num) ((num > 0) ? 1 : ((num == 0) ? 0 : -1))

/* Declare global pointer to vid stream used for current decoding. */

VidStream *curVidStream = NULL;

/* set up array to use for frames per second */

double VidRateNum[16]={0.0, 23.976, 24.0, 25.0, 29.97, 30.0, 50.0 ,59.94, 60.0,
  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};


/* Set up array for fast conversion from zig zag order to row/column
   coordinates.
*/


int zigzag[64][2] = {
  0, 0, 1, 0, 0, 1, 0, 2, 1, 1, 2, 0, 3, 0, 2, 1, 1, 2, 0, 3, 0, 4, 1, 3,
  2, 2, 3, 1, 4, 0, 5, 0, 4, 1, 3, 2, 2, 3, 1, 4, 0, 5, 0, 6, 1, 5, 2, 4,
  3, 3, 4, 2, 5, 1, 6, 0, 7, 0, 6, 1, 5, 2, 4, 3, 3, 4, 2, 5, 1, 6, 0, 7,
  1, 7, 2, 6, 3, 5, 4, 4, 5, 3, 6, 2, 7, 1, 7, 2, 6, 3, 5, 4, 4, 5, 3, 6,
  2, 7, 3, 7, 4, 6, 5, 5, 6, 4, 7, 3, 7, 4, 6, 5, 5, 6, 4, 7, 5, 7, 6, 6,
7, 5, 7, 6, 6, 7, 7, 7};
/* Array mapping zigzag to array pointer offset. */

int zigzag_direct[64] = {
  0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12,
  19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
  42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
/* Set up array for fast conversion from row/column coordinates to
   zig zag order.
*/

int scan[8][8] = {
  {0, 1, 5, 6, 14, 15, 27, 28},
  {2, 4, 7, 13, 16, 26, 29, 42},
  {3, 8, 12, 17, 25, 30, 41, 43},
  {9, 11, 18, 24, 31, 40, 44, 53},
  {10, 19, 23, 32, 39, 45, 52, 54},
  {20, 22, 33, 38, 46, 51, 55, 60},
  {21, 34, 37, 47, 50, 56, 59, 61},
{35, 36, 48, 49, 57, 58, 62, 63}};
/* Initialize P and B skip flags. */

static int No_P_Flag = 0;
static int No_B_Flag = 0;

/* Max lum, chrom indices for illegal block checking. */

static int lmaxx;
static int lmaxy;
static int cmaxx;
static int cmaxy;

/*
 * We use a lookup table to make sure values stay in the 0..255 range.
 * Since this is cropping (ie, x = (x < 0)?0:(x>255)?255:x; ), wee call this
 * table the "crop table".
 * MAX_NEG_CROP is the maximum neg/pos value we can handle.
 */

#define MAX_NEG_CROP 384
#define NUM_CROP_ENTRIES (256+2*MAX_NEG_CROP)
#define assertCrop(x)	assert(((x) >= -MAX_NEG_CROP) && \
			       ((x) <= 256+MAX_NEG_CROP)) 
static unsigned char cropTbl[NUM_CROP_ENTRIES];

/* used to present info on bitrate */
#define MAX_EXPECTED_SIZE           1799

unsigned int bitCount = 0;

int showmb_flag = 0;
int showEachFlag = 1;

typedef struct {
  int frametype;
  unsigned int totsize;
  unsigned int number;
  unsigned int i_mbsize;
  unsigned int p_mbsize;
  unsigned int b_mbsize;
  unsigned int bi_mbsize;
  unsigned int i_mbnum;
  unsigned int p_mbnum;
  unsigned int b_mbnum;
  unsigned int bi_mbnum;
  unsigned int i_mbcbp[64];
  unsigned int p_mbcbp[64];
  unsigned int b_mbcbp[64];
  unsigned int bi_mbcbp[64];
  unsigned int i_mbcoeff[64];
  unsigned int p_mbcoeff[64];
  unsigned int b_mbcoeff[64];
  unsigned int bi_mbcoeff[64];
  double tottime;
} Statval;

Statval stat_a[4];
unsigned int pictureSizeCount;
unsigned int mbSizeCount;
unsigned int *mbCBPPtr, *mbCoeffPtr, *mbSizePtr;
unsigned int cacheHit[8][8];
unsigned int cacheMiss[8][8];


static void
init_stat_struct(astat)
  Statval *astat;
{
  int j;

  astat->frametype = 0;
  astat->totsize = 0;
  astat->number = 0;
  astat->i_mbsize = 0;
  astat->p_mbsize = 0;
  astat->b_mbsize = 0;
  astat->bi_mbsize = 0;
  astat->i_mbnum = 0;
  astat->p_mbnum = 0;
  astat->b_mbnum = 0;
  astat->bi_mbnum = 0;

  for (j = 0; j < 64; j++) {

    astat->i_mbcbp[j] = 0;
    astat->p_mbcbp[j] = 0;
    astat->b_mbcbp[j] = 0;
    astat->bi_mbcbp[j] = 0;
    astat->i_mbcoeff[j] = 0;
    astat->p_mbcoeff[j] = 0;
    astat->b_mbcoeff[j] = 0;
    astat->bi_mbcoeff[j] = 0;
  }
  astat->tottime = 0.0;
}

void
init_stats()
{
  int i, j;
  
  for (i = 0; i < 4; i++) {
    init_stat_struct(&(stat_a[i]));
    stat_a[i].frametype = i;
  }
  
  for (i = 0; i < 8; i++) {
    for (j = 0; j < 8; j++) {
      cacheHit[i][j] = 0;
      cacheMiss[i][j] = 0;
    }
  }
  
  bitCount = 0;
}

/* add command to tcl script to be execuated at current frame
   display time */
static void
AddToScript( video_stream, scriptCommand ) 
     VidStream *video_stream;
     char *scriptCommand;
{
  /* if there is no buffer for the currentScript, allocate one */
  if ( !(video_stream->currentScript )) {
    video_stream->currentScript = 
      (char *)malloc( video_stream->currentScriptBufSize*sizeof(char));
    video_stream->currentScript[0] = '\0';
  }

  /* grow the buffer if necessary */
  if (( video_stream->currentScriptLength + strlen(scriptCommand)) >= 
      video_stream->currentScriptBufSize) {
    char * tmp = video_stream->currentScript;
    video_stream->currentScriptBufSize *= 2;
    video_stream->currentScript = 
      (char *)malloc( video_stream->currentScriptBufSize*sizeof(char));
    if (tmp) {
      strcpy( video_stream->currentScript, tmp );
      /*free(tmp);*/
    } else {
      (video_stream->currentScript)[0] = '\0';
    }
  }
  
  /* write the command into the script */
  video_stream->currentScriptLength += strlen(scriptCommand);
  strcat( video_stream->currentScript, scriptCommand );
  (video_stream->currentScript)[video_stream->currentScriptLength] = '\0';
  
}

static unsigned int
bitCountRead()
{
  return bitCount;
}


static void
StartTime()
{
  stat_a[0].tottime = ReadSysClock();
}

static void
EndTime()
{
  stat_a[0].tottime = ReadSysClock() - stat_a[0].tottime;
}

double realTimeStart;
int totNumFrames = 0;

double
ReadSysClock()
{
  struct timeval tv;
  (void) gettimeofday(&tv, (struct timezone *)NULL);
  return (tv.tv_sec + tv.tv_usec / 1000000.0);
}

void
init_clock()
{
    realTimeStart = ReadSysClock();
}

void
PrintTimeInfo()
{
  double spent;

  spent = ReadSysClock() - realTimeStart;
}



/*
 *--------------------------------------------------------------
 *
 * NewVidStream --
 *
 *	Allocates and initializes a VidStream structure. Takes
 *      as parameter requested size for buffer length.
 *
 * Results:
 *	A pointer to the new VidStream structure.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

VidStream *
NewVidStream(bufLength)
  int bufLength;
{
  int i, j;
  VidStream *new;
  static unsigned char default_intra_matrix[64] = {
    8, 16, 19, 22, 26, 27, 29, 34,
    16, 16, 22, 24, 27, 29, 34, 37,
    19, 22, 26, 27, 29, 34, 34, 38,
    22, 22, 26, 27, 29, 34, 37, 40,
    22, 26, 27, 29, 32, 35, 40, 48,
    26, 27, 29, 32, 35, 40, 48, 58,
    26, 27, 29, 34, 38, 46, 56, 69,
  27, 29, 35, 38, 46, 56, 69, 83};

  /* Check for legal buffer length. */

  if (bufLength < 4)
    return NULL;

  /* Make buffer length multiple of 4. */

  bufLength = (bufLength + 3) >> 2;

  /* Allocate memory for new structure. */

  new = (VidStream *) malloc(sizeof(VidStream));

  /* Initialize pointers to extension and user data. */

  new->group.ext_data = new->group.user_data =
    new->picture.extra_info = new->picture.user_data =
    new->picture.ext_data = new->slice.extra_info =
    new->ext_data = new->user_data = NULL;

  /* Copy default intra matrix. */

  for (i = 0; i < 8; i++) {
    for (j = 0; j < 8; j++) {
      new->intra_quant_matrix[j][i] = default_intra_matrix[i * 8 + j];
    }
  }

  /* Initialize crop table. */

  for (i = (-MAX_NEG_CROP); i < NUM_CROP_ENTRIES - MAX_NEG_CROP; i++) {
    if (i <= 0) {
      cropTbl[i + MAX_NEG_CROP] = 0;
    } else if (i >= 255) {
      cropTbl[i + MAX_NEG_CROP] = 255;
    } else {
      cropTbl[i + MAX_NEG_CROP] = i;
    }
  }

  /* Initialize non intra quantization matrix. */

  for (i = 0; i < 8; i++) {
    for (j = 0; j < 8; j++) {
      new->non_intra_quant_matrix[j][i] = 16;
    }
  }

  /* Initialize pointers to image spaces. */

  new->current = new->past = new->future = NULL;

  /* Initialize script variables */
  new->currentScript = (char *)NULL;
  new->futureScript = (char *)NULL;
  new->currentScriptLength = 0;
  new->currentScriptBufSize = MB_QUANTUM*20;
  for (i = 0; i < RING_BUF_SIZE; i++) {
    new->ring[i] = NULL;
  }

  /* initialize monitoring variables */
  new->bitAryIndex = 0;
  new->bitAryValid = 0;
  new->bitArySize = INITIAL_FRAME_ARY_SIZE;
  new->bitsPastSecAry = 
    (int *)malloc( sizeof(int)*INITIAL_FRAME_ARY_SIZE);

  /* initialize state list */
  new->stateListSize = 0;
  new->stateList = (StateHolder *)malloc( sizeof(StateHolder));
  new->stateList->next = new->stateList;
  new->stateList->prev = new->stateList;

  /* initialize size of current picture to 0 */
  new->currentSize = 0;

  /* Create buffer. */

  new->buf_start = (unsigned int *) malloc(bufLength * 4);

  /*
   * Set max_buf_length to one less than actual length to deal with messy
   * data without proper seq. end codes.
   */

  new->max_buf_length = bufLength - 1;

  /* Initialize bitstream i/o fields. */

  new->bit_offset = 0;
  new->buf_length = 0;
  new->buffer = new->buf_start;


  /* initialize firstSeqHead */
  new->firstSeqHead = 0;

  /* Return structure. */

  return new;
}



/*
 *--------------------------------------------------------------
 *
 * DestroyVidStream --
 *
 *	Deallocates a VidStream structure.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */
void
DestroyVidStream(bstream)
  VidStream **bstream;
{
  int i;
    VidStream *astream;

    astream = *bstream;

  if (astream->currentScript != NULL)      free(astream->currentScript);
  if (astream->futureScript != NULL)       free(astream->futureScript); 
  if (astream->bitsPastSecAry != NULL)     free(astream->bitsPastSecAry); 
  if (astream->ext_data != NULL )	   free(astream->ext_data);
  if (astream->user_data != NULL)	   free(astream->user_data);
  if (astream->group.ext_data != NULL)     free(astream->group.ext_data);
  if (astream->group.user_data != NULL)	   free(astream->group.user_data);
  if (astream->picture.extra_info != NULL) free(astream->picture.extra_info);
  if (astream->picture.ext_data != NULL)   free(astream->picture.ext_data);
  if (astream->picture.user_data != NULL)  free(astream->picture.user_data);
  if (astream->slice.extra_info != NULL)   free(astream->slice.extra_info);
  /* buffer? -- no -- just a pointer */
  if (astream->buf_start != NULL)	   free(astream->buf_start);

  for (i = 0; i < RING_BUF_SIZE; i++) {
    if (astream->ring[i] != NULL) {
      DestroyPictImage(astream->ring[i]);
      astream->ring[i] = NULL;
    }
  }
  
  if (astream->stateList != NULL) {
    while (astream->stateListSize != 0) {
      StateHolder * tmp;
      tmp = astream->stateList->next;
      astream->stateList->next = astream->stateList->next->next;
      free(tmp);
      (astream->stateListSize)--;
    }
    free( astream->stateList);
  }


  free((char *) astream);

  *bstream = NULL;
}


/*
 *--------------------------------------------------------------
 *
 * NewPictImage --
 *
 *	Allocates and initializes a PictImage structure.
 *      The width and height of the image space are passed in
 *      as parameters.
 *
 * Results:
 *	A pointer to the new PictImage structure.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

PictImage *
NewPictImage(width, height)
  unsigned int width, height;
{
  PictImage *new;

  /* Allocate memory space for new structure. */

  new = (PictImage *) malloc(sizeof(PictImage));


  /* Allocate memory for image spaces. */

  {
  shmemerror:
    
    if ((ditherType == Twox2_DITHER) ||
	(ditherType == BIG_ORDERED_DITHER) ||
	(ditherType == FULL_COLOR_DITHER)) {
      new->display = (unsigned char *) malloc(width * height * 4);
    }
    else if (ditherType == RGBL_DITHER) {
      new->display = (unsigned char *) malloc(width * height * 9);
    } else if (ditherType == HALFTONE_DITHER) {
      new->display = (unsigned char *) malloc(width * height * 16);
    } else {
      new->display = (unsigned char *) malloc(width * height);
    }
  }

  new->luminance = (unsigned char *) malloc(width * height);
  new->Cr = (unsigned char *) malloc(width * height / 4);
  new->Cb = (unsigned char *) malloc(width * height / 4);

  /* Reset locked flag. */

  new->locked = 0;

  /* Return pointer to new structure. */

  return new;
}



/*
 *--------------------------------------------------------------
 *
 * DestroyPictImage --
 *
 *	Deallocates a PictImage structure.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */
void
DestroyPictImage(apictimage)
  PictImage *apictimage;
{
  if (apictimage->luminance != NULL) {
    free(apictimage->luminance);
  }
  if (apictimage->Cr != NULL) {
    free(apictimage->Cr);
  }
  if (apictimage->Cb != NULL) {
    free(apictimage->Cb);
  }
  if (apictimage->display != NULL) {
    free(apictimage->display);
  }
  free(apictimage);
}



/*
 *--------------------------------------------------------------
 *
 * mpegVidRsrc --
 *
 *      Parses bit stream until MB_QUANTUM number of
 *      macroblocks have been decoded or current slice or
 *      picture ends, whichever comes first. If the start
 *      of a frame is encountered, the frame is time stamped
 *      with the value passed in time_stamp. If the value
 *      passed in buffer is not null, the video stream buffer
 *      is set to buffer and the length of the buffer is
 *      expected in value passed in through length. The current
 *      video stream is set to vid_stream. If vid_stream
 *      is passed as NULL, a new VidStream structure is created
 *      and initialized and used as the current video stream.
 *
 * Results:
 *      A pointer to the video stream structure used.
 *
 * Side effects:
 *      Bit stream is irreversibly parsed. If a picture is completed,
 *      a function is called to display the frame at the correct time.
 *
 *--------------------------------------------------------------
 */

VidStream *
mpegVidRsrc(time_stamp, vid_stream)
  TimeStamp time_stamp;
  VidStream *vid_stream;
{
  extern int first;
  unsigned int data;
  int i, status;
  extern jmp_buf env2;
  extern Tcl_Interp *interp;

  /*
   * Set global curVidStream to vid_stream. Necessary because bit i/o use
   * curVidStream and are not passed vid_stream. Also set global bitstream
   * parameters.
   */
  
  curVidStream = vid_stream;
  bitOffset = curVidStream->bit_offset;
#ifdef UTIL2
  curBits = *curVidStream->buffer << bitOffset;
#else
  curBits = *curVidStream->buffer;
#endif
  bufLength = curVidStream->buf_length;
  bitBuffer = curVidStream->buffer;
  
  /*
   * If called for the first time, find start code, make sure it is a
   * sequence start code.
   */
  
  if (first) {
    init_clock();
    next_start_code();
    show_bits32(data);
    if (data != SEQ_START_CODE) {
      Tcl_Eval(interp, "AnnounceError \"Beginning of stream is not a sequence start code\"");
      longjmp( env2, 1);
    }
    first = 0;
  }
  /* Get next 32 bits (size of start codes). */
  
  show_bits32(data);
  
  /*
   * Process according to start code (or parse macroblock if not a start code
   * at all.
   */
  
  switch (data) {
    
  case SEQ_END_CODE:
    
    /* Display last frame. */
    if (vid_stream->future != NULL) {
      vid_stream->current = vid_stream->future;
      if ( ! ExecuteDisplay(vid_stream) )
	return NULL;
    }
    
    /* Sequence done. Do the right thing. For right now, exit. */
    Tcl_Eval( interp, "RestoreControls\n");
    return NULL;
    break;
    
  case SEQ_START_CODE:
    
    /* Sequence start code. Parse sequence header. */
    if (ParseSeqHead(vid_stream) != PARSE_OK)
      goto error;
    
    /*
     * Return after sequence start code so that application above can use
     * info in header.
     */
    
    goto done;
    
  case GOP_START_CODE: {
    /* Group of Pictures start code. Parse gop header. */
    /* Fist save state so we can reverse to this point */
    StateHolder * newState = (StateHolder *)malloc( sizeof(StateHolder));
    newState->bit_offset = bitOffset;
    newState->fileOffset = (ftell(input) - 4*bufLength);
    newState->bitAryIndex = vid_stream->bitAryIndex;

    /* pare down list if necessary */
    if ( vid_stream->stateListSize > REWIND_SIZE ) {
      StateHolder * deadMeat = vid_stream->stateList->prev;
      deadMeat->prev->next = vid_stream->stateList;
      vid_stream->stateList->prev = deadMeat->prev;
      free( deadMeat );
      (vid_stream->stateListSize)--;
    }
    
    /* Put new state at top of list */
    newState->next = vid_stream->stateList->next;
    newState->prev = vid_stream->stateList;
    vid_stream->stateList->next->prev = newState;
    vid_stream->stateList->next = newState;
    (vid_stream->stateListSize)++;
    
    /* Note that you just parsed a GOP header */
    startedGOP = TRUE;

    if (ParseGOP(vid_stream) != PARSE_OK)
      goto error;
  }
    
  case PICTURE_START_CODE:
    /* Picture start code. Parse picture header and first slice header. */
    
    status = ParsePicture(vid_stream, time_stamp);
    
    if (status == SKIP_PICTURE) {
      numSkipped++;
      
      next_start_code();
      /*#ifdef BLEAH*/
      fprintf(stderr, "Skipping picture...\n");
      /*#endif*/
      while (!next_bits(32, PICTURE_START_CODE)) {
	if (next_bits(32, GOP_START_CODE))
	  break;
	else if (next_bits(32, SEQ_END_CODE))
	  break;
	flush_bits(24);
	next_start_code();
      }
      goto done;
    } else if (status != PARSE_OK)
      goto error;
    
    if (ParseSlice(vid_stream) != PARSE_OK)
      {
	fprintf(stdout, "ERROR IN PARSE SLICE\n");
	goto error;
      }
    break;
    
  default:
    
    /* Check for slice start code. */
    
    if ((data >= SLICE_MIN_START_CODE) && (data <= SLICE_MAX_START_CODE)) {
      
      /* Slice start code. Parse slice header. */
      
      if (ParseSlice(vid_stream) != PARSE_OK)
	goto error;
    }
    /* otherwise, is probably end of slice ? */

    break;
  }

  /* Parse next MB_QUANTUM macroblocks. */

  for (i = 0; i < MB_QUANTUM; i++) {
    /* Check to see if actually a startcode and not a macroblock. */
    
    if (!next_bits(23, 0x00000000)) {
      /* Not start code. Parse Macroblock. */
      
      
      if (ParseMacroBlock(vid_stream) != PARSE_OK)
	{
	  fprintf(stdout, "ERROR PARSING MACROBLOCK!!!!!!!!!!!!!!!!!\n");
	  
	  goto error;
	}
      
      if (showmb_flag) {
	DoDitherImage(vid_stream->current->luminance, vid_stream->current->Cr,
		      vid_stream->current->Cb, vid_stream->current->display,
		      vid_stream->mb_height * 16, vid_stream->mb_width * 16);
	if ( ! ExecuteDisplay(vid_stream) )
	  return NULL;
      }
      
    } else {
      /* Not macroblock, actually start code. Get start code. */

      next_start_code();
      show_bits32(data);

      /*
       * If start code is outside range of slice start codes, frame is
       * complete, display frame.
       */

      if ((data < SLICE_MIN_START_CODE) || (data > SLICE_MAX_START_CODE)) {
	EndTime();
	stat_a[0].totsize += bitCountRead() - pictureSizeCount;
	if ( ! DoPictureDisplay(vid_stream) )
	  return NULL;
      }
      break;
    }
  }
  
  
  /* ADDED BY KEVING */
  if ((i == MB_QUANTUM) && next_bits(23, 0x00000000)) {
    
    /* Not macroblock, actually start code. Get start code. */
    
    next_start_code();
    show_bits32(data);
    
    /*
     * If start code is outside range of slice start codes, frame is
     * complete, display frame.
     */
    
    if ((data < SLICE_MIN_START_CODE) || (data > SLICE_MAX_START_CODE)) {
      
      EndTime();
      stat_a[0].totsize += bitCountRead() - pictureSizeCount;
      
      if ( ! DoPictureDisplay(vid_stream) )
	return NULL;
    }
  }
  

  /* Return pointer to video stream structure. */

  goto done;

error:
  fprintf(stderr, "Error!!!!\n");
  next_start_code();
  goto done;

done:

  /* Copy global bit i/o variables back into vid_stream. */

  vid_stream->buffer = bitBuffer;
  vid_stream->buf_length = bufLength;
  vid_stream->bit_offset = bitOffset;

  return vid_stream;

}



/*
 *--------------------------------------------------------------
 *
 * ParseSeqHead --
 *
 *      Assumes bit stream is at the begining of the sequence
 *      header start code. Parses off the sequence header.
 *
 * Results:
 *      Fills the vid_stream structure with values derived and
 *      decoded from the sequence header. Allocates the pict image
 *      structures based on the dimensions of the image space
 *      found in the sequence header.
 *
 * Side effects:
 *      Bit stream irreversibly parsed off.
 *
 *--------------------------------------------------------------
 */

static int
ParseSeqHead(vid_stream)
  VidStream *vid_stream;
{

  unsigned int data;
  char command[256];
  extern Tcl_Interp *interp;
  int i;

  /* Flush off sequence start code. */

  flush_bits32;

  /* Get horizontal size of image space. */

  get_bits12(data);
  vid_stream->h_size = data;

  /* Get vertical size of image space. */

  get_bits12(data);
  vid_stream->v_size = data;

  /* Calculate macroblock width and height of image space. */

  vid_stream->mb_width = (vid_stream->h_size + 15) / 16;
  vid_stream->mb_height = (vid_stream->v_size + 15) / 16;

  /* Set display width and height and make macroblock display */
  if (!vid_stream->firstSeqHead) {
    sprintf( command, "MakeBoxes %d %d\n", 
	    vid_stream->mb_width, 
	    vid_stream->mb_height);
    Tcl_Eval( interp, command);
  }

  /* If dither type is MBORDERED allocate ditherFlags. */
  
  if (ditherType == MBORDERED_DITHER) {
    ditherFlags = (char *) malloc(vid_stream->mb_width*vid_stream->mb_height);
  }

  /* Initialize lmaxx, lmaxy, cmaxx, cmaxy. */

  lmaxx = vid_stream->mb_width*16-1;
  lmaxy = vid_stream->mb_height*16-1;
  cmaxx = vid_stream->mb_width*8-1;
  cmaxy = vid_stream->mb_height*8-1;

  /*
   * Initialize ring buffer of pict images now that dimensions of image space
   * are known.
   */

  if (vid_stream->ring[0] == NULL) {
    for (i = 0; i < RING_BUF_SIZE; i++) {
      vid_stream->ring[i] = NewPictImage(vid_stream->mb_width * 16,
					 vid_stream->mb_height * 16);
    }
  }

  /* Parse of aspect ratio code. */

  get_bits4(data);
  vid_stream->aspect_ratio = (unsigned char) data;
  
  /* Parse off picture rate code. */
  
  get_bits4(data);
  vid_stream->picture_rate = (unsigned char) data;
  /* Get frames per sec - round up to an integer */
  if (!vid_stream->firstSeqHead) {
    vid_stream->frames_per_sec = 
      (int) (0.5+ VidRateNum[(int)vid_stream->picture_rate]);
    sprintf( command, "set FramesPerSec %d\n", vid_stream->frames_per_sec);
    Tcl_Eval( interp, command );
    sprintf( command, "ScaleHandler %d\n", vid_stream->frames_per_sec);
    Tcl_Eval( interp, command );
  }

  /* Parse off bit rate. */
  
  get_bits18(data);
  vid_stream->bit_rate = data;
  
  if (!vid_stream->firstSeqHead) {
    sprintf( command, "set OptimalBitRate %d\n", (vid_stream->bit_rate*400) );
    Tcl_Eval( interp, command );
    Tcl_Eval( interp, "setUpGauge\n" );
  }

  /* Flush marker bit. */
  
  flush_bits(1);
  
  /* Parse off vbv buffer size. */
  
  get_bits10(data);
  vid_stream->vbv_buffer_size = data;
  
  /* Parse off contrained parameter flag. */
  
  get_bits1(data);
  if (data) {
    vid_stream->const_param_flag = TRUE;
  } else
    vid_stream->const_param_flag = FALSE;

  /*
   * If intra_quant_matrix_flag set, parse off intra quant matrix values.
   */

  get_bits1(data);
  if (data) {
    for (i = 0; i < 64; i++) {
      get_bits8(data);

      vid_stream->intra_quant_matrix[zigzag[i][1]][zigzag[i][0]] =
	(unsigned char) data;
    }
  }
  /*
   * If non intra quant matrix flag set, parse off non intra quant matrix
   * values.
   */

  get_bits1(data);
  if (data) {
    for (i = 0; i < 64; i++) {
      get_bits8(data);

      vid_stream->non_intra_quant_matrix[zigzag[i][1]][zigzag[i][0]] =
	(unsigned char) data;
    }
  }
  /* Go to next start code. */

  next_start_code();

  /*
   * If next start code is extension start code, parse off extension data.
   */

  if (next_bits(32, EXT_START_CODE)) {
    flush_bits32;
    if (vid_stream->ext_data != NULL) {
      free(vid_stream->ext_data);
      vid_stream->ext_data = NULL;
    }
    vid_stream->ext_data = get_ext_data();
  }
  /* If next start code is user start code, parse off user data. */

  if (next_bits(32, USER_START_CODE)) {
    flush_bits32;
    if (vid_stream->user_data != NULL) {
      free(vid_stream->user_data);
      vid_stream->user_data = NULL;
    }
    vid_stream->user_data = get_ext_data();
  }
  vid_stream->firstSeqHead = 1;
  return PARSE_OK;
}



/*
 *--------------------------------------------------------------
 *
 * ParseGOP --
 *
 *      Parses of group of pictures header from bit stream
 *      associated with vid_stream.
 *
 * Results:
 *      Values in gop header placed into video stream structure.
 *
 * Side effects:
 *      Bit stream irreversibly parsed.
 *
 *--------------------------------------------------------------
 */

static int
ParseGOP(vid_stream)
  VidStream *vid_stream;
{
  unsigned int data;


  /* Flush group of pictures start code. WWWWWWOOOOOOOSSSSSSHHHHH!!! */

  flush_bits32;

  /* Parse off drop frame flag. */

  get_bits1(data);
  if (data) {
    vid_stream->group.drop_flag = TRUE;
  } else
    vid_stream->group.drop_flag = FALSE;

  /* Parse off hour component of time code. */

  get_bits5(data);
  vid_stream->group.tc_hours = data;

  /* Parse off minute component of time code. */

  get_bits6(data);
  vid_stream->group.tc_minutes = data;

  /* Flush marker bit. */

  flush_bits(1);

  /* Parse off second component of time code. */

  get_bits6(data);
  vid_stream->group.tc_seconds = data;

  /* Parse off picture count component of time code. */

  get_bits6(data);
  vid_stream->group.tc_pictures = data;

  /* Parse off closed gop and broken link flags. */

  get_bits2(data);
  if (data > 1) {
    vid_stream->group.closed_gop = TRUE;
    if (data > 2) {
      vid_stream->group.broken_link = TRUE;
    } else
      vid_stream->group.broken_link = FALSE;
  } else {
    vid_stream->group.closed_gop = FALSE;
    if (data) {
      vid_stream->group.broken_link = TRUE;
    } else
      vid_stream->group.broken_link = FALSE;
  }

  /* Goto next start code. */

  next_start_code();

  /* If next start code is extension data, parse off extension data. */

  if (next_bits(32, EXT_START_CODE)) {
    flush_bits32;
    if (vid_stream->group.ext_data != NULL) {
      free(vid_stream->group.ext_data);
      vid_stream->group.ext_data = NULL;
    }
    vid_stream->group.ext_data = get_ext_data();
  }
  /* If next start code is user data, parse off user data. */

  if (next_bits(32, USER_START_CODE)) {
    flush_bits32;
    if (vid_stream->group.user_data != NULL) {
      free(vid_stream->group.user_data);
      vid_stream->group.user_data = NULL;
    }
    vid_stream->group.user_data = get_ext_data();
  }
  return PARSE_OK;
}



/*
 *--------------------------------------------------------------
 *
 * ParsePicture --
 *
 *      Parses picture header. Marks picture to be presented
 *      at particular time given a time stamp.
 *
 * Results:
 *      Values from picture header put into video stream structure.
 *
 * Side effects:
 *      Bit stream irreversibly parsed.
 *
 *--------------------------------------------------------------
 */

static int
ParsePicture(vid_stream, time_stamp)
     VidStream *vid_stream;
     TimeStamp time_stamp;
{
  static int lastLastSkip = SKIP_NONE;	/* ignore B-frames */
  static int lastSkip = SKIP_NONE;
  static int numB = 0;
  static int numP = 0;
  static int numI = 0;
  register int codeType;
  unsigned int data;
  int i;
  

  /* Flush header start code. */
  flush_bits32;

  /* Parse off temporal reference. */
  get_bits10(data);
  vid_stream->picture.temp_ref = data;

  /* Parse of picture type. */
  get_bits3(data);
  vid_stream->picture.code_type = data;
  
  codeType = data;
  
  if ( codeType != B_TYPE )
    lastLastSkip = lastSkip;
  
  /* check for forced skipping of B-frame */
  if ( codeType == B_TYPE )
    {
      if ( (lastSkip == SKIP_REF) || (lastLastSkip == SKIP_REF) )
	{
	  return SKIP_PICTURE;
	}
    }
  /* check for forced skipping of P-frame */
  else if ( codeType == P_TYPE )
    {
      if ( lastSkip == SKIP_REF )
	{
	  lastSkip = SKIP_REF;
	  return SKIP_PICTURE;
	}
    }
  
  if ( totNumFrames == 0 )
    {
      numI = 0;	numP = 0; numB = 0;
    }
  
  switch(codeType)
    {
    case I_TYPE:	numI++; break;
    case P_TYPE:	numP++; break;
    case B_TYPE:	numB++; break;
    }
  
  if ((codeType == B_TYPE) &&
      (No_B_Flag ||
       (vid_stream->past == NULL) ||
       (vid_stream->future == NULL)))
    {
#ifdef BLEAH
      fprintf(stdout, "No_B_Flag = %d\n", No_B_Flag);
      fprintf(stdout, "vid_stream->past = 0x%x\n", vid_stream->past);
      fprintf(stdout, "vid_stream->future = 0x%x\n", vid_stream->future);
#endif
      
      fprintf(stdout, "SKIP PICTURE 1\n");
      return SKIP_PICTURE;
    }
  
  if ((codeType == P_TYPE) &&
      (No_P_Flag || (vid_stream->future == NULL)))
    {
      fprintf(stdout, "SKIP PICTURE 2\n");
      
      lastSkip = SKIP_REF;
      
      return SKIP_PICTURE;
    }
  
  if ( codeType != B_TYPE )
    lastSkip = SKIP_NONE;
  
  StartTime();
  stat_a[0].frametype = codeType;
  stat_a[0].number = 1;
  stat_a[0].totsize = 45;
  pictureSizeCount = bitCountRead();
  
  /* Parse off vbv buffer delay value. */
  get_bits16(data);
  vid_stream->picture.vbv_delay = data;
  
  /* If P or B type frame... */
  
  if ((vid_stream->picture.code_type == 2) || (vid_stream->picture.code_type == 3)) {
    
    /* Parse off forward vector full pixel flag. */
    get_bits1(data);
    if (data)
      vid_stream->picture.full_pel_forw_vector = TRUE;
    else
      vid_stream->picture.full_pel_forw_vector = FALSE;
    
    /* Parse of forw_r_code. */
    get_bits3(data);
    
    /* Decode forw_r_code into forw_r_size and forw_f. */
    
    vid_stream->picture.forw_r_size = data - 1;
    vid_stream->picture.forw_f = (1 << vid_stream->picture.forw_r_size);
  }
  /* If B type frame... */
  
  if (vid_stream->picture.code_type == 3) {
    
    /* Parse off back vector full pixel flag. */
    get_bits1(data);
    if (data)
      vid_stream->picture.full_pel_back_vector = TRUE;
    else
      vid_stream->picture.full_pel_back_vector = FALSE;
    
    /* Parse off back_r_code. */
    get_bits3(data);
    
    /* Decode back_r_code into back_r_size and back_f. */
    
    vid_stream->picture.back_r_size = data - 1;
    vid_stream->picture.back_f = (1 << vid_stream->picture.back_r_size);
  }
  /* Get extra bit picture info. */
  
  if (vid_stream->picture.extra_info != NULL) {
    free(vid_stream->picture.extra_info);
    vid_stream->picture.extra_info = NULL;
  }
  vid_stream->picture.extra_info = get_extra_bit_info();
  
  /* Goto next start code. */
  next_start_code();
  
  /* If start code is extension start code, parse off extension data. */
  
  if (next_bits(32, EXT_START_CODE)) {
    flush_bits32;
    
    if (vid_stream->picture.ext_data != NULL) {
      free(vid_stream->picture.ext_data);
      vid_stream->picture.ext_data = NULL;
    }
    vid_stream->picture.ext_data = get_ext_data();
  }
  /* If start code is user start code, parse off user data. */
  
  if (next_bits(32, USER_START_CODE)) {
    flush_bits32;
    
    if (vid_stream->picture.user_data != NULL) {
      free(vid_stream->picture.user_data);
      vid_stream->picture.user_data = NULL;
    }
    vid_stream->picture.user_data = get_ext_data();
  }
  /* Find a pict image structure in ring buffer not currently locked. */
  
  i = 0;
  
  while (vid_stream->ring[i]->locked != 0) {
    if (++i >= RING_BUF_SIZE) {
      fprintf( stderr, "Fatal error. Ring buffer full.");
      exit(1);
    }
  }
  
  /* Set current pict image structure to the one just found in ring. */
  
  vid_stream->current = vid_stream->ring[i];
  
  /* Set time stamp. */
  
  vid_stream->current->show_time = time_stamp;
  
  /* Reset past macroblock address field. */
  
  vid_stream->mblock.past_mb_addr = -1;
  
  return PARSE_OK;
}



/*
 *--------------------------------------------------------------
 *
 * ParseSlice --
 *
 *      Parses off slice header.
 *
 * Results:
 *      Values found in slice header put into video stream structure.
 *
 * Side effects:
 *      Bit stream irreversibly parsed.
 *
 *--------------------------------------------------------------
 */

static int
ParseSlice(vid_stream)
  VidStream *vid_stream;
{
  unsigned int data;

  /* Flush slice start code. */

  flush_bits(24);

  /* Parse off slice vertical position. */

  get_bits8(data);
  vid_stream->slice.vert_pos = data;

  /* Parse off quantization scale. */

  get_bits5(data);
  vid_stream->slice.quant_scale = data;

  /* Parse off extra bit slice info. */

  if (vid_stream->slice.extra_info != NULL) {
    free(vid_stream->slice.extra_info);
    vid_stream->slice.extra_info = NULL;
  }
  vid_stream->slice.extra_info = get_extra_bit_info();

  /* Reset past intrablock address. */

  vid_stream->mblock.past_intra_addr = -2;

  /* Reset previous recon motion vectors. */

  vid_stream->mblock.recon_right_for_prev = 0;
  vid_stream->mblock.recon_down_for_prev = 0;
  vid_stream->mblock.recon_right_back_prev = 0;
  vid_stream->mblock.recon_down_back_prev = 0;

  /* Reset macroblock address. */

  vid_stream->mblock.mb_address = ((vid_stream->slice.vert_pos - 1) *
				   vid_stream->mb_width) - 1;

  /* Reset past dct dc y, cr, and cb values. */

  vid_stream->block.dct_dc_y_past = 1024;
  vid_stream->block.dct_dc_cr_past = 1024;
  vid_stream->block.dct_dc_cb_past = 1024;

  return PARSE_OK;
}



/*
 *--------------------------------------------------------------
 *
 * ParseMacroBlock --
 *
 *      Parseoff macroblock. Reconstructs DCT values. Applies
 *      inverse DCT, reconstructs motion vectors, calculates and
 *      set pixel values for macroblock in current pict image
 *      structure.
 *
 * Results:
 *      Here's where everything really happens. Welcome to the
 *      heart of darkness.
 *
 * Side effects:
 *      Bit stream irreversibly parsed off.
 *
 *--------------------------------------------------------------
 */

static int
ParseMacroBlock(vid_stream)
  VidStream *vid_stream;
{
  int addr_incr;
  unsigned int data;
  int mask, i, recon_right_for, recon_down_for, recon_right_back,
      recon_down_back;
  int zero_block_flag;
  BOOLEAN mb_quant, mb_motion_forw, mb_motion_back, mb_pattern;
  int no_dith_flag = 0;
#ifndef NO_CHANGE
  char command[256];
  int rightForw, downForw, rightBack, downBack;
  int currMB, num_h_mb, num_v_mb;
  extern Tcl_Interp *interp;
  extern VidStream *theStream;

/* start dsb code */
  int thisMBSize;
/* end dsb code */

#endif

  mbSizeCount = bitCountRead();

  /*
   * Parse off macroblock address increment and add to macroblock address.
   */
  do {
    DecodeMBAddrInc(addr_incr);
    if (addr_incr == MB_ESCAPE) {
      vid_stream->mblock.mb_address += 33;
      addr_incr = MB_STUFFING;
    }
  } while (addr_incr == MB_STUFFING);
  vid_stream->mblock.mb_address += addr_incr;
  
  if (vid_stream->mblock.mb_address > (vid_stream->mb_height *
				       vid_stream->mb_width - 1)) {
    return SKIP_TO_START_CODE;
  }

#ifndef NO_CHANGE
  currMB = vid_stream->mblock.mb_address;
  num_h_mb = theStream->h_size / 16;
  if ((theStream->h_size % 16) != 0)
    num_h_mb++;
  num_v_mb = theStream->v_size / 16;
  if ((theStream->v_size % 16) != 0) 
    num_v_mb++;
#endif  

  /*
   * If macroblocks have been skipped, process skipped macroblocks.
   */

  if (vid_stream->mblock.mb_address - vid_stream->mblock.past_mb_addr > 1) {
    if (vid_stream->picture.code_type == P_TYPE) 
      ProcessSkippedPFrameMBlocks(vid_stream);
    else if (vid_stream->picture.code_type == B_TYPE) 
      ProcessSkippedBFrameMBlocks(vid_stream);
  }

  /* Set past macroblock address to current macroblock address. */
  vid_stream->mblock.past_mb_addr = vid_stream->mblock.mb_address;

  /* Based on picture type decode macroblock type. */
  switch (vid_stream->picture.code_type) {
  case I_TYPE:
    DecodeMBTypeI(mb_quant, mb_motion_forw, mb_motion_back, mb_pattern,
		  vid_stream->mblock.mb_intra);
    break;

  case P_TYPE:
    DecodeMBTypeP(mb_quant, mb_motion_forw, mb_motion_back, mb_pattern,
		  vid_stream->mblock.mb_intra);
    break;

  case B_TYPE:
    DecodeMBTypeB(mb_quant, mb_motion_forw, mb_motion_back, mb_pattern,
		  vid_stream->mblock.mb_intra);
    break;
  }

  /* If quantization flag set, parse off new quantization scale. */

  if (mb_quant == TRUE) {
    get_bits5(data);
    vid_stream->slice.quant_scale = data;
  }

  /* If forward motion vectors exist... */
  if (mb_motion_forw == TRUE) {
    /* Parse off and decode horizontal forward motion vector. */
    DecodeMotionVectors(vid_stream->mblock.motion_h_forw_code);

    /* If horiz. forward r data exists, parse off. */

    if ((vid_stream->picture.forw_f != 1) &&
	(vid_stream->mblock.motion_h_forw_code != 0)) {

      get_bitsn(vid_stream->picture.forw_r_size, data);
      vid_stream->mblock.motion_h_forw_r = data;

    }

    /* Parse off and decode vertical forward motion vector. */
    DecodeMotionVectors(vid_stream->mblock.motion_v_forw_code);

    /* If vert. forw. r data exists, parse off. */

    if ((vid_stream->picture.forw_f != 1) &&
	(vid_stream->mblock.motion_v_forw_code != 0)) {
      get_bitsn(vid_stream->picture.forw_r_size, data);
      vid_stream->mblock.motion_v_forw_r = data;
    }
  }

  /* If back motion vectors exist... */
  if (mb_motion_back == TRUE) {
    /* Parse off and decode horiz. back motion vector. */
    DecodeMotionVectors(vid_stream->mblock.motion_h_back_code);

    /* If horiz. back r data exists, parse off. */

    if ((vid_stream->picture.back_f != 1) &&
	(vid_stream->mblock.motion_h_back_code != 0)) {
      get_bitsn(vid_stream->picture.back_r_size, data);
      vid_stream->mblock.motion_h_back_r = data;
    }
    /* Parse off and decode vert. back motion vector. */
    DecodeMotionVectors(vid_stream->mblock.motion_v_back_code);

    /* If vert. back r data exists, parse off. */

    if ((vid_stream->picture.back_f != 1) &&
	(vid_stream->mblock.motion_v_back_code != 0)) {
      get_bitsn(vid_stream->picture.back_r_size, data);
      vid_stream->mblock.motion_v_back_r = data;
    }
  }

  if (vid_stream->mblock.mb_intra) {
    stat_a[0].i_mbnum++;
    mbCBPPtr = stat_a[0].i_mbcbp;
    mbCoeffPtr = stat_a[0].i_mbcoeff;
    mbSizePtr = &(stat_a[0].i_mbsize);
  } else if (mb_motion_back && mb_motion_forw) {
    stat_a[0].bi_mbnum++;
    mbCBPPtr = stat_a[0].bi_mbcbp;
    mbCoeffPtr = stat_a[0].bi_mbcoeff;
    mbSizePtr = &(stat_a[0].bi_mbsize);
  } else if (mb_motion_back) {
    stat_a[0].b_mbnum++;
    mbCBPPtr = stat_a[0].b_mbcbp;
    mbCoeffPtr = stat_a[0].b_mbcoeff;
    mbSizePtr = &(stat_a[0].b_mbsize);
  } else {
    stat_a[0].p_mbnum++;
    mbCBPPtr = stat_a[0].p_mbcbp;
    mbCoeffPtr = stat_a[0].p_mbcoeff;
    mbSizePtr = &(stat_a[0].p_mbsize);
  }

  /* If mblock pattern flag set, parse and decode CBP (code block pattern). */
  if (mb_pattern == TRUE) {
    DecodeCBP(vid_stream->mblock.cbp);
  }
  /* Otherwise, set CBP to zero. */
  else
    vid_stream->mblock.cbp = 0;

  mbCBPPtr[vid_stream->mblock.cbp]++;

  /* Reconstruct motion vectors depending on picture (frame) type. */
  if (vid_stream->picture.code_type == P_TYPE) {
    /*
     * If no forw motion vectors, reset previous and current vectors to 0.
     */

    if (!mb_motion_forw) {
      recon_right_for = 0;
      recon_down_for = 0;
      vid_stream->mblock.recon_right_for_prev = 0;
      vid_stream->mblock.recon_down_for_prev = 0;
    }
    /*
     * Otherwise, compute new forw motion vectors. Reset previous vectors to
     * current vectors.
     */
    else {
      ComputeForwVector(&recon_right_for, &recon_down_for);
    }
  }

  if (vid_stream->picture.code_type == B_TYPE) {
    /* Reset prev. and current vectors to zero if mblock is intracoded. */

    if (vid_stream->mblock.mb_intra) {
      vid_stream->mblock.recon_right_for_prev = 0;
      vid_stream->mblock.recon_down_for_prev = 0;
      vid_stream->mblock.recon_right_back_prev = 0;
      vid_stream->mblock.recon_down_back_prev = 0;
    } else {
      /* If no forw vectors, current vectors equal prev. vectors. */

      if (!mb_motion_forw) {
	recon_right_for = vid_stream->mblock.recon_right_for_prev;
	recon_down_for = vid_stream->mblock.recon_down_for_prev;
      }
      /*
       * Otherwise compute forw. vectors. Reset prev vectors to new values.
       */

      else {
        ComputeForwVector(&recon_right_for, &recon_down_for);
      }

      /* If no back vectors, set back vectors to prev back vectors. */

      if (!mb_motion_back) {
	recon_right_back = vid_stream->mblock.recon_right_back_prev;
	recon_down_back = vid_stream->mblock.recon_down_back_prev;
      }
      /* Otherwise compute new vectors and reset prev. back vectors. */

      else {
	ComputeBackVector(&recon_right_back, &recon_down_back);
      }

      /*
       * Store vector existance flags in structure for possible skipped
       * macroblocks to follow.
       */

      vid_stream->mblock.bpict_past_forw = mb_motion_forw;
      vid_stream->mblock.bpict_past_back = mb_motion_back;
    }
  }

#ifndef NO_CHANGE 
#endif

  /* For each possible block in macroblock. */
  if (ditherType == GRAY_DITHER ||
      ditherType == MONO_DITHER ||
      ditherType == MONO_FS4_DITHER ||
      ditherType == HALFTONE_DITHER ||
      ditherType == MONO_THRESHOLD) {
    for (mask = 32, i = 0; i < 4; mask >>= 1, i++) {

      /* If block exists... */
      if ((vid_stream->mblock.mb_intra) || (vid_stream->mblock.cbp & mask)) {
	zero_block_flag = 0;
	ParseReconBlock(i);
      } else {
	zero_block_flag = 1;
      }

      /* If macroblock is intra coded... */
      if (vid_stream->mblock.mb_intra) {
	ReconIMBlock(vid_stream, i);
      } else if (mb_motion_forw && mb_motion_back) {
	ReconBiMBlock(vid_stream, i, recon_right_for, recon_down_for,
		      recon_right_back, recon_down_back, zero_block_flag);
      } else if (mb_motion_forw || (vid_stream->picture.code_type == P_TYPE)) {
	ReconPMBlock(vid_stream, i, recon_right_for, recon_down_for,
		     zero_block_flag);
      } else if (mb_motion_back) {
	ReconBMBlock(vid_stream, i, recon_right_back, recon_down_back,
		     zero_block_flag);
      }
    }
    /* Kill the Chrominace blocks... */
    if ((vid_stream->mblock.mb_intra) || (vid_stream->mblock.cbp & 0x2)) {
        ParseAwayBlock(4);
    }
    if ((vid_stream->mblock.mb_intra) || (vid_stream->mblock.cbp & 0x1)) {
        ParseAwayBlock(5);
    }
  } else {
    if ((ditherType == MBORDERED_DITHER) &&
	(vid_stream->mblock.cbp == 0) &&
	(vid_stream->picture.code_type == 3) &&
	(!vid_stream->mblock.mb_intra) &&
	(!(mb_motion_forw && mb_motion_back))) {
      MBOrderedDitherDisplayCopy(vid_stream, vid_stream->mblock.mb_address, 
		     mb_motion_forw, recon_right_for, recon_down_for, 
		     mb_motion_back, recon_right_back, recon_down_back,
		     vid_stream->past->display, vid_stream->future->display);
      ditherFlags[vid_stream->mblock.mb_address] = 0;
      no_dith_flag = 1;
    }
    else {
      for (mask = 32, i = 0; i < 6; mask >>= 1, i++) {
	
	/* If block exists... */
	if ((vid_stream->mblock.mb_intra) || (vid_stream->mblock.cbp & mask)) {
	  zero_block_flag = 0;
	  ParseReconBlock(i);
	} else {
	  zero_block_flag = 1;
	}
	
	/* If macroblock is intra coded... */
	if (vid_stream->mblock.mb_intra) {
	  ReconIMBlock(vid_stream, i);
	} else if (mb_motion_forw && mb_motion_back) {
	  ReconBiMBlock(vid_stream, i, recon_right_for, recon_down_for,
			recon_right_back, recon_down_back, zero_block_flag);
	} else if (mb_motion_forw || (vid_stream->picture.code_type == P_TYPE)) {
	  ReconPMBlock(vid_stream, i, recon_right_for, recon_down_for,
		       zero_block_flag);
	} else if (mb_motion_back) {
	  ReconBMBlock(vid_stream, i, recon_right_back, recon_down_back,
		       zero_block_flag);
	}
      }
    }
  }

  if ((ditherType == MBORDERED_DITHER) && (!no_dith_flag)) {
    if ((vid_stream->picture.code_type == 2) &&
	(vid_stream->mblock.cbp == 0) &&
	(!vid_stream->mblock.mb_intra)) {
      MBOrderedDitherDisplayCopy(vid_stream, vid_stream->mblock.mb_address,
				 1, recon_right_for, recon_down_for,
				 0, 0, 0,
				 vid_stream->future->display,
				 (unsigned char *) NULL);
      ditherFlags[vid_stream->mblock.mb_address] = 0;
    }
    else {
      ditherFlags[vid_stream->mblock.mb_address] = 1;
    }
  }

  /* If D Type picture, flush marker bit. */
  if (vid_stream->picture.code_type == 4)
    flush_bits(1);

  /* If macroblock was intracoded, set macroblock past intra address. */
  if (vid_stream->mblock.mb_intra)
    vid_stream->mblock.past_intra_addr =
      vid_stream->mblock.mb_address;

  thisMBSize = bitCountRead() - mbSizeCount;
  *mbSizePtr += thisMBSize;
  vid_stream->currentSize += thisMBSize;
  if (thisMBSize > MAX_EXPECTED_SIZE)
    thisMBSize = MAX_EXPECTED_SIZE;
  sprintf(command, "ModBox %d %d %d\n",
	  (currMB % num_h_mb), (currMB / num_h_mb), 
	  thisMBSize);
  AddToScript( vid_stream, command );
  
  return PARSE_OK;
}



/*
 *--------------------------------------------------------------
 *
 * ReconIMBlock --
 *
 *	Reconstructs intra coded macroblock.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static void
ReconIMBlock(vid_stream, bnum)
  VidStream *vid_stream;
  int bnum;
{
  int mb_row, mb_col, row, col, row_size, rr;
  unsigned char *dest;

  /* Calculate macroblock row and column from address. */

  mb_row = vid_stream->mblock.mb_address / vid_stream->mb_width;
  mb_col = vid_stream->mblock.mb_address % vid_stream->mb_width;


  /* If block is luminance block... */

  if (bnum < 4) {

    /* Calculate row and col values for upper left pixel of block. */

    row = mb_row * 16;
    col = mb_col * 16;
    if (bnum > 1)
      row += 8;
    if (bnum % 2)
      col += 8;

    /* Set dest to luminance plane of current pict image. */

    dest = vid_stream->current->luminance;

    /* Establish row size. */

    row_size = vid_stream->mb_width * 16;
  }
  /* Otherwise if block is Cr block... */

  else if (bnum == 4) {

    /* Set dest to Cr plane of current pict image. */

    dest = vid_stream->current->Cr;

    /* Establish row size. */

    row_size = vid_stream->mb_width * 8;

    /* Calculate row,col for upper left pixel of block. */

    row = mb_row * 8;
    col = mb_col * 8;
  }
  /* Otherwise block is Cb block, and ... */

  else {

    /* Set dest to Cb plane of current pict image. */

    dest = vid_stream->current->Cb;

    /* Establish row size. */

    row_size = vid_stream->mb_width * 8;

    /* Calculate row,col for upper left pixel value of block. */

    row = mb_row * 8;
    col = mb_col * 8;
  }

  /*
   * For each pixel in block, set to cropped reconstructed value from inverse
   * dct.
   */
  {
    short *sp = &vid_stream->block.dct_recon[0][0];
    unsigned char *cm = cropTbl + MAX_NEG_CROP;
    dest += row * row_size + col;
    for (rr = 0; rr < 4; rr++, sp += 16, dest += row_size) {
      dest[0] = cm[sp[0]];
      assertCrop(sp[0]);
      dest[1] = cm[sp[1]];
      assertCrop(sp[1]);
      dest[2] = cm[sp[2]];
      assertCrop(sp[2]);
      dest[3] = cm[sp[3]];
      assertCrop(sp[3]);
      dest[4] = cm[sp[4]];
      assertCrop(sp[4]);
      dest[5] = cm[sp[5]];
      assertCrop(sp[5]);
      dest[6] = cm[sp[6]];
      assertCrop(sp[6]);
      dest[7] = cm[sp[7]];
      assertCrop(sp[7]);

      dest += row_size;
      dest[0] = cm[sp[8]];
      assertCrop(sp[8]);
      dest[1] = cm[sp[9]];
      assertCrop(sp[9]);
      dest[2] = cm[sp[10]];
      assertCrop(sp[10]);
      dest[3] = cm[sp[11]];
      assertCrop(sp[11]);
      dest[4] = cm[sp[12]];
      assertCrop(sp[12]);
      dest[5] = cm[sp[13]];
      assertCrop(sp[13]);
      dest[6] = cm[sp[14]];
      assertCrop(sp[14]);
      dest[7] = cm[sp[15]];
      assertCrop(sp[15]);
    }
  }
}



/*
 *--------------------------------------------------------------
 *
 * ReconPMBlock --
 *
 *	Reconstructs forward predicted macroblocks.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

static void
ReconPMBlock(vid_stream, bnum, recon_right_for, recon_down_for, zflag)
  VidStream *vid_stream;
  int bnum, recon_right_for, recon_down_for, zflag;
{
  int mb_row, mb_col, row, col, row_size, rr;
  unsigned char *dest, *past;
  static int right_for, down_for, right_half_for, down_half_for;
  unsigned char *rindex1, *rindex2;
  unsigned char *index;
  short int *blockvals;

#ifndef LOOSE_MPEG
  int maxx, maxy;
  int illegalBlock = 0;
  int row_start, row_end, rfirst, rlast, col_start, col_end, cfirst, clast;
#endif

  /* Calculate macroblock row and column from address. */

  mb_row = vid_stream->mblock.mb_address / vid_stream->mb_width;
  mb_col = vid_stream->mblock.mb_address % vid_stream->mb_width;

  if (bnum < 4) {
int value;

    /* Calculate right_for, down_for motion vectors. */

    right_for = recon_right_for >> 1;
    down_for = recon_down_for >> 1;
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;

    /* Set dest to luminance plane of current pict image. */

    dest = vid_stream->current->luminance;

    if (vid_stream->picture.code_type == B_TYPE) {
      if (vid_stream->past != NULL)
	past = vid_stream->past->luminance;
    } else {

      /* Set predicitive frame to current future frame. */

      if (vid_stream->future != NULL)
	past = vid_stream->future->luminance;
    }

    /* Establish row size. */

    row_size = vid_stream->mb_width << 4;

    /* Calculate row,col of upper left pixel in block. */

    row = mb_row << 4;
    col = mb_col << 4;
    if (bnum > 1)
      row += 8;
    if (bnum % 2)
      col += 8;

#ifndef LOOSE_MPEG
    /* Check for block illegality. */

    maxx = lmaxx; maxy = lmaxy;

    if (row + down_for + 7 > maxy) illegalBlock |= 0x4;
    else if (row + down_for < 0)  illegalBlock |= 0x1;
    
    if (col + right_for + 7 > maxx) illegalBlock |= 0x2;
    else if (col + right_for < 0) illegalBlock |= 0x8;

if ( illegalBlock != 0 )   
{
    fprintf(stdout, "Illegal block encountered!!! row %d, down_for %d, col %d, right_for %d\n",
	    row, down_for, col, right_for);
    fprintf(stdout, "maxx = %d, maxy = %d\n", maxx, maxy);
}
#endif
  }
  /* Otherwise, block is NOT luminance block, ... */

  else {

    /* Construct motion vectors. */

    recon_right_for /= 2;
    recon_down_for /= 2;
    right_for = recon_right_for >> 1;
    down_for = recon_down_for >> 1;
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;

    /* Establish row size. */

    row_size = vid_stream->mb_width << 3;

    /* Calculate row,col of upper left pixel in block. */

    row = mb_row << 3;
    col = mb_col << 3;

#ifndef LOOSE_MPEG
    /* Check for block illegality. */

    maxx = cmaxx; maxy = cmaxy;

    if (row + down_for  + 7 > maxy) illegalBlock |= 0x4;
    else if (row + down_for < 0) illegalBlock |= 0x1;

    if (col + right_for  + 7 > maxx) illegalBlock  |= 0x2;
    else if (col + right_for < 0) illegalBlock |= 0x8;

if ( illegalBlock != 0 )   
    fprintf(stdout, "Illegal block encountered!!! row %d, down_for %d, col %d, right_for %d\n",
	    row, down_for, col, right_for);
#endif

    /* If block is Cr block... */

    if (bnum == 4) {

      /* Set dest to Cr plane of current pict image. */

      dest = vid_stream->current->Cr;

      if (vid_stream->picture.code_type == B_TYPE) {

	if (vid_stream->past != NULL)
	  past = vid_stream->past->Cr;
      } else {
	if (vid_stream->future != NULL)
	  past = vid_stream->future->Cr;
      }
    }
    /* Otherwise, block is Cb block... */

    else {

      /* Set dest to Cb plane of current pict image. */

      dest = vid_stream->current->Cb;

      if (vid_stream->picture.code_type == B_TYPE) {
	if (vid_stream->past != NULL)
	  past = vid_stream->past->Cb;
      } else {
	if (vid_stream->future != NULL)
	  past = vid_stream->future->Cb;
      }
    }
  }

  /* For each pixel in block... */

#ifdef LOOSE_MPEG

  if (illegalBlock) {
    if (illegalBlock & 0x1) {
      row_start = 0;
      row_end = row+down_for+8;
      rfirst = rlast = 8 - row_end;
    }
    else if (illegalBlock & 0x4) {
      row_start = row + down_for;
      row_end = maxy+1;
      rlast = row_end - row_start - 1;
      rfirst = 0;
    }
    else {
      row_start = row+down_for;
      row_end = row_start+8;
      rfirst = 0;
    }

    if (illegalBlock & 0x8) {
      col_start = 0;
      col_end = col + right_for + 8;
      cfirst = clast = 8 - col_end;
    }
    else if (illegalBlock & 0x2) {
      col_start = col + right_for;
      col_end = maxx + 1;
      clast = col_end - col_start - 1;
      cfirst = 0;
    }
    else {
      col_start = col + right_for;
      col_end = col_start + 8;
      cfirst = 0;
    }

    for (rr = row_start; rr < row_end; rr++) {
      rindex1 = past + (rr * row_size) + col_start;
      index = dest + ((row + rfirst) * row_size) + col + cfirst;
      for (cc = col_start; cc < col_end; cc++) {
	*index++ = *rindex1++;
      }
    }

    if (illegalBlock & 0x1) {
      for (rr = rlast -1; rr >=0; rr--) {
	index = dest + ((row + rr) * row_size) + col;
	rindex1 = dest + ((row + rlast) * row_size) + col;
	for (cc = 0; cc < 8; cc ++) {
	  *index++ = *rindex1++;
	}
      }
    }
    else if (illegalBlock & 0x4) {
      for (rr = rlast+1; rr < 8; rr++) {
	index = dest + ((row + rr) * row_size) + col;
	rindex1 = dest + ((row + rlast) * row_size) + col;
	for (cc = 0; cc < 8; cc ++) {
	  *index++ = *rindex1++;
	}
      }
    }

    if (illegalBlock & 0x2) {
      for (cc = clast+1; cc < 8; cc++) {
	index = dest + (row * row_size) + (col + cc);
	rindex1 = dest + (row * row_size) + (col + clast);
	for (rr = 0; rr < 8; rr++) {
	  *index = *rindex1;
	  index += row_size;
	  rindex1 += row_size;
	}
      }
    }
    else if (illegalBlock & 0x8) {
      for (cc = clast-1; cc >= 0; cc--) {
	index = dest + (row * row_size) + (col + cc);
	rindex1 = dest + (row * row_size) + (col + clast);
	for (rr = 0; rr < 8; rr++) {
	  *index = *rindex1;
	  index += row_size;
	  rindex1 += row_size;
	}
      }
    }

    if (!zflag) {
      for (rr = 0; rr < 8; rr++) {
	index = dest + (row*row_size) + col;
	blockvals = &(vid_stream->block.dct_recon[rr][0]);
	index[0] += blockvals[0];
	index[1] += blockvals[1];
	index[2] += blockvals[2];
	index[3] += blockvals[3];
	index[4] += blockvals[4];
	index[5] += blockvals[5];
	index[6] += blockvals[6];
	index[7] += blockvals[7];
      }
    }
  }
  else {

#endif

    index = dest + (row * row_size) + col;
    rindex1 = past + (row + down_for) * row_size + col + right_for;
    
    blockvals = &(vid_stream->block.dct_recon[0][0]);
    
    /*
     * Calculate predictive pixel value based on motion vectors and copy to
     * dest plane.
     */
    
    if ((!down_half_for) && (!right_half_for)) {
      unsigned char *cm = cropTbl + MAX_NEG_CROP;
      if (!zflag)
	for (rr = 0; rr < 4; rr++) {
	  index[0] = cm[(int) rindex1[0] + (int) blockvals[0]];
	  index[1] = cm[(int) rindex1[1] + (int) blockvals[1]];
	  index[2] = cm[(int) rindex1[2] + (int) blockvals[2]];
	  index[3] = cm[(int) rindex1[3] + (int) blockvals[3]];
	  index[4] = cm[(int) rindex1[4] + (int) blockvals[4]];
	  index[5] = cm[(int) rindex1[5] + (int) blockvals[5]];
	  index[6] = cm[(int) rindex1[6] + (int) blockvals[6]];
	  index[7] = cm[(int) rindex1[7] + (int) blockvals[7]];
	  index += row_size;
	  rindex1 += row_size;
	  
	  index[0] = cm[(int) rindex1[0] + (int) blockvals[8]];
	  index[1] = cm[(int) rindex1[1] + (int) blockvals[9]];
	  index[2] = cm[(int) rindex1[2] + (int) blockvals[10]];
	  index[3] = cm[(int) rindex1[3] + (int) blockvals[11]];
	  index[4] = cm[(int) rindex1[4] + (int) blockvals[12]];
	  index[5] = cm[(int) rindex1[5] + (int) blockvals[13]];
	  index[6] = cm[(int) rindex1[6] + (int) blockvals[14]];
	  index[7] = cm[(int) rindex1[7] + (int) blockvals[15]];
	  blockvals += 16;
	  index += row_size;
	  rindex1 += row_size;
	}
      else {
	if (right_for & 0x1) {
	  /* No alignment, use bye copy */
	  for (rr = 0; rr < 4; rr++) {
	    index[0] = rindex1[0];
	    index[1] = rindex1[1];
	    index[2] = rindex1[2];
	    index[3] = rindex1[3];
	    index[4] = rindex1[4];
	    index[5] = rindex1[5];
	    index[6] = rindex1[6];
	    index[7] = rindex1[7];
	    index += row_size;
	    rindex1 += row_size;
	    
	    index[0] = rindex1[0];
	    index[1] = rindex1[1];
	    index[2] = rindex1[2];
	    index[3] = rindex1[3];
	    index[4] = rindex1[4];
	    index[5] = rindex1[5];
	    index[6] = rindex1[6];
	    index[7] = rindex1[7];
	    index += row_size;
	    rindex1 += row_size;
	  }
	} else if (right_for & 0x2) {
	  /* Half-word bit aligned, use 16 bit copy */
	  short *src = (short *)rindex1;
	  short *dest = (short *)index;
	  row_size >>= 1;
	  for (rr = 0; rr < 4; rr++) {
	    dest[0] = src[0];
	    dest[1] = src[1];
	    dest[2] = src[2];
	    dest[3] = src[3];
	    dest += row_size;
	    src += row_size;
	    
	    dest[0] = src[0];
	    dest[1] = src[1];
	    dest[2] = src[2];
	    dest[3] = src[3];
	    dest += row_size;
	    src += row_size;
	  }
	} else {
	  /* Word aligned, use 32 bit copy */
	  int *src = (int *)rindex1;
	  int *dest = (int *)index;
	  row_size >>= 2;
	  for (rr = 0; rr < 4; rr++) {
	    dest[0] = src[0];
	    dest[1] = src[1];
	    dest += row_size;
	    src += row_size;
	    
	    dest[0] = src[0];
	    dest[1] = src[1];
	    dest += row_size;
	    src += row_size;
	  }
	}
      }
    } else {
      unsigned char *cm = cropTbl + MAX_NEG_CROP;
      rindex2 = rindex1 + right_half_for + (down_half_for * row_size);
      if (!zflag)
	for (rr = 0; rr < 4; rr++) {
	  index[0] = cm[((int) (rindex1[0] + rindex2[0]) >> 1) + blockvals[0]];

assert(index[0] <= 2047);
	  index[1] = cm[((int) (rindex1[1] + rindex2[1]) >> 1) + blockvals[1]];
	  index[2] = cm[((int) (rindex1[2] + rindex2[2]) >> 1) + blockvals[2]];
	  index[3] = cm[((int) (rindex1[3] + rindex2[3]) >> 1) + blockvals[3]];
	  index[4] = cm[((int) (rindex1[4] + rindex2[4]) >> 1) + blockvals[4]];
	  index[5] = cm[((int) (rindex1[5] + rindex2[5]) >> 1) + blockvals[5]];
	  index[6] = cm[((int) (rindex1[6] + rindex2[6]) >> 1) + blockvals[6]];
	  index[7] = cm[((int) (rindex1[7] + rindex2[7]) >> 1) + blockvals[7]];
	  index += row_size;
	  rindex1 += row_size;
	  rindex2 += row_size;
	  
	  index[0] = cm[((int) (rindex1[0] + rindex2[0]) >> 1) + blockvals[8]];
assert(index[0] <= 2047);
	  index[1] = cm[((int) (rindex1[1] + rindex2[1]) >> 1) + blockvals[9]];
	  index[2] = cm[((int) (rindex1[2] + rindex2[2]) >> 1) + blockvals[10]];
	  index[3] = cm[((int) (rindex1[3] + rindex2[3]) >> 1) + blockvals[11]];
	  index[4] = cm[((int) (rindex1[4] + rindex2[4]) >> 1) + blockvals[12]];
	  index[5] = cm[((int) (rindex1[5] + rindex2[5]) >> 1) + blockvals[13]];
	  index[6] = cm[((int) (rindex1[6] + rindex2[6]) >> 1) + blockvals[14]];
	  index[7] = cm[((int) (rindex1[7] + rindex2[7]) >> 1) + blockvals[15]];
	  blockvals += 16;
	  index += row_size;
	  rindex1 += row_size;
	  rindex2 += row_size;
	}
      else
	for (rr = 0; rr < 4; rr++) {
	  index[0] = (int) (rindex1[0] + rindex2[0]) >> 1;

	  index[1] = (int) (rindex1[1] + rindex2[1]) >> 1;
	  index[2] = (int) (rindex1[2] + rindex2[2]) >> 1;
	  index[3] = (int) (rindex1[3] + rindex2[3]) >> 1;
	  index[4] = (int) (rindex1[4] + rindex2[4]) >> 1;
	  index[5] = (int) (rindex1[5] + rindex2[5]) >> 1;
	  index[6] = (int) (rindex1[6] + rindex2[6]) >> 1;
	  index[7] = (int) (rindex1[7] + rindex2[7]) >> 1;
	  index += row_size;
	  rindex1 += row_size;
	  rindex2 += row_size;
	  
	  index[0] = (int) (rindex1[0] + rindex2[0]) >> 1;
	  index[1] = (int) (rindex1[1] + rindex2[1]) >> 1;
	  index[2] = (int) (rindex1[2] + rindex2[2]) >> 1;
	  index[3] = (int) (rindex1[3] + rindex2[3]) >> 1;
	  index[4] = (int) (rindex1[4] + rindex2[4]) >> 1;
	  index[5] = (int) (rindex1[5] + rindex2[5]) >> 1;
	  index[6] = (int) (rindex1[6] + rindex2[6]) >> 1;
	  index[7] = (int) (rindex1[7] + rindex2[7]) >> 1;
	  index += row_size;
	  rindex1 += row_size;
	  rindex2 += row_size;
	}
    }

#ifdef LOOSE_MPEG
  }
#endif
}


/*
 *--------------------------------------------------------------
 *
 * ReconBMBlock --
 *
 *	Reconstructs back predicted macroblocks.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

static void
ReconBMBlock(vid_stream, bnum, recon_right_back, recon_down_back, zflag)
  VidStream *vid_stream;
  int bnum, recon_right_back, recon_down_back, zflag;
{
  int mb_row, mb_col, row, col, row_size, rr;
  unsigned char *dest, *future;
  int right_back, down_back, right_half_back, down_half_back;
  unsigned char *rindex1, *rindex2;
  unsigned char *index;
  short int *blockvals;

#ifdef LOOSE_MPEG
  int illegalBlock = 0;
  int maxx, maxy;
  int row_start, row_end, rlast, rfirst, col_start, col_end, clast, cfirst;
#endif

  /* Calculate macroblock row and column from address. */

  mb_row = vid_stream->mblock.mb_address / vid_stream->mb_width;
  mb_col = vid_stream->mblock.mb_address % vid_stream->mb_width;

  /* If block is luminance block... */

  if (bnum < 4) {

    /* Calculate right_back, down_bakc motion vectors. */

    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;

    /* Set dest to luminance plane of current pict image. */

    dest = vid_stream->current->luminance;

    /*
     * If future frame exists, set future to luminance plane of future frame.
     */

    if (vid_stream->future != NULL)
      future = vid_stream->future->luminance;

    /* Establish row size. */

    row_size = vid_stream->mb_width << 4;

    /* Calculate row,col of upper left pixel in block. */

    row = mb_row << 4;
    col = mb_col << 4;
    if (bnum > 1)
      row += 8;
    if (bnum % 2)
      col += 8;

#ifdef LOOSE_MPEG

    /* Check for block illegality. */

    maxx = lmaxx; maxy = lmaxy;

    if (row + down_back + 7 > maxy) illegalBlock |= 0x4;
    else if (row + down_back < 0)  illegalBlock |= 0x1;
    
    if (col + right_back + 7 > maxx) illegalBlock |= 0x2;
    else if (col + right_back < 0) illegalBlock |= 0x8;

#endif

  }
  /* Otherwise, block is NOT luminance block, ... */

  else {

    /* Construct motion vectors. */

    recon_right_back /= 2;
    recon_down_back /= 2;
    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;

    /* Establish row size. */

    row_size = vid_stream->mb_width << 3;

    /* Calculate row,col of upper left pixel in block. */

    row = mb_row << 3;
    col = mb_col << 3;

#ifdef LOOSE_MPEG

    /* Check for block illegality. */

    maxx = cmaxx; maxy = cmaxy;

    if (row + down_back + 7 > maxy) illegalBlock |= 0x4;
    else if (row + down_back < 0) illegalBlock |= 0x1;

    if (col + right_back + 7 > maxx) illegalBlock  |= 0x2;
    else if (col + right_back < 0) illegalBlock |= 0x8;

#endif

    /* If block is Cr block... */

    if (bnum == 4) {

      /* Set dest to Cr plane of current pict image. */

      dest = vid_stream->current->Cr;

      /*
       * If future frame exists, set future to Cr plane of future image.
       */

      if (vid_stream->future != NULL)
	future = vid_stream->future->Cr;
    }
    /* Otherwise, block is Cb block... */

    else {

      /* Set dest to Cb plane of current pict image. */

      dest = vid_stream->current->Cb;

      /*
       * If future frame exists, set future to Cb plane of future frame.
       */

      if (vid_stream->future != NULL)
	future = vid_stream->future->Cb;
    }
  }

  /* For each pixel in block do... */

#ifdef LOOSE_MPEG

  if (illegalBlock) {
    if (illegalBlock & 0x1) {
      row_start = 0;
      row_end = row+down_back+8;
      rfirst = rlast = 8 - row_end;
    }
    else if (illegalBlock & 0x4) {
      row_start = row + down_back;
      row_end = maxy+1;
      rlast = row_end - row_start - 1;
      rfirst = 0;
    }
    else {
      row_start = row+down_back;
      row_end = row_start+8;
      rfirst = 0;
    }

    if (illegalBlock & 0x8) {
      col_start = 0;
      col_end = col + right_back + 8;
      cfirst = clast = 8 - col_end;
    }
    else if (illegalBlock & 0x2) {
      col_start = col + right_back;
      col_end = maxx + 1;
      clast = col_end - col_start - 1;
      cfirst = 0;
    }
    else {
      col_start = col + right_back;
      col_end = col_start + 8;
      cfirst = 0;
    }

    for (rr = row_start; rr < row_end; rr++) {
      rindex1 = future + (rr * row_size) + col_start;
      index = dest + ((row + rfirst) * row_size) + col + cfirst;
      for (cc = col_start; cc < col_end; cc++) {
	*index++ = *rindex1++;
      }
    }

    if (illegalBlock & 0x1) {
      for (rr = rlast -1; rr >=0; rr--) {
	index = dest + ((row + rr) * row_size) + col;
	rindex1 = dest + ((row + rlast) * row_size) + col;
	for (cc = 0; cc < 8; cc ++) {
	  *index++ = *rindex1++;
	}
      }
    }
    else if (illegalBlock & 0x4) {
      for (rr = rlast+1; rr < 8; rr++) {
	index = dest + ((row + rr) * row_size) + col;
	rindex1 = dest + ((row + rlast) * row_size) + col;
	for (cc = 0; cc < 8; cc ++) {
	  *index++ = *rindex1++;
	}
      }
    }

    if (illegalBlock & 0x2) {
      for (cc = clast+1; cc < 8; cc++) {
	index = dest + (row * row_size) + (col + cc);
	rindex1 = dest + (row * row_size) + (col + clast);
	for (rr = 0; rr < 8; rr++) {
	  *index = *rindex1;
	  index += row_size;
	  rindex1 += row_size;
	}
      }
    }
    else if (illegalBlock & 0x8) {
      for (cc = clast-1; cc >= 0; cc--) {
	index = dest + (row * row_size) + (col + cc);
	rindex1 = dest + (row * row_size) + (col + clast);
	for (rr = 0; rr < 8; rr++) {
	  *index = *rindex1;
	  index += row_size;
	  rindex1 += row_size;
	}
      }
    }

    if (!zflag) {
      for (rr = 0; rr < 8; rr++) {
	index = dest + (row*row_size) + col;
	blockvals = &(vid_stream->block.dct_recon[rr][0]);
	index[0] += blockvals[0];
	index[1] += blockvals[1];
	index[2] += blockvals[2];
	index[3] += blockvals[3];
	index[4] += blockvals[4];
	index[5] += blockvals[5];
	index[6] += blockvals[6];
	index[7] += blockvals[7];
      }
    }
  }
  else {

#endif
    
    index = dest + (row * row_size) + col;
    rindex1 = future + (row + down_back) * row_size + col + right_back;

    blockvals = &(vid_stream->block.dct_recon[0][0]);

    if ((!right_half_back) && (!down_half_back)) {
      unsigned char *cm = cropTbl + MAX_NEG_CROP;
      if (!zflag)
	for (rr = 0; rr < 4; rr++) {
	  index[0] = cm[(int) rindex1[0] + (int) blockvals[0]];
	  index[1] = cm[(int) rindex1[1] + (int) blockvals[1]];
	  index[2] = cm[(int) rindex1[2] + (int) blockvals[2]];
	  index[3] = cm[(int) rindex1[3] + (int) blockvals[3]];
	  index[4] = cm[(int) rindex1[4] + (int) blockvals[4]];
	  index[5] = cm[(int) rindex1[5] + (int) blockvals[5]];
	  index[6] = cm[(int) rindex1[6] + (int) blockvals[6]];
	  index[7] = cm[(int) rindex1[7] + (int) blockvals[7]];
	  index += row_size;
	  rindex1 += row_size;
	  
	  index[0] = cm[(int) rindex1[0] + (int) blockvals[8]];
	  index[1] = cm[(int) rindex1[1] + (int) blockvals[9]];
	  index[2] = cm[(int) rindex1[2] + (int) blockvals[10]];
	  index[3] = cm[(int) rindex1[3] + (int) blockvals[11]];
	  index[4] = cm[(int) rindex1[4] + (int) blockvals[12]];
	  index[5] = cm[(int) rindex1[5] + (int) blockvals[13]];
	  index[6] = cm[(int) rindex1[6] + (int) blockvals[14]];
	  index[7] = cm[(int) rindex1[7] + (int) blockvals[15]];
	  blockvals += 16;
	  index += row_size;
	  rindex1 += row_size;
	}
      else {
	if (right_back & 0x1) {
	  /* No alignment, use bye copy */
	  for (rr = 0; rr < 4; rr++) {
	    index[0] = rindex1[0];
	    index[1] = rindex1[1];
	    index[2] = rindex1[2];
	    index[3] = rindex1[3];
	    index[4] = rindex1[4];
	    index[5] = rindex1[5];
	    index[6] = rindex1[6];
	    index[7] = rindex1[7];
	    index += row_size;
	    rindex1 += row_size;
	    
	    index[0] = rindex1[0];
	    index[1] = rindex1[1];
	    index[2] = rindex1[2];
	    index[3] = rindex1[3];
	    index[4] = rindex1[4];
	    index[5] = rindex1[5];
	    index[6] = rindex1[6];
	    index[7] = rindex1[7];
	    index += row_size;
	    rindex1 += row_size;
	  }
	} else if (right_back & 0x2) {
	  /* Half-word bit aligned, use 16 bit copy */
	  short *src = (short *)rindex1;
	  short *dest = (short *)index;
	  row_size >>= 1;
	  for (rr = 0; rr < 4; rr++) {
	    dest[0] = src[0];
	    dest[1] = src[1];
	    dest[2] = src[2];
	    dest[3] = src[3];
	    dest += row_size;
	    src += row_size;
	    
	    dest[0] = src[0];
	    dest[1] = src[1];
	    dest[2] = src[2];
	    dest[3] = src[3];
	    dest += row_size;
	    src += row_size;
	  }
	} else {
	  /* Word aligned, use 32 bit copy */
	  int *src = (int *)rindex1;
	  int *dest = (int *)index;
	  row_size >>= 2;
	  for (rr = 0; rr < 4; rr++) {
	    dest[0] = src[0];
	    dest[1] = src[1];
	    dest += row_size;
	    src += row_size;
	    
	    dest[0] = src[0];
	    dest[1] = src[1];
	    dest += row_size;
	    src += row_size;
	  }
	}
      }
    } else {
      unsigned char *cm = cropTbl + MAX_NEG_CROP;
      rindex2 = rindex1 + right_half_back + (down_half_back * row_size);
      if (!zflag)
	for (rr = 0; rr < 4; rr++) {
	  index[0] = cm[((int) (rindex1[0] + rindex2[0]) >> 1) + blockvals[0]];
	  index[1] = cm[((int) (rindex1[1] + rindex2[1]) >> 1) + blockvals[1]];
	  index[2] = cm[((int) (rindex1[2] + rindex2[2]) >> 1) + blockvals[2]];
	  index[3] = cm[((int) (rindex1[3] + rindex2[3]) >> 1) + blockvals[3]];
	  index[4] = cm[((int) (rindex1[4] + rindex2[4]) >> 1) + blockvals[4]];
	  index[5] = cm[((int) (rindex1[5] + rindex2[5]) >> 1) + blockvals[5]];
	  index[6] = cm[((int) (rindex1[6] + rindex2[6]) >> 1) + blockvals[6]];
	  index[7] = cm[((int) (rindex1[7] + rindex2[7]) >> 1) + blockvals[7]];
	  index += row_size;
	  rindex1 += row_size;
	  rindex2 += row_size;
	  
	  index[0] = cm[((int) (rindex1[0] + rindex2[0]) >> 1) + blockvals[8]];
	  index[1] = cm[((int) (rindex1[1] + rindex2[1]) >> 1) + blockvals[9]];
	  index[2] = cm[((int) (rindex1[2] + rindex2[2]) >> 1) + blockvals[10]];
	  index[3] = cm[((int) (rindex1[3] + rindex2[3]) >> 1) + blockvals[11]];
	  index[4] = cm[((int) (rindex1[4] + rindex2[4]) >> 1) + blockvals[12]];
	  index[5] = cm[((int) (rindex1[5] + rindex2[5]) >> 1) + blockvals[13]];
	  index[6] = cm[((int) (rindex1[6] + rindex2[6]) >> 1) + blockvals[14]];
	  index[7] = cm[((int) (rindex1[7] + rindex2[7]) >> 1) + blockvals[15]];
	  blockvals += 16;
	  index += row_size;
	  rindex1 += row_size;
	  rindex2 += row_size;
	}
      else
	for (rr = 0; rr < 4; rr++) {
	  index[0] = (int) (rindex1[0] + rindex2[0]) >> 1;
	  index[1] = (int) (rindex1[1] + rindex2[1]) >> 1;
	  index[2] = (int) (rindex1[2] + rindex2[2]) >> 1;
	  index[3] = (int) (rindex1[3] + rindex2[3]) >> 1;
	  index[4] = (int) (rindex1[4] + rindex2[4]) >> 1;
	  index[5] = (int) (rindex1[5] + rindex2[5]) >> 1;
	  index[6] = (int) (rindex1[6] + rindex2[6]) >> 1;
	  index[7] = (int) (rindex1[7] + rindex2[7]) >> 1;
	  index += row_size;
	  rindex1 += row_size;
	  rindex2 += row_size;
	  
	  index[0] = (int) (rindex1[0] + rindex2[0]) >> 1;
	  index[1] = (int) (rindex1[1] + rindex2[1]) >> 1;
	  index[2] = (int) (rindex1[2] + rindex2[2]) >> 1;
	  index[3] = (int) (rindex1[3] + rindex2[3]) >> 1;
	  index[4] = (int) (rindex1[4] + rindex2[4]) >> 1;
	  index[5] = (int) (rindex1[5] + rindex2[5]) >> 1;
	  index[6] = (int) (rindex1[6] + rindex2[6]) >> 1;
	  index[7] = (int) (rindex1[7] + rindex2[7]) >> 1;
	  index += row_size;
	  rindex1 += row_size;
	  rindex2 += row_size;
	}
    }

#ifdef LOOSE_MPEG
  }
#endif

}


/*
 *--------------------------------------------------------------
 *
 * ReconBiMBlock --
 *
 *	Reconstructs bidirectionally predicted macroblocks.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

static void
ReconBiMBlock(vid_stream, bnum, recon_right_for, recon_down_for,
	      recon_right_back, recon_down_back, zflag)
  VidStream *vid_stream;
  int bnum, recon_right_for, recon_down_for, recon_right_back, recon_down_back;
  int zflag;
{
  int mb_row, mb_col, row, col, row_size, rr;
  unsigned char *dest, *past, *future;
  int right_for, down_for, right_half_for, down_half_for;
  int right_back, down_back, right_half_back, down_half_back;
  unsigned char *index, *rindex1, *bindex1;
  short int *blockvals;
  int forw_row_start, back_row_start, forw_col_start, back_col_start;

#ifdef LOOSE_MPEG
  int illegal_forw = 0;
  int illegal_back = 0;
#endif

  /* Calculate macroblock row and column from address. */

  mb_row = vid_stream->mblock.mb_address / vid_stream->mb_width;
  mb_col = vid_stream->mblock.mb_address % vid_stream->mb_width;

  /* If block is luminance block... */

  if (bnum < 4) {

    /*
     * Calculate right_for, down_for, right_half_for, down_half_for,
     * right_back, down_bakc, right_half_back, and down_half_back, motion
     * vectors.
     */

    right_for = recon_right_for >> 1;
    down_for = recon_down_for >> 1;
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;

    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;

    /* Set dest to luminance plane of current pict image. */

    dest = vid_stream->current->luminance;

    /* If past frame exists, set past to luminance plane of past frame. */

    if (vid_stream->past != NULL)
      past = vid_stream->past->luminance;

    /*
     * If future frame exists, set future to luminance plane of future frame.
     */

    if (vid_stream->future != NULL)
      future = vid_stream->future->luminance;

    /* Establish row size. */

    row_size = (vid_stream->mb_width << 4);

    /* Calculate row,col of upper left pixel in block. */

    row = (mb_row << 4);
    col = (mb_col << 4);
    if (bnum > 1)
      row += 8;
    if (bnum & 0x01)
      col += 8;

    forw_col_start = col + right_for;
    forw_row_start = row + down_for;

    back_col_start = col + right_back;
    back_row_start = row + down_back;

#ifdef LOOSE_MPEG

    /* Check for illegal pred. blocks. */


    if (forw_col_start+8 > lmaxx) illegal_forw = 1;
    else if (forw_col_start < 0) illegal_forw = 1;

    if (forw_row_start+8 > lmaxy) illegal_forw = 1;
    else if (forw_row_start < 0) illegal_forw = 1;

    if (back_col_start+8 > lmaxx) illegal_back = 1;
    else if (back_col_start < 0) illegal_back = 1;

    if (back_row_start+8 > lmaxy) illegal_back = 1;
    else if (back_row_start < 0) illegal_back = 1;

#endif

  }
  /* Otherwise, block is NOT luminance block, ... */

  else {

    /* Construct motion vectors. */

    recon_right_for /= 2;
    recon_down_for /= 2;
    right_for = recon_right_for >> 1;
    down_for = recon_down_for >> 1;
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;

    recon_right_back /= 2;
    recon_down_back /= 2;
    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;

    /* Establish row size. */

    row_size = (vid_stream->mb_width << 3);

    /* Calculate row,col of upper left pixel in block. */

    row = (mb_row << 3);
    col = (mb_col << 3);

    forw_col_start = col + right_for;
    forw_row_start = row + down_for;

    back_col_start = col + right_back;
    back_row_start = row + down_back;

#ifdef LOOSE_MPEG

    /* Check for illegal pred. blocks. */

    if (forw_col_start+8 > cmaxx) illegal_forw = 1;
    else if (forw_col_start < 0) illegal_forw = 1;

    if (forw_row_start+8 > cmaxy) illegal_forw = 1;
    else if (forw_row_start < 0) illegal_forw = 1;

    if (back_col_start+8 > cmaxx) illegal_back = 1;
    else if (back_col_start < 0) illegal_back = 1;
    
    if (back_row_start+8 > cmaxy) illegal_back = 1;
    else if (back_row_start < 0) illegal_back = 1;

#endif
    
    /* If block is Cr block... */

    if (bnum == 4) {

      /* Set dest to Cr plane of current pict image. */

      dest = vid_stream->current->Cr;

      /* If past frame exists, set past to Cr plane of past image. */

      if (vid_stream->past != NULL)
	past = vid_stream->past->Cr;

      /*
       * If future frame exists, set future to Cr plane of future image.
       */

      if (vid_stream->future != NULL)
	future = vid_stream->future->Cr;
    }
    /* Otherwise, block is Cb block... */

    else {

      /* Set dest to Cb plane of current pict image. */

      dest = vid_stream->current->Cb;

      /* If past frame exists, set past to Cb plane of past frame. */

      if (vid_stream->past != NULL)
	past = vid_stream->past->Cb;

      /*
       * If future frame exists, set future to Cb plane of future frame.
       */

      if (vid_stream->future != NULL)
	future = vid_stream->future->Cb;
    }
  }

  /* For each pixel in block... */

  index = dest + (row * row_size) + col;

#ifdef LOOSE_MPEG
  if (illegal_forw) 
    rindex1 = future + back_row_start * row_size + back_col_start;
  else 
#endif
    rindex1 = past + forw_row_start  * row_size + forw_col_start;

#ifdef LOOSE_MPEG
  if (illegal_back) 
    bindex1 = past + forw_row_start * row_size + forw_col_start;
  else 
#endif
    bindex1 = future + back_row_start * row_size + back_col_start;

  blockvals = (short int *) &(vid_stream->block.dct_recon[0][0]);

  {
  unsigned char *cm = cropTbl + MAX_NEG_CROP;
  if (!zflag)
    for (rr = 0; rr < 4; rr++) {
      index[0] = cm[((int) (rindex1[0] + bindex1[0]) >> 1) + blockvals[0]];
      index[1] = cm[((int) (rindex1[1] + bindex1[1]) >> 1) + blockvals[1]];
      index[2] = cm[((int) (rindex1[2] + bindex1[2]) >> 1) + blockvals[2]];
      index[3] = cm[((int) (rindex1[3] + bindex1[3]) >> 1) + blockvals[3]];
      index[4] = cm[((int) (rindex1[4] + bindex1[4]) >> 1) + blockvals[4]];
      index[5] = cm[((int) (rindex1[5] + bindex1[5]) >> 1) + blockvals[5]];
      index[6] = cm[((int) (rindex1[6] + bindex1[6]) >> 1) + blockvals[6]];
      index[7] = cm[((int) (rindex1[7] + bindex1[7]) >> 1) + blockvals[7]];
      index += row_size;
      rindex1 += row_size;
      bindex1 += row_size;

      index[0] = cm[((int) (rindex1[0] + bindex1[0]) >> 1) + blockvals[8]];
      index[1] = cm[((int) (rindex1[1] + bindex1[1]) >> 1) + blockvals[9]];
      index[2] = cm[((int) (rindex1[2] + bindex1[2]) >> 1) + blockvals[10]];
      index[3] = cm[((int) (rindex1[3] + bindex1[3]) >> 1) + blockvals[11]];
      index[4] = cm[((int) (rindex1[4] + bindex1[4]) >> 1) + blockvals[12]];
      index[5] = cm[((int) (rindex1[5] + bindex1[5]) >> 1) + blockvals[13]];
      index[6] = cm[((int) (rindex1[6] + bindex1[6]) >> 1) + blockvals[14]];
      index[7] = cm[((int) (rindex1[7] + bindex1[7]) >> 1) + blockvals[15]];
      blockvals += 16;
      index += row_size;
      rindex1 += row_size;
      bindex1 += row_size;
    }

  else
    for (rr = 0; rr < 4; rr++) {
      index[0] = (int) (rindex1[0] + bindex1[0]) >> 1;
      index[1] = (int) (rindex1[1] + bindex1[1]) >> 1;
      index[2] = (int) (rindex1[2] + bindex1[2]) >> 1;
      index[3] = (int) (rindex1[3] + bindex1[3]) >> 1;
      index[4] = (int) (rindex1[4] + bindex1[4]) >> 1;
      index[5] = (int) (rindex1[5] + bindex1[5]) >> 1;
      index[6] = (int) (rindex1[6] + bindex1[6]) >> 1;
      index[7] = (int) (rindex1[7] + bindex1[7]) >> 1;
      index += row_size;
      rindex1 += row_size;
      bindex1 += row_size;

      index[0] = (int) (rindex1[0] + bindex1[0]) >> 1;
      index[1] = (int) (rindex1[1] + bindex1[1]) >> 1;
      index[2] = (int) (rindex1[2] + bindex1[2]) >> 1;
      index[3] = (int) (rindex1[3] + bindex1[3]) >> 1;
      index[4] = (int) (rindex1[4] + bindex1[4]) >> 1;
      index[5] = (int) (rindex1[5] + bindex1[5]) >> 1;
      index[6] = (int) (rindex1[6] + bindex1[6]) >> 1;
      index[7] = (int) (rindex1[7] + bindex1[7]) >> 1;
      index += row_size;
      rindex1 += row_size;
      bindex1 += row_size;
    }
  }
}

/*
 *--------------------------------------------------------------
 *
 * ProcessSkippedPFrameMBlocks --
 *
 *	Processes skipped macroblocks in P frames.
 *
 * Results:
 *	Calculates pixel values for luminance, Cr, and Cb planes
 *      in current pict image for skipped macroblocks.
 *
 * Side effects:
 *	Pixel values in pict image changed.
 *
 *--------------------------------------------------------------
 */

static void
ProcessSkippedPFrameMBlocks(vid_stream)
  VidStream *vid_stream;
{
  int row_size, half_row, mb_row, mb_col, row, col, rr;
  int addr, row_incr, half_row_incr, crow, ccol;
  int *dest, *src, *dest1, *src1;
  char command[256];

  /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */

  row_size = vid_stream->mb_width << 4;
  half_row = (row_size >> 1);
  row_incr = row_size >> 2;
  half_row_incr = half_row >> 2;

  /* For each skipped macroblock, do... */

  for (addr = vid_stream->mblock.past_mb_addr + 1;
       addr < vid_stream->mblock.mb_address; addr++) {

    /* Calculate macroblock row and col. */

    mb_row = addr / vid_stream->mb_width;
    mb_col = addr % vid_stream->mb_width;

    /* Calculate upper left pixel row,col for luminance plane. */

    row = mb_row << 4;
    col = mb_col << 4;


    /* For each row in macroblock luminance plane... */

    dest = (int *)(vid_stream->current->luminance + (row * row_size) + col);
    src = (int *)(vid_stream->future->luminance + (row * row_size) + col);

    for (rr = 0; rr < 8; rr++) {

      /* Copy pixel values from last I or P picture. */

      dest[0] = src[0];
      dest[1] = src[1];
      dest[2] = src[2];
      dest[3] = src[3];
      dest += row_incr;
      src += row_incr;

      dest[0] = src[0];
      dest[1] = src[1];
      dest[2] = src[2];
      dest[3] = src[3];
      dest += row_incr;
      src += row_incr;
    }

    /*
     * Divide row,col to get upper left pixel of macroblock in Cr and Cb
     * planes.
     */

    crow = row >> 1;
    ccol = col >> 1;

    /* For each row in Cr, and Cb planes... */

    dest = (int *)(vid_stream->current->Cr + (crow * half_row) + ccol);
    src = (int *)(vid_stream->future->Cr + (crow * half_row) + ccol);
    dest1 = (int *)(vid_stream->current->Cb + (crow * half_row) + ccol);
    src1 = (int *)(vid_stream->future->Cb + (crow * half_row) + ccol);

    for (rr = 0; rr < 4; rr++) {

      /* Copy pixel values from last I or P picture. */

      dest[0] = src[0];
      dest[1] = src[1];

      dest1[0] = src1[0];
      dest1[1] = src1[1];

      dest += half_row_incr;
      src += half_row_incr;
      dest1 += half_row_incr;
      src1 += half_row_incr;

      dest[0] = src[0];
      dest[1] = src[1];

      dest1[0] = src1[0];
      dest1[1] = src1[1];

      dest += half_row_incr;
      src += half_row_incr;
      dest1 += half_row_incr;
      src1 += half_row_incr;
    }

    if (ditherType == MBORDERED_DITHER) {
      MBOrderedDitherDisplayCopy(vid_stream, addr,
				 1, 0, 0, 0, 0, 0,
				 vid_stream->future->display,
				 (unsigned char *) NULL);
      ditherFlags[addr] = 0;
    }
    
    /* set the associated block in the display to clear */
    sprintf(command, "ModBox %d %d 0\n",
	    mb_col, mb_row );
    AddToScript( vid_stream, command );
    
  }

  vid_stream->mblock.recon_right_for_prev = 0;
  vid_stream->mblock.recon_down_for_prev = 0;
}




/*
 *--------------------------------------------------------------
 *
 * ProcessSkippedBFrameMBlocks --
 *
 *	Processes skipped macroblocks in B frames.
 *
 * Results:
 *	Calculates pixel values for luminance, Cr, and Cb planes
 *      in current pict image for skipped macroblocks.
 *
 * Side effects:
 *	Pixel values in pict image changed.
 *
 *--------------------------------------------------------------
 */

static void
ProcessSkippedBFrameMBlocks(vid_stream)
  VidStream *vid_stream;
{
  int row_size, half_row, mb_row, mb_col, row, col, rr;
  int right_half_for, down_half_for, c_right_half_for, c_down_half_for;
  int right_half_back, down_half_back, c_right_half_back, c_down_half_back;
  int addr, right_for, down_for;
  int recon_right_for, recon_down_for;
  int recon_right_back, recon_down_back;
  int right_back, down_back;
  int c_right_for, c_down_for;
  int c_right_back, c_down_back;
  unsigned char forw_lum[256];
  unsigned char forw_cr[64], forw_cb[64];
  unsigned char back_lum[256], back_cr[64], back_cb[64];
  int row_incr, half_row_incr;
  int ccol, crow;
  char command[256];

  /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */

  row_size = vid_stream->mb_width << 4;
  half_row = (row_size >> 1);
  row_incr = row_size >> 2;
  half_row_incr =  half_row >> 2;

  /* Establish motion vector codes based on full pixel flag. */

  if (vid_stream->picture.full_pel_forw_vector) {
    recon_right_for = vid_stream->mblock.recon_right_for_prev << 1;
    recon_down_for = vid_stream->mblock.recon_down_for_prev << 1;
  } else {
    recon_right_for = vid_stream->mblock.recon_right_for_prev;
    recon_down_for = vid_stream->mblock.recon_down_for_prev;
  }

  if (vid_stream->picture.full_pel_back_vector) {
    recon_right_back = vid_stream->mblock.recon_right_back_prev << 1;
    recon_down_back = vid_stream->mblock.recon_down_back_prev << 1;
  } else {
    recon_right_back = vid_stream->mblock.recon_right_back_prev;
    recon_down_back = vid_stream->mblock.recon_down_back_prev;
  }

  /* If only one motion vector, do display copy, else do full
     calculation. 
  */

  if (ditherType == MBORDERED_DITHER) {
    if (vid_stream->mblock.bpict_past_forw &&
	!vid_stream->mblock.bpict_past_back) {
      for (addr = vid_stream->mblock.past_mb_addr+1;
	   addr < vid_stream->mblock.mb_address; addr++) {
	
	MBOrderedDitherDisplayCopy(vid_stream, addr,
				   1, recon_right_for, recon_down_for,
				 0, 0, 0, vid_stream->past->display,
				   vid_stream->future->display);
	ditherFlags[addr] = 0;
      }
      return;
    }
    if (vid_stream->mblock.bpict_past_back && 
	!vid_stream->mblock.bpict_past_forw) {
      for (addr = vid_stream->mblock.past_mb_addr+1;
	   addr < vid_stream->mblock.mb_address; addr++) {
	
	MBOrderedDitherDisplayCopy(vid_stream, addr,
				   0, 0, 0,
				   1, recon_right_back, recon_down_back,
				   vid_stream->past->display, vid_stream->future->display);
	ditherFlags[addr] = 0;
      }
      return;
    }
  }

  /* Calculate motion vectors. */
  
  if (vid_stream->mblock.bpict_past_forw) {
    right_for = recon_right_for >> 1;
    down_for = recon_down_for >> 1;
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;
    
    recon_right_for /= 2;
    recon_down_for /= 2;
    c_right_for = recon_right_for >> 1;
    c_down_for = recon_down_for >> 1;
    c_right_half_for = recon_right_for & 0x1;
    c_down_half_for = recon_down_for & 0x1;
    
  }
  if (vid_stream->mblock.bpict_past_back) {
    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;
    
    recon_right_back /= 2;
    recon_down_back /= 2;
    c_right_back = recon_right_back >> 1;
    c_down_back = recon_down_back >> 1;
    c_right_half_back = recon_right_back & 0x1;
    c_down_half_back = recon_down_back & 0x1;
    
  }
  /* For each skipped macroblock, do... */
  
  for (addr = vid_stream->mblock.past_mb_addr + 1;
       addr < vid_stream->mblock.mb_address; addr++) {
    
    /* Calculate macroblock row and col. */
    
    mb_row = addr / vid_stream->mb_width;
    mb_col = addr % vid_stream->mb_width;
    
    /* Calculate upper left pixel row,col for luminance plane. */
    
    row = mb_row << 4;
    col = mb_col << 4;
    crow = row / 2;
    ccol = col / 2;
    
    /* If forward predicted, calculate prediction values. */
    
    if (vid_stream->mblock.bpict_past_forw) {
      
      ReconSkippedBlock(vid_stream->past->luminance, forw_lum,
			row, col, row_size, right_for, down_for,
			right_half_for, down_half_for, 16);
      ReconSkippedBlock(vid_stream->past->Cr, forw_cr, crow,
			ccol, half_row,
			c_right_for, c_down_for, c_right_half_for, c_down_half_for, 8);
      ReconSkippedBlock(vid_stream->past->Cb, forw_cb, crow,
			ccol, half_row,
			c_right_for, c_down_for, c_right_half_for, c_down_half_for, 8);
    }
    /* If back predicted, calculate prediction values. */
    
    if (vid_stream->mblock.bpict_past_back) {
      ReconSkippedBlock(vid_stream->future->luminance, back_lum,
			row, col, row_size, right_back, down_back,
			right_half_back, down_half_back, 16);
      ReconSkippedBlock(vid_stream->future->Cr, back_cr, crow,
			ccol, half_row,
			c_right_back, c_down_back,
			c_right_half_back, c_down_half_back, 8);
      ReconSkippedBlock(vid_stream->future->Cb, back_cb, crow,
			ccol, half_row,
			c_right_back, c_down_back,
			c_right_half_back, c_down_half_back, 8);
    }
    if (vid_stream->mblock.bpict_past_forw &&
	!vid_stream->mblock.bpict_past_back) {
      
      int *dest, *dest1;
      int *src, *src1;
      dest = (int *)(vid_stream->current->luminance + (row * row_size) + col);
      src = (int *)forw_lum;
      
      for (rr = 0; rr < 16; rr++) {
	
	/* memcpy(dest, forw_lum+(rr<<4), 16);  */
	dest[0] = src[0];
	dest[1] = src[1];
	dest[2] = src[2];
	dest[3] = src[3];
	dest += row_incr;
	src += 4;
      }
      
      dest = (int *)(vid_stream->current->Cr + (crow * half_row) + ccol);
      dest1 = (int *)(vid_stream->current->Cb + (crow * half_row) + ccol);
      src = (int *)forw_cr;
      src1 = (int *)forw_cb;
      
      for (rr = 0; rr < 8; rr++) {
	/*
	 * memcpy(dest, forw_cr+(rr<<3), 8); memcpy(dest1, forw_cb+(rr<<3),
	 * 8);
	 */
	
	dest[0] = src[0];
	dest[1] = src[1];
	
	dest1[0] = src1[0];
	dest1[1] = src1[1];
	
	dest += half_row_incr;
	dest1 += half_row_incr;
	src += 2;
	src1 += 2;
      }
    } else if (vid_stream->mblock.bpict_past_back &&
	       !vid_stream->mblock.bpict_past_forw) {
      
      int *src, *src1;
      int *dest, *dest1;
      dest = (int *)(vid_stream->current->luminance + (row * row_size) + col);
      src = (int *)back_lum;
      
      for (rr = 0; rr < 16; rr++) {
	dest[0] = src[0];
	dest[1] = src[1];
	dest[2] = src[2];
	dest[3] = src[3];
	dest += row_incr;
	src += 4;
      }
      
      
      dest = (int *)(vid_stream->current->Cr + (crow * half_row) + ccol);
      dest1 = (int *)(vid_stream->current->Cb + (crow * half_row) + ccol);
      src = (int *)back_cr;
      src1 = (int *)back_cb;
      
      for (rr = 0; rr < 8; rr++) {
	/*
	 * memcpy(dest, back_cr+(rr<<3), 8); memcpy(dest1, back_cb+(rr<<3),
	 * 8);
	 */
	
	dest[0] = src[0];
	dest[1] = src[1];
	
	dest1[0] = src1[0];
	dest1[1] = src1[1];
	
	dest += half_row_incr;
	dest1 += half_row_incr;
	src += 2;
	src1 += 2;
      }
    } else {
      
      unsigned char *src1, *src2, *src1a, *src2a;
      unsigned char *dest, *dest1;
      dest = vid_stream->current->luminance + (row * row_size) + col;
      src1 = forw_lum;
      src2 = back_lum;
      
      for (rr = 0; rr < 16; rr++) {
	dest[0] = (int) (src1[0] + src2[0]) >> 1;
	dest[1] = (int) (src1[1] + src2[1]) >> 1;
	dest[2] = (int) (src1[2] + src2[2]) >> 1;
	dest[3] = (int) (src1[3] + src2[3]) >> 1;
	dest[4] = (int) (src1[4] + src2[4]) >> 1;
	dest[5] = (int) (src1[5] + src2[5]) >> 1;
	dest[6] = (int) (src1[6] + src2[6]) >> 1;
	dest[7] = (int) (src1[7] + src2[7]) >> 1;
	dest[8] = (int) (src1[8] + src2[8]) >> 1;
	dest[9] = (int) (src1[9] + src2[9]) >> 1;
	dest[10] = (int) (src1[10] + src2[10]) >> 1;
	dest[11] = (int) (src1[11] + src2[11]) >> 1;
	dest[12] = (int) (src1[12] + src2[12]) >> 1;
	dest[13] = (int) (src1[13] + src2[13]) >> 1;
	dest[14] = (int) (src1[14] + src2[14]) >> 1;
	dest[15] = (int) (src1[15] + src2[15]) >> 1;
	dest += row_size;
	src1 += 16;
	src2 += 16;
      }
      
      
      dest = vid_stream->current->Cr + (crow * half_row) + ccol;
      dest1 = vid_stream->current->Cb + (crow * half_row) + ccol;
      src1 = forw_cr;
      src2 = back_cr;
      src1a = forw_cb;
      src2a = back_cb;
      
      for (rr = 0; rr < 8; rr++) {
	dest[0] = (int) (src1[0] + src2[0]) >> 1;
	dest[1] = (int) (src1[1] + src2[1]) >> 1;
	dest[2] = (int) (src1[2] + src2[2]) >> 1;
	dest[3] = (int) (src1[3] + src2[3]) >> 1;
	dest[4] = (int) (src1[4] + src2[4]) >> 1;
	dest[5] = (int) (src1[5] + src2[5]) >> 1;
	dest[6] = (int) (src1[6] + src2[6]) >> 1;
	dest[7] = (int) (src1[7] + src2[7]) >> 1;
	dest += half_row;
	src1 += 8;
	src2 += 8;
	
	dest1[0] = (int) (src1a[0] + src2a[0]) >> 1;
	dest1[1] = (int) (src1a[1] + src2a[1]) >> 1;
	dest1[2] = (int) (src1a[2] + src2a[2]) >> 1;
	dest1[3] = (int) (src1a[3] + src2a[3]) >> 1;
	dest1[4] = (int) (src1a[4] + src2a[4]) >> 1;
	dest1[5] = (int) (src1a[5] + src2a[5]) >> 1;
	dest1[6] = (int) (src1a[6] + src2a[6]) >> 1;
	dest1[7] = (int) (src1a[7] + src2a[7]) >> 1;
	dest1 += half_row;
	src1a += 8;
	src2a += 8;
      }
    }
    
    if (ditherType == MBORDERED_DITHER) {
      ditherFlags[addr] = 1;
    }
  }
  
  /* set the associated block in the display to clear */
  sprintf(command, "ModBox %d %d 0\n",
	  mb_col , mb_row );
  AddToScript( vid_stream, command );
  
}




/*
 *--------------------------------------------------------------
 *
 * ReconSkippedBlock --
 *
 *	Reconstructs predictive block for skipped macroblocks
 *      in B Frames.
 *
 * Results:
 *	No return values.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static void
ReconSkippedBlock(source, dest, row, col, row_size,
		  right, down, right_half, down_half, width)
  unsigned char *source;
  unsigned char *dest;
  int row, col, row_size, right, down, right_half, down_half, width;
{
  int rr;
  unsigned char *source2;

  source += ((row + down) * row_size) + col + right;

  if (width == 16) {
    if ((!right_half) && (!down_half)) {
	if (right & 0x1) {
	  /* No alignment, use bye copy */
	  for (rr = 0; rr < 16; rr++) {
	    dest[0] = source[0];
	    dest[1] = source[1];
	    dest[2] = source[2];
	    dest[3] = source[3];
	    dest[4] = source[4];
	    dest[5] = source[5];
	    dest[6] = source[6];
	    dest[7] = source[7];
	    dest[8] = source[8];
	    dest[9] = source[9];
	    dest[10] = source[10];
	    dest[11] = source[11];
	    dest[12] = source[12];
	    dest[13] = source[13];
	    dest[14] = source[14];
	    dest[15] = source[15];
	    dest += 16;
	    source += row_size;
	  }
	} else if (right & 0x2) {
	  /* Half-word bit aligned, use 16 bit copy */
	  short *src = (short *)source;
	  short *d = (short *)dest;
	  row_size >>= 1;
	  for (rr = 0; rr < 16; rr++) {
	    d[0] = src[0];
	    d[1] = src[1];
	    d[2] = src[2];
	    d[3] = src[3];
	    d[4] = src[4];
	    d[5] = src[5];
	    d[6] = src[6];
	    d[7] = src[7];
	    d += 8;
	    src += row_size;
	  }
	} else {
	  /* Word aligned, use 32 bit copy */
	  int *src = (int *)source;
	  int *d = (int *)dest;
	  row_size >>= 2;
	  for (rr = 0; rr < 16; rr++) {
	    d[0] = src[0];
	    d[1] = src[1];
	    d[2] = src[2];
	    d[3] = src[3];
	    d += 4;
	    src += row_size;
	  }
	}
    } else {
      source2 = source + right_half + (row_size * down_half);
      for (rr = 0; rr < width; rr++) {
	dest[0] = (int) (source[0] + source2[0]) >> 1;
	dest[1] = (int) (source[1] + source2[1]) >> 1;
	dest[2] = (int) (source[2] + source2[2]) >> 1;
	dest[3] = (int) (source[3] + source2[3]) >> 1;
	dest[4] = (int) (source[4] + source2[4]) >> 1;
	dest[5] = (int) (source[5] + source2[5]) >> 1;
	dest[6] = (int) (source[6] + source2[6]) >> 1;
	dest[7] = (int) (source[7] + source2[7]) >> 1;
	dest[8] = (int) (source[8] + source2[8]) >> 1;
	dest[9] = (int) (source[9] + source2[9]) >> 1;
	dest[10] = (int) (source[10] + source2[10]) >> 1;
	dest[11] = (int) (source[11] + source2[11]) >> 1;
	dest[12] = (int) (source[12] + source2[12]) >> 1;
	dest[13] = (int) (source[13] + source2[13]) >> 1;
	dest[14] = (int) (source[14] + source2[14]) >> 1;
	dest[15] = (int) (source[15] + source2[15]) >> 1;
	dest += width;
	source += row_size;
	source2 += row_size;
      }
    }
  } else {			/* (width == 8) */
    assert(width == 8);
    if ((!right_half) && (!down_half)) {
      if (right & 0x1) {
	for (rr = 0; rr < width; rr++) {
	  dest[0] = source[0];
	  dest[1] = source[1];
	  dest[2] = source[2];
	  dest[3] = source[3];
	  dest[4] = source[4];
	  dest[5] = source[5];
	  dest[6] = source[6];
	  dest[7] = source[7];
	  dest += 8;
	  source += row_size;
	}
      } else if (right & 0x02) {
	short *d = (short *)dest;
	short *src = (short *)source;
	row_size >>= 1;
	for (rr = 0; rr < width; rr++) {
	  d[0] = src[0];
	  d[1] = src[1];
	  d[2] = src[2];
	  d[3] = src[3];
	  d += 4;
	  src += row_size;
	}
      } else {
	int *d = (int *)dest;
	int *src = (int *)source;
	row_size >>= 2;
	for (rr = 0; rr < width; rr++) {
	  d[0] = src[0];
	  d[1] = src[1];
	  d += 2;
	  src += row_size;
	}
      }
    } else {
      source2 = source + right_half + (row_size * down_half);
      for (rr = 0; rr < width; rr++) {
	dest[0] = (int) (source[0] + source2[0]) >> 1;
	dest[1] = (int) (source[1] + source2[1]) >> 1;
	dest[2] = (int) (source[2] + source2[2]) >> 1;
	dest[3] = (int) (source[3] + source2[3]) >> 1;
	dest[4] = (int) (source[4] + source2[4]) >> 1;
	dest[5] = (int) (source[5] + source2[5]) >> 1;
	dest[6] = (int) (source[6] + source2[6]) >> 1;
	dest[7] = (int) (source[7] + source2[7]) >> 1;
	dest += width;
	source += row_size;
	source2 += row_size;
      }
    }
  }
}



/*
 *--------------------------------------------------------------
 *
 * DoPictureDisplay --
 *
 *	Converts image from Lum, Cr, Cb to colormap space. Puts
 *      image in lum plane. Updates past and future frame
 *      pointers. Dithers image. Sends to display mechanism.
 *
 * Results:
 *	Pict image structure locked if displaying or if frame
 *      is needed as past or future reference.
 *
 * Side effects:
 *	Lum plane pummelled.
 *
 *--------------------------------------------------------------
 */

static int
DoPictureDisplay(vid_stream)
     VidStream *vid_stream;
{
  extern Tcl_Interp *interp;
  extern int inPlayLoop;
  char command[256];
  float barSize;
  unsigned int pictureType = vid_stream->picture.code_type;

  /* Add bar creation command, size and type info to the script */
  /* for this frame                                             */
  sprintf( command, "ScrollRect %d %d\n", 
	  (int)vid_stream->currentSize, 
	  vid_stream->picture.code_type);  
  AddToScript( vid_stream, command );
  sprintf( command, "set SizeFrame \"Frame Size: %d\"\n", 
	  vid_stream->currentSize);
  AddToScript( vid_stream, command );
  sprintf( command, "notePictureType %d\n", (int) pictureType);
  AddToScript( vid_stream, command);
  
  /* Convert to colormap space and dither. */  
  DoDitherImage(vid_stream->current->luminance, vid_stream->current->Cr,
		vid_stream->current->Cb, vid_stream->current->display,
		vid_stream->mb_height * 16, vid_stream->mb_width * 16);
  
  /* Update past and future references if needed. */
  if ((pictureType == I_TYPE) ||
      (pictureType == P_TYPE)) {
    char * tmpScript;
    int    tmpScriptLength;
    int    tmpSize;
    if (vid_stream->future == NULL) {
      /* nothing waiting to be displayed - just put current display and
	 tcl script in "future" slots to be used later, and return */
      vid_stream->future = vid_stream->current;
      vid_stream->futureScript = vid_stream->currentScript;
      vid_stream->futureScriptLength = vid_stream->currentScriptLength;
      vid_stream->futureSize = vid_stream->currentSize;
      vid_stream->futureType = pictureType;
      vid_stream->currentScriptLength = 0;
      vid_stream->currentScript =
	(char *)malloc(vid_stream->currentScriptBufSize*sizeof(char));
      vid_stream->currentScript[0] = '\0';
      vid_stream->currentSize = 0;

      vid_stream->future->locked |= FUTURE_LOCK;
      return TRUE;
    }
    /* otherwise something is waiting to be displayed.  Put the current
       frame and script into the "future" slots to be displayed later,
       and move the frame and script that were waiting in "future" into
       "current" and "past" slots */
    if (vid_stream->past != NULL) {
      vid_stream->past->locked &= ~PAST_LOCK;
    }
    vid_stream->past = vid_stream->future;
    vid_stream->past->locked &= ~FUTURE_LOCK;
    vid_stream->past->locked |= PAST_LOCK;
    vid_stream->future = vid_stream->current;
    tmpScript = vid_stream->futureScript;
    tmpScriptLength = vid_stream->futureScriptLength;
    tmpSize = vid_stream->futureSize;
    pictureType = vid_stream->futureType;
    vid_stream->futureScript = vid_stream->currentScript;
    vid_stream->futureScriptLength = vid_stream->currentScriptLength;
    vid_stream->futureSize = vid_stream->currentSize;
    vid_stream->futureType = vid_stream->picture.code_type;
    vid_stream->future->locked |= FUTURE_LOCK;
    vid_stream->current = vid_stream->past;
    vid_stream->currentScript = tmpScript;
    vid_stream->currentScriptLength = tmpScriptLength;
    vid_stream->currentSize = tmpSize;
  }
  
  /* If this is the first I Frame after a GOP header, this is the first frame */
  /* that will be presented upon rewinding to that GOP header.  Because frames */
  /* are presented out of order, it would be hard to calculate the correct frame number */
  /* for this frame upon a rewind - we need to just save it here. */
  if ((pictureType == I_TYPE) && (startedGOP)) {
    recordFrameNo( vid_stream );
    startedGOP = FALSE;
  }

  /* Calculate number of bits used in past second of display   */
  /* and send the number to the bitrate monitor.  (This must   */
  /* be done here as opposed to before the switch because it   */
  /* relies on previous displays.)                             */
  vid_stream->bitsPastSecAry[ vid_stream->bitAryIndex ] 
    = vid_stream->currentSize;
  vid_stream->bitAryIndex += 1;

  /* grow the array if necessary */
  if (vid_stream->bitAryIndex == vid_stream->bitArySize) {
    int * tmp;
    int i;
    tmp = (int *)malloc( sizeof(int)*vid_stream->bitArySize*2 );
    for (i = 0; i < vid_stream->bitAryIndex; i++) {
      tmp[i] = vid_stream->bitsPastSecAry[i];
    }
    free(vid_stream->bitsPastSecAry);
    vid_stream->bitsPastSecAry = tmp;
    vid_stream->bitArySize *= 2;
  }

  /* If you've got a second's worth of history then           */
  /* indicate that the array is valid                         */
  if (vid_stream->bitAryIndex >= vid_stream->frames_per_sec) {
    vid_stream->bitAryValid = 1;
  } else {
    vid_stream->bitAryValid = 0;
  }

  if (vid_stream->bitAryValid) {
    int sum = 0;
    int i;
    for ( i = (vid_stream->bitAryIndex - vid_stream->frames_per_sec); 
	 i < vid_stream->bitAryIndex; 
	 i++) {
      sum += vid_stream->bitsPastSecAry[i];
    }
    sum = (GAUGE_SIZE*sum)/(2*vid_stream->bit_rate*400);
    sprintf( command, "PlotBitsSec %d\n", sum);
    Tcl_Eval( interp, command );
  }    

  /* Set info on current frame  */
  sprintf( command, "set NumFrames \"Current Frame: %d\"\n", (vid_stream->bitAryIndex)-1 );
  Tcl_Eval( interp, command );
  sprintf( command, "set FrameCurrentlyDisplayed %d\n", (vid_stream->bitAryIndex)-1  );
  Tcl_Eval( interp, command );
  sprintf( command, "updateLastFrame\n");
  Tcl_Eval( interp, command );

  /* execute the current script and clear associated state */
  Tcl_Eval( interp, vid_stream->currentScript );
  Tcl_Eval( interp, "update\n");
  vid_stream->currentScriptLength = 0;
  (vid_stream->currentScript)[0] = '\0';
  vid_stream->currentSize = 0;

  /* display the picture */
  if ( ! ExecuteDisplay(vid_stream) )
    return FALSE;
  return TRUE;
}



/*
 *--------------------------------------------------------------
 *
 * ToggleBFlag --
 *
 *	Called to set no b frame processing flag.
 *
 * Results:
 *      No_B_Flag flag is toggled from present value to opposite value.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

void
ToggleBFlag()
{
  if (No_B_Flag) {
    No_B_Flag = 0;
  } else
    No_B_Flag = 1;
}




/*
 *--------------------------------------------------------------
 *
 * TogglePFlag --
 *
 *	Called to set no p frame processing flag.
 *
 * Results:
 *      No_P_Flag flag is toggled from present value to opposite value.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

void
TogglePFlag()
{
  if (No_P_Flag) {
    No_P_Flag = 0;
  } else
    No_P_Flag = 1;
}


HandleMPEGError(message)
    char *message;
{
    extern Tcl_Interp *interp;
    extern Window window;
    char command[256];

    Tcl_Eval(interp, "wm geometry . 300x100");
    Tcl_Eval(interp, "destroy .stopButton");
    Tcl_Eval(interp, "destroy .rewindButton");
    Tcl_Eval(interp, "destroy .playPauseButton");
    Tcl_Eval(interp, "destroy .loopButton");
    Tcl_Eval(interp, "destroy .fpsScale");
    Tcl_Eval(interp, "destroy .percentScale");
    Tcl_Eval(interp, "destroy .lineCanvas");
    Tcl_Eval(interp, "destroy .ditherButton");
    Tcl_Eval(interp, "label .errorTitleLabel -text \"Error in MPEG stream:\"");
    Tcl_Eval(interp, "place .errorTitleLabel -x 5 -y 40");
    sprintf(command, "label .errorLabel -text \"%s\"", message);
    Tcl_Eval(interp, command);
    Tcl_Eval(interp, "place .errorLabel -x 5 -y 70");
    XClearArea(display, window, 0, 0, 300, 100, 0 /* false */);

    handlingError = TRUE;

    if ( curVidStream != NULL )
	DestroyVidStream(&curVidStream);

    Tk_MainLoop();

  exit(1);
}


/*********************************************************
 * restoreVidStream()
 * 
 * pull latest state off state list and restore the vid stream
 * to it's previous state.
 **********************************************************/
int
restoreVidStream( vid_stream )
     VidStream * vid_stream;
{
  if ( vid_stream->stateListSize == 0 ) {
    /* there are no previous frames */
    return 0;
  } else {
    /* get the latest state, pull it off list */
    StateHolder * lastState = vid_stream->stateList->next;
    vid_stream->stateList->next->next->prev = vid_stream->stateList;
    vid_stream->stateList ->next = vid_stream->stateList->next->next;
    (vid_stream->stateListSize)--;
    
    /* if this is a GOP we just started, i.e. we have not displayed */
    /* an I-frame since this GOP was parsed, keep going back */
    /* or, if we just rewound, the top thing in the list is the */
    /* thing we just rewound to, so we want to go one further */
    if (startedGOP || justRewound) {
      if (vid_stream->stateListSize == 0) {
	/* no more - replace what we just took off and quit */
	vid_stream->stateListSize = 1;
	lastState->prev = vid_stream->stateList;
	lastState->next = vid_stream->stateList;
	vid_stream->stateList->prev = lastState;
	vid_stream->stateList->next = lastState;
	return 0;
      } else {
	free(lastState);
	lastState = vid_stream->stateList->next;
	vid_stream->stateList->next->next->prev = vid_stream->stateList;
	vid_stream->stateList ->next = vid_stream->stateList->next->next;
	(vid_stream->stateListSize)--;
      }
    }
	
    /* copy state into the vid_stream and destroy the state */
    fseek(input, lastState->fileOffset, 0);
    /* Note that even if we were at the end of the file */
    /* we no longer are */
    EOF_flag = 0;
    vid_stream->bit_offset = lastState->bit_offset;
    vid_stream->buffer = vid_stream->buf_start;
    vid_stream->buf_length = 0;
    vid_stream->bitAryIndex = lastState->bitAryIndex;
    totNumFrames = lastState->bitAryIndex;
    vid_stream->futureScript[0] = '\0';
    vid_stream->futureScriptLength = 0;
    vid_stream->future->locked &= ~FUTURE_LOCK;
    vid_stream->future = (PictImage *)NULL;
    vid_stream->futureSize = 0;
    vid_stream->currentScript[0] = '\0';
    vid_stream->currentScriptLength = 0;
    vid_stream->current = (PictImage *)NULL;
    vid_stream->currentSize = 0;
    vid_stream->past->locked &= ~PAST_LOCK;
    vid_stream->past = (PictImage *)NULL;
    return 1;
  }
}

/********************************************************************
*
* recordFrameNo()
*
* record the current frame number (which is just the index into the bit array)
* into the top saved-state.
**********************************************************************/
void
recordFrameNo( vid_stream )
     VidStream * vid_stream;
{
  assert( vid_stream->stateListSize != 0 );
  vid_stream->stateList->next->bitAryIndex = vid_stream->bitAryIndex;
}


