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

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

#include <time.h>

#define MYRECON

/* Declarations of functions. */
//static void init_stat_struct(Statval *);
//static void PrintOneStat(void);
//static void CollectStats(void);
//static unsigned int bitCountRead(void);
//static void StartTime(void);
//static void EndTime(void);
static int ParseSeqHead(VidStream *);
static int ParseGOP(VidStream *);
static int ParsePicture(VidStream *, TimeStamp);
static int ParseSlice(VidStream *);
static int ParseMacroBlock(VidStream *);
static void DoPictureDisplay(VidStream *);

#ifndef MYRECON
static void ReconIMBlock(VidStream *, int);
static void ReconPMBlock(VidStream *, int, int, int, int);
static void ReconBMBlock(VidStream *, int, int, int, int);
static void ReconBiMBlock(VidStream *, int, int, int, int, int, int);
#endif

static void ProcessSkippedPFrameMBlocks(VidStream *);
static void ProcessSkippedBFrameMBlocks(VidStream *);
static void ReconSkippedBlock16(unsigned char *, unsigned char *, int, int, int, int, int, int, int);
static void ReconSkippedBlock8(unsigned char *, unsigned char *, int, int, int, int, int, int, int);

extern int ditherType;

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

/* 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};

/* 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, lmaxy, cmaxx, cmaxy;


/*
  The following accounts for time and size  spent in various parsing acitivites
  if ANALYSIS has been defined.
*/
#ifdef ANALYSIS
#include "analysis.h"
#endif

double realTimeStart;
int totNumFrames = 0;

double ReadSysClock(void)
{
	unsigned int clock[2];

	(void) timer(clock);
	return clock[0] + clock[1] / 1000000.0;
}

void PrintTimeInfo(void)
{
	if (!quietFlag) {
		double spent = ReadSysClock() - realTimeStart;

		printf("\nReal Time Spent (After Initializations): %f secs.\n", spent);
		printf("Avg. Frames/Sec: %f\n", ((double) totNumFrames) / spent);

		printf("Frame size: %i*%i pixels\n", curVidStream->h_size, curVidStream->v_size);
		if(modeid != 0xffffffff)
			printf("ModeID of CyberGraphX screen: 0x%X\n", modeid);
	}
}



/*
 *--------------------------------------------------------------
 *
 * 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(int bufLength)
{
  int i,j;
  short *intra_quant_matrix, *intra_quant_matrix0, *intra_quant_matrix1;
  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;

  /* Create default intra matrix and qscale multiplication tables. */

  new->intra_quant_matrix_ptr[0] = (short*) malloc(32 * 8*8 * sizeof(short));
  clear64words(intra_quant_matrix = new->intra_quant_matrix_ptr[0]);
  for(j=1; j<32; j++) new->intra_quant_matrix_ptr[j] = (intra_quant_matrix+=64);

  intra_quant_matrix0 = intra_quant_matrix = new->intra_quant_matrix_ptr[1];

  for(i=0; i<64; i++)
	*intra_quant_matrix++ = default_intra_matrix[zigzag_direct[i]];

  for(j=2; j<32; j++)
  {
	intra_quant_matrix1 = new->intra_quant_matrix_ptr[1];
	for(i=0; i<64; i++) *intra_quant_matrix++ = (*intra_quant_matrix0++) + (*intra_quant_matrix1++);
  }

  /* Initialize non intra quantization matrix. */

  new->non_intra_default = TRUE;
  new->non_intra_quant_matrix_ptr[0] = NULL;

  /* Initialize pointers to image spaces. */

  new->current = new->past = new->future = NULL;
  for (i = 0; i < RING_BUF_SIZE; i++) new->ring[i] = NULL;

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

  /* display stuff */

  new->display_is_initialized = FALSE;

  /* Return structure. */

  return new;
}



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

  if (astream->ext_data) free(astream->ext_data);

  if (astream->user_data) free(astream->user_data);

  if (astream->group.ext_data) free(astream->group.ext_data);

  if (astream->group.user_data) free(astream->group.user_data);

  if (astream->picture.extra_info) free(astream->picture.extra_info);

  if (astream->picture.ext_data) free(astream->picture.ext_data);

  if (astream->picture.user_data) free(astream->picture.user_data);

  if (astream->slice.extra_info) free(astream->slice.extra_info);

  if (astream->buf_start) free(astream->buf_start);

  for (i = 0; i < RING_BUF_SIZE; i++) {
    if (astream->ring[i]) {
      DestroyPictImage(astream->ring[i]);
      astream->ring[i] = NULL;
    }
  }

  if (!astream->non_intra_default) free(astream->non_intra_quant_matrix_ptr[0]);

  if (astream->intra_quant_matrix_ptr[0]) free(astream->intra_quant_matrix_ptr[0]);

  free((char *) astream);
}




/*
 *--------------------------------------------------------------
 *
 * 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(unsigned int width, unsigned int height)
{
  extern int lores, ham6;
  PictImage *new;
  int size = (short)width * (short)height;

  /* Allocate memory space for new structure. */

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

  /* Allocate memory for image spaces. */
  if (ditherType == FULL_COLOR_DITHER) {
    new->display = (unsigned char *) malloc(ham6 ? size : (lores ? (size << 1) : (size << 2)));
  } else if (ditherType == CYBERGFX_DITHER) {
		new->display = (unsigned char *) malloc(size * 4);	/* one byte for each of R, G and B  and A :)*/
	} else {
    new->display = (unsigned char *) malloc(size);
  }

  new->luminance = (unsigned char *) malloc(size);
  new->Cr = (unsigned char *) malloc(size >> 2);
  new->Cb = (unsigned char *) malloc(size >> 2);

  /* 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(PictImage *apictimage)
{
  if (apictimage->luminance) free(apictimage->luminance);
  if (apictimage->Cr) free(apictimage->Cr);
  if (apictimage->Cb) free(apictimage->Cb);
  if (apictimage->display) free(apictimage->display);
  free(apictimage);
}

/*
 *--------------------------------------------------------------
 *
 * mpegInitVidRsrc --
 *
 *      reset first to 1
 *
 * gonna check this thingy out !!!!
 *
 *--------------------------------------------------------------
 */
static int first = 1;

void mpegInitVidRsrc(void)
{
	first = 1;
}


/*
 *--------------------------------------------------------------
 *
 * 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 Boolean value is returned - 0 is end of picture or error
 *
 * Side effects:
 *      Bit stream is irreversibly parsed. If a picture is completed,
 *      a function is called to display the frame at the correct time.
 *
 *--------------------------------------------------------------
 */

int mpegVidRsrc(TimeStamp time_stamp, VidStream *vid_stream)
{
  unsigned int data;
  int i, status;

  /* If vid_stream is null, create new VidStream structure.  MR: ????? */

	if(!handle_window())
		return 0;

  if (vid_stream == NULL) return 0;

  /*
   * 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;
  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) {
    next_start_code();
    if (show_bits32(data) != SEQ_START_CODE) {
      fprintf(stderr, "This is not an MPEG stream.\n");
      DestroyVidStream(curVidStream);
      exit(1);
    }
    first = 0;
  }

  /*
   * Process according to start code (or parse macroblock if not a start code
   * at all.
   */

  switch (show_bits32(data)) {

  case SEQ_END_CODE:

    /* Display last frame. */

    if (vid_stream->future != NULL) {
      vid_stream->current = vid_stream->future;
      ExecuteDisplay(vid_stream);
    }
    
    /* Sequence done. Do the right thing. return False. */

    if (!quietFlag) {
      fprintf(stderr, "\nDone!\n");
    }

#ifdef ANALYSIS
    PrintAllStats();
#endif
    PrintTimeInfo();

    if (loopFlag) longjmp(env, 1);

    DestroyVidStream(curVidStream);
    return 0;
    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. */

    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) {
      next_start_code();
      fprintf(stderr, "Skipping picture...");
      while ((show_bits32(data)) != PICTURE_START_CODE) {
	if (data == GOP_START_CODE || data == SEQ_END_CODE)
	  break;
	flush_bits(24);
	next_start_code();
      }
      fprintf(stderr, "Done.\n");
      goto done;
    } else if (status != PARSE_OK) goto error;


    if (ParseSlice(vid_stream) != PARSE_OK) goto error;
    break;

  case USER_START_CODE:	// there has been one stream with an error in a GOP header
    flush_bits32;
    if (vid_stream->group.user_data) {
      free(vid_stream->group.user_data);
      vid_stream->group.user_data = NULL;
    }
    vid_stream->group.user_data = get_ext_data();
    break;
  case EXT_START_CODE:
    flush_bits32;
    if (vid_stream->group.ext_data) {
      free(vid_stream->group.ext_data);
      vid_stream->group.ext_data = NULL;
    }
    vid_stream->group.ext_data = get_ext_data();
    break;

  default:
    /* Check for slice start code. */

    if ((data >= SLICE_MIN_START_CODE) && (data <= SLICE_MAX_START_CODE))
      if (ParseSlice(vid_stream) != PARSE_OK)	/* Slice start code. Parse slice header. */
	goto error;

    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 ((show_bitsn(23,data)) != 0x00000000) {

      /* Not start code. Parse Macroblock. */

      if (ParseMacroBlock(vid_stream) != PARSE_OK)
	goto error;

#ifdef ANALYSIS
      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);
	ExecuteDisplay(vid_stream);
      }
#endif

    } 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)) {

#ifdef ANALYSIS
	EndTime();
	stat_a[0].totsize += bitCountRead() - pictureSizeCount;
	if (showEachFlag)
	  PrintOneStat();

	CollectStats();
#endif

	DoPictureDisplay(vid_stream);
      }
      break;
    }
  }

  /* 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 1;
}


/*
 *--------------------------------------------------------------
 *
 * 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(VidStream *vid_stream)
{
  unsigned int data;
  int i;

  /* Flush off sequence start code. */

  flush_bits32;

  /* Get horizontal size of image space. */

  get_bits12(vid_stream->h_size);

  /* Get vertical size of image space. */

  get_bits12(vid_stream->v_size);

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

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

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

  lmaxx = (vid_stream->mb_width<<4)-1;
  lmaxy = (vid_stream->mb_height<<4)-1;
  cmaxx = (vid_stream->mb_width<<3)-1;
  cmaxy = (vid_stream->mb_height<<3)-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 << 4, vid_stream->mb_height << 4);
  }

  /* Parse of aspect ratio code. */

  get_bits4(vid_stream->aspect_ratio);

  /* Parse off picture rate code. */

  get_bits4(vid_stream->picture_rate);

  /* Parse off bit rate. */

  get_bits18(vid_stream->bit_rate);

  /* Flush marker bit. */

  flush_bits(1);

  /* Parse off vbv buffer size. */

  get_bits10(vid_stream->vbv_buffer_size);

  /* Parse off contrained parameter flag. */

  get_bits1(vid_stream->const_param_flag);

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

  if (get_bits1(data))
    new_matrix(vid_stream->intra_quant_matrix_ptr[1]);

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

  if (get_bits1(data))
  {
    if(vid_stream->non_intra_default)
    {
	unsigned short *intra_quant_matrix;

	vid_stream->non_intra_quant_matrix_ptr[0] = (short*) malloc(32 * 8*8 * sizeof(short));
	clear64words(intra_quant_matrix = vid_stream->non_intra_quant_matrix_ptr[0]);
	for(i=1; i<32; i++) vid_stream->non_intra_quant_matrix_ptr[i] = (intra_quant_matrix+=64);

	vid_stream->non_intra_default = FALSE;
    }

    new_matrix(vid_stream->non_intra_quant_matrix_ptr[1]);
  }

  /* Go to next start code. */

  next_start_code();

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

  if ((show_bits32(data)) == EXT_START_CODE) {
    flush_bits32;
    if (vid_stream->ext_data) {
      free(vid_stream->ext_data);
      vid_stream->ext_data = NULL;
    }
    vid_stream->ext_data = get_ext_data();
    show_bits32(data);
  }

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

  if (data == USER_START_CODE) {
    flush_bits32;
    if (vid_stream->user_data) {
      free(vid_stream->user_data);
      vid_stream->user_data = NULL;
    }
    vid_stream->user_data = get_ext_data();
  }

  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(VidStream *vid_stream)
{
  unsigned int data;

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

  flush_bits32;

  /* Parse off drop frame flag. */

  get_bits1(vid_stream->group.drop_flag);

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

  get_bits5(vid_stream->group.tc_hours);

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

  get_bits6(vid_stream->group.tc_minutes);

  /* Flush marker bit. */

  flush_bits(1);

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

  get_bits6(vid_stream->group.tc_seconds);

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

  get_bits6(vid_stream->group.tc_pictures);

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

  get_bits1(vid_stream->group.closed_gop);
  get_bits1(vid_stream->group.broken_link);

  /* Goto next start code. */
  next_start_code();

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

  if ((show_bits32(data)) == EXT_START_CODE) {
    flush_bits32;
    if (vid_stream->group.ext_data) {
      free(vid_stream->group.ext_data);
      vid_stream->group.ext_data = NULL;
    }
    vid_stream->group.ext_data = get_ext_data();
    show_bits32(data);
  }

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

  if (data == USER_START_CODE) {
    flush_bits32;
    if (vid_stream->group.user_data) {
      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(VidStream *vid_stream, TimeStamp time_stamp)
{
  unsigned int data;
  int i;

  /* Flush header start code. */
  flush_bits32;

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

  /* Parse of picture type. */
  get_bits3(vid_stream->picture.code_type);

  if ((vid_stream->picture.code_type == B_TYPE) &&
      (No_B_Flag ||
	   (vid_stream->past == NULL) ||
	   (vid_stream->future == NULL)))
    return SKIP_PICTURE;

  if ((vid_stream->picture.code_type == P_TYPE) &&
      (No_P_Flag || (vid_stream->future == NULL)))
    return SKIP_PICTURE;

#ifdef ANALYSIS
  StartTime();
  stat_a[0].frametype = vid_stream->picture.code_type;
  stat_a[0].number = 1;
  stat_a[0].totsize = 45;
  pictureSizeCount = bitCountRead();
#endif

  /* Parse off vbv buffer delay value. */

  get_bits16(vid_stream->picture.vbv_delay);

  /* 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(vid_stream->picture.full_pel_forw_vector);

    /* Parse of and decode forw_r_code into forw_r_size and forw_f. */

    get_bits3(vid_stream->picture.forw_r_size) - 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(vid_stream->picture.full_pel_back_vector);

    /* Parse off and decode back_r_code into back_r_size and back_f. */

    get_bits3(vid_stream->picture.back_r_size) - 1;
    vid_stream->picture.back_f = (1 << vid_stream->picture.back_r_size);
  }

  /* Get extra bit picture info. */

  if (vid_stream->picture.extra_info) {
    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 ((show_bits32(data)) == EXT_START_CODE) {
    flush_bits32;
    if (vid_stream->picture.ext_data) {
      free(vid_stream->picture.ext_data);
      vid_stream->picture.ext_data = NULL;
    }
    vid_stream->picture.ext_data = get_ext_data();
    show_bits32(data);
  }

  /* If start code is user start code, parse off user data. */

  if (data == USER_START_CODE) {
    flush_bits32;
    if (vid_stream->picture.user_data) {
      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) {
      perror("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(VidStream *vid_stream)
{
  /* Flush slice start code. */

  flush_bits(24);

  /* Parse off slice vertical position. */

  get_bits8(vid_stream->slice.vert_pos);

  /* Parse off quantization scale. */

  get_bits5(vid_stream->slice.quant_scale);

  /* Parse off extra bit slice info. */

  if (vid_stream->slice.extra_info) {
    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 = ((short)(vid_stream->slice.vert_pos - 1) * (short)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(VidStream *vid_stream)
{
	int addr_incr, mask, i;
	int recon_right_for, recon_down_for, recon_right_back, recon_down_back, zero_block_flag;
	int mb_data;

#ifdef ANALYSIS
	mbSizeCount = bitCountRead();
#endif

	/*
	 * 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 > ((short)vid_stream->mb_height * (short)vid_stream->mb_width - 1))
		return SKIP_TO_START_CODE;

	/*
	 * 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_data, vid_stream->mblock.mb_intra);
			break;
		case P_TYPE:
			DecodeMBTypeP(mb_data, vid_stream->mblock.mb_intra);
			break;
		case B_TYPE:
			DecodeMBTypeB(mb_data, vid_stream->mblock.mb_intra);
			break;
	}

	/* If quantization flag set, parse off new quantization scale. */
	if (mb_data&MB_quant) {
		get_bits5(vid_stream->slice.quant_scale);
	}

  /* If forward motion vectors exist... */
  if (mb_data&MB_motion_forw) {

    /* 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, vid_stream->mblock.motion_h_forw_r);
    }
    /* 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, vid_stream->mblock.motion_v_forw_r);
    }
  }
  /* If back motion vectors exist... */
  if (mb_data&MB_motion_back) {

    /* 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, vid_stream->mblock.motion_h_back_r);
    }
    /* 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, vid_stream->mblock.motion_v_back_r);
    }
  }
#ifdef ANALYSIS
	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_data&(MB_motion_back | MB_motion_forw)) == (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_data&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);
	}
#endif

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

#ifdef ANALYSIS
	mbCBPPtr[vid_stream->mblock.cbp]++;
#endif

  /* Reconstruct motion vectors depending on picture type. */
  if (vid_stream->picture.code_type == P_TYPE) {

    /*
     * If no forw motion vectors, reset previous and current vectors to 0.
     */

    if (!(mb_data&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_data&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_data&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.
       */

      if(mb_data<0)
	vid_stream->mblock.bpict_past_forw = vid_stream->mblock.bpict_past_back = -1;
      else {
	vid_stream->mblock.bpict_past_forw = mb_data&MB_motion_forw;
	vid_stream->mblock.bpict_past_back = mb_data&MB_motion_back;
      }

    }
  }

  /* For each possible block in macroblock. */
  if (ditherType == GRAY_DITHER || ditherType == CYBERGFXGRAY_DITHER) {
    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_data&(MB_motion_forw | MB_motion_back)) == (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_data&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_data&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 {
      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_data&(MB_motion_forw | MB_motion_back)) == (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_data&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_data&MB_motion_back) {
	  ReconBMBlock(vid_stream, i, recon_right_back, recon_down_back,
		       zero_block_flag);
	}
      }
  }

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

#ifdef ANALYSIS
  *mbSizePtr += bitCountRead() - mbSizeCount;
#endif

  return PARSE_OK;
}

#ifndef MYRECON

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

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

  row_size = vid_stream->mb_width;
  /* Calculate macroblock row and column from address. */
  row = vid_stream->mblock.mb_address / row_size;
  col = vid_stream->mblock.mb_address % row_size;

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

  if (bnum < 4) {

    /* Calculate row and col values for upper left pixel of block. */
    row <<= 4;
    col <<= 4;
    if (bnum > 1) row += 8;
    if (bnum & 1) col += 8;

    /* Set dest to luminance plane of current pict image. */
    dest = vid_stream->current->luminance;

    /* Establish row size. */
    row_size *= 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 <<= 3;

    /* Calculate row,col for upper left pixel of block. */
    row <<= 3;
    col <<= 3;
  }
  /* 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 <<= 3;

    /* Calculate row,col for upper left pixel value of block. */
    row <<= 3;
    col <<= 3;
  }

  /*
   * For each pixel in block, set to cropped reconstructed value from inverse dct.
   */
  IM_reconstruct(dest + row * row_size + col, &vid_stream->block.dct_recon[0][0], row_size);
}


/* MR
 *--------------------------------------------------------------
 *
 * ReconPMBlock --
 *
 *	Reconstructs forward predicted macroblocks.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */
static void ReconPMBlock(VidStream *vid_stream, int bnum, int recon_right_for, int recon_down_for, int zflag)
{
  int row, col, row_size;
  unsigned char *dest, *past, *rindex2;
  int right_half_for, down_half_for;

  row_size = vid_stream->mb_width;
  /* Calculate macroblock row and column from address. */
  row = vid_stream->mblock.mb_address / row_size;
  col = vid_stream->mblock.mb_address % row_size;

  if (bnum < 4) {
    /* Calculate right_for, down_for motion vectors. */

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

    /* 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) past = vid_stream->past->luminance;
    } else {
      /* Set predicitive frame to current future frame. */
      if (vid_stream->future) past = vid_stream->future->luminance;
    }

    /* Establish row size. */
    row_size <<= 4;

    /* Calculate row,col of upper left pixel in block. */
    row <<= 4;
    col <<= 4;
    if (bnum > 1) row += 8;
    if (bnum & 1) col += 8;

  }
  /* Otherwise, block is NOT luminance block, ... */
  else {
    /* Construct motion vectors. */
    right_half_for = (recon_right_for & 0x2)>>1;
    down_half_for = recon_down_for & 0x2;
    recon_right_for >>= 2;
    recon_down_for >>= 2;

    /* Establish row size. */
    row_size <<= 3;

    /* Calculate row,col of upper left pixel in block. */
    row <<= 3;
    col <<= 3;

    /* 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) past = vid_stream->past->Cr;
      } else {
	if (vid_stream->future) 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) past = vid_stream->past->Cb;
      } else {
	if (vid_stream->future) past = vid_stream->future->Cb;
      }
    }
  }

  /* For each pixel in block... */
    dest += ((short)row * (short)row_size) + col;
    past += (short)(row + recon_down_for) * (short)row_size + col + recon_right_for;

    /*
     * Calculate predictive pixel value based on motion vectors and copy to
     * dest plane.
     */

    if ((!down_half_for) && (!right_half_for)) {
      if (!zflag)
	BMcm_reconstruct(dest, past, &(vid_stream->block.dct_recon[0][0]), row_size);
      else
	BM_reconstruct(dest, past, row_size);
    } else {
      rindex2 = past + right_half_for + (down_half_for?row_size:0);
      if (!zflag)
	BIMcm_reconstruct(dest, past, rindex2, &(vid_stream->block.dct_recon[0][0]), row_size);
      else
	BIM_reconstruct(dest, past, rindex2, row_size);
    }

}


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

static void ReconBMBlock(VidStream *vid_stream, int bnum, int recon_right_back, int recon_down_back, int zflag)
{
  int row, col, row_size;
  unsigned char *dest, *future, *rindex2;
  int right_half_back, down_half_back;

  row_size = vid_stream->mb_width;
  /* Calculate macroblock row and column from address. */
  row = vid_stream->mblock.mb_address / row_size;
  col = vid_stream->mblock.mb_address % row_size;

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

  if (bnum < 4) {
    /* Calculate right_back, down_back motion vectors. */
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;
    recon_right_back >>= 1;
    recon_down_back >>= 1;

    /* 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) future = vid_stream->future->luminance;

    /* Establish row size. */
    row_size <<= 4;

    /* Calculate row,col of upper left pixel in block. */
    row <<= 4;
    col <<= 4;
    if (bnum > 1) row += 8;
    if (bnum & 1) col += 8;

  }
  /* Otherwise, block is NOT luminance block, ... */
  else {
    /* Construct motion vectors. */
    right_half_back = (recon_right_back & 0x2)>>1;	/* used as integer down there */
    down_half_back = recon_down_back & 0x2;
    recon_right_back >>= 2;
    recon_down_back >>= 2;

    /* Establish row size. */
    row_size <<= 3;

    /* Calculate row,col of upper left pixel in block. */
    row <<= 3;
    col <<= 3;

    /* 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) 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) future = vid_stream->future->Cb;
    }
  }

  /* For each pixel in block do... */
    dest += ((short)row * (short)row_size) + col;
    future += (short)(row + recon_down_back) * (short)row_size + col + recon_right_back;

    if ((!right_half_back) && (!down_half_back)) {
      if (!zflag)
	BMcm_reconstruct(dest, future, &(vid_stream->block.dct_recon[0][0]), row_size);
      else
	BM_reconstruct(dest, future, row_size);
    } else {
      rindex2 = future + right_half_back + (down_half_back?row_size:0);
      if (!zflag)
	BIMcm_reconstruct(dest, future, rindex2, &(vid_stream->block.dct_recon[0][0]), row_size);
      else
	BIM_reconstruct(dest, future, rindex2, row_size);
    }
}


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

static void ReconBiMBlock(VidStream *vid_stream, int bnum,
			  int forw_col_start, int forw_row_start,
			  int back_col_start, int back_row_start,
			  int zflag)
{
  int row, col, row_size;
  unsigned char *dest, *past, *future;

  row_size = vid_stream->mb_width;
  /* Calculate macroblock row and column from address. */
  row = vid_stream->mblock.mb_address / row_size;
  col = vid_stream->mblock.mb_address % row_size;

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

  if (bnum < 4) {
    /* 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) past = vid_stream->past->luminance;

    /*If future frame exists, set future to luminance plane of future frame. */
    if (vid_stream->future) future = vid_stream->future->luminance;

    /* Establish row size. */
    row_size <<= 4;

    /* Calculate row,col of upper left pixel in block. */
    row <<= 4;
    col <<= 4;
    if (bnum > 1) row += 8;
    if (bnum & 1) col += 8;

    forw_col_start = (forw_col_start>>1) + col;
    forw_row_start = (forw_row_start>>1) + row;
    back_col_start = (back_col_start>>1) + col;
    back_row_start = (back_row_start>>1) + row;

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

  else {
    /* Establish row size. */
    row_size <<= 3;

    /* Calculate row,col of upper left pixel in block. */
    row <<= 3;
    col <<= 3;

    forw_col_start = (forw_col_start>>2) + col;
    forw_row_start = (forw_row_start>>2) + row;
    back_col_start = (back_col_start>>2) + col;
    back_row_start = (back_row_start>>2) + row;

    /* 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) past = vid_stream->past->Cr;

      /* If future frame exists, set future to Cr plane of future image. */
      if (vid_stream->future) 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) past = vid_stream->past->Cb;

      /* If future frame exists, set future to Cb plane of future frame. */
      if (vid_stream->future) future = vid_stream->future->Cb;
    }
  }

  dest += (short)row * (short)row_size + col;
  past += (short)forw_row_start  * (short)row_size + forw_col_start;
  future += (short)back_row_start * (short)row_size + back_col_start;

  if (!zflag)
	BIMcm_reconstruct(dest, past, future, &(vid_stream->block.dct_recon[0][0]), row_size);
  else
	BIM_reconstruct(dest, past, future, row_size);
}
#endif


/* MR
 *--------------------------------------------------------------
 *
 * 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(VidStream *vid_stream)
{
  int row_size, row, col, addr, off;

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

  row_size = vid_stream->mb_width << 4;

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

  addr = vid_stream->mblock.past_mb_addr + 1;
  row = (addr / vid_stream->mb_width)<<4;
  col = (addr % vid_stream->mb_width)<<4;

  for (; addr < vid_stream->mblock.mb_address; addr++, col+=16) {

    /* Calculate macroblock row and col. */
    if(col>=row_size) col=0, row+=16;

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

    off = ((short)row * (short)row_size);
    PMB1_reconstruct(vid_stream->current->luminance + off + col,
		     vid_stream->future->luminance + off + col,
		     row_size >> 2);

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

    off >>= 2;
    off += (col>>1);
    PMB2_reconstruct(vid_stream->current->Cr + off,
		     vid_stream->current->Cb + off,
		     vid_stream->future->Cr + off,
		     vid_stream->future->Cb + off,
		     row_size >> 3);
  }

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




/* MR
 *--------------------------------------------------------------
 *
 * 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(VidStream *vid_stream)
{
  int row_size, half_row, row, col, ccol, crow;
  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;
  int recon_right_for, recon_down_for, recon_right_back, recon_down_back;
  int c_right_for, c_down_for, 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 h;
  char *lum;

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

  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_forw_vector) {
    recon_right_for <<= 1;
    recon_down_for <<= 1;
  }

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


  /* If only one motion vector, do display copy, else do full
     calculation. 
  */
  /* Calculate motion vectors. */
  
  if (vid_stream->mblock.bpict_past_forw) {
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;
    recon_right_for >>= 1;
    recon_down_for >>= 1;
    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_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;    
    recon_right_back >>= 1;
    recon_down_back >>= 1;
    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... */
  addr = vid_stream->mblock.past_mb_addr + 1;
  row = (addr / vid_stream->mb_width)<<4;
  col = (addr % vid_stream->mb_width)<<4;

  for (; addr < vid_stream->mblock.mb_address; addr++, col+=16) {
    
    /* Calculate macroblock row and col. */
    if(col>=row_size) col=0, row+=16;

    /* Calculate upper left pixel row,col for luminance plane. */
    crow = row >> 1;
    ccol = col >> 1;
    
    /* If forward predicted, calculate prediction values. */
    
    if (vid_stream->mblock.bpict_past_forw) {
      
      ReconSkippedBlock16(vid_stream->past->luminance, forw_lum,
			  row, col, row_size, recon_right_for, recon_down_for,
			  right_half_for, down_half_for);
      ReconSkippedBlock8(vid_stream->past->Cr, forw_cr, crow, ccol, half_row,
			 c_right_for, c_down_for, c_right_half_for, c_down_half_for);
      ReconSkippedBlock8(vid_stream->past->Cb, forw_cb, crow, ccol, half_row,
			 c_right_for, c_down_for, c_right_half_for, c_down_half_for);
    }
    /* If back predicted, calculate prediction values. */
    
    if (vid_stream->mblock.bpict_past_back) {
      ReconSkippedBlock16(vid_stream->future->luminance, back_lum,
			  row, col, row_size, recon_right_back, recon_down_back,
			  right_half_back, down_half_back);
      ReconSkippedBlock8(vid_stream->future->Cr, back_cr, crow, ccol, half_row,
			 c_right_back, c_down_back,
			 c_right_half_back, c_down_half_back);
      ReconSkippedBlock8(vid_stream->future->Cb, back_cb, crow, ccol, half_row,
			 c_right_back, c_down_back,
			 c_right_half_back, c_down_half_back);
    }

    h = ((short)crow * (short)half_row) + ccol;
    lum = (vid_stream->current->luminance + ((short)row * (short)row_size) + col);

    if (vid_stream->mblock.bpict_past_forw && !vid_stream->mblock.bpict_past_back) {

      PSB1_reconstruct(lum, forw_lum, row_incr);

      PSB2_reconstruct(vid_stream->current->Cr + h, vid_stream->current->Cb + h,
		       forw_cr, forw_cb, half_row_incr);

    } else if (vid_stream->mblock.bpict_past_back && !vid_stream->mblock.bpict_past_forw) {

      PSB1_reconstruct(lum, back_lum, row_incr);

      PSB2_reconstruct(vid_stream->current->Cr + h, vid_stream->current->Cb + h,
		       back_cr, back_cb, half_row_incr);
    } else {
      PSB3_reconstruct(lum, forw_lum, back_lum, row_size);

      PSB4_reconstruct(vid_stream->current->Cr + h, vid_stream->current->Cb + h,
		       forw_cr, back_cr, forw_cb, back_cb, half_row);
    }    
  }
}



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

static
void ReconSkippedBlock16(unsigned char *source, unsigned char *dest, int row, int col, int row_size,
			 int right, int down, int right_half, int down_half)
{
  source += ((short)(row + down) * (short)row_size) + col + right;

  if ((!right_half) && (!down_half))
    RSB1_reconstruct(dest, source, row_size);
  else 
    RSB2_reconstruct(dest, source, source + right_half + ((short)row_size * (short)down_half), row_size);
}

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

static
void ReconSkippedBlock8(unsigned char *source, unsigned char *dest, int row, int col, int row_size,
			int right, int down, int right_half, int down_half)
{
  source += ((short)(row + down) * (short)row_size) + col + right;

  if ((!right_half) && (!down_half))
    RSB3_reconstruct(dest, source, row_size);
  else 
    RSB4_reconstruct(dest, source, source + right_half + ((short)row_size * (short)down_half), 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 void DoPictureDisplay(VidStream *vid_stream)
{

  /* init display, if needed */


  if(!vid_stream->display_is_initialized)
  {
    ResizeDisplay(vid_stream->h_size, vid_stream->v_size);
    vid_stream->display_is_initialized = TRUE;
  }
  /* Convert to colormap space and dither. */

	if(ditherType == CYBERGFXGRAY_DITHER)
		vid_stream->current->display = vid_stream->current->luminance;
	else
	  DoDitherImage(vid_stream->current->luminance, vid_stream->current->Cr,
		vid_stream->current->Cb, vid_stream->current->display,
		vid_stream->mb_height <<4, vid_stream->mb_width <<4);

  /* Update past and future references if needed. */

  if ((vid_stream->picture.code_type == I_TYPE) || (vid_stream->picture.code_type == P_TYPE)) {
    if (vid_stream->future == NULL) {
      vid_stream->future = vid_stream->current;
      vid_stream->future->locked |= FUTURE_LOCK;
    } else {
      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;
      vid_stream->future->locked |= FUTURE_LOCK;
      vid_stream->current = vid_stream->past;
      ExecuteDisplay(vid_stream);
    }
  } else
    ExecuteDisplay(vid_stream);

}



/*
 *--------------------------------------------------------------
 *
 * 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(void)
{
  No_B_Flag ^= 0xffff;
}


/*
 *--------------------------------------------------------------
 *
 * 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(void)
{
  No_P_Flag ^= 0xffff;
}
