ftp.nice.ch/pub/next/graphics/movie/VideoStream.1.0.NIHS.bs.tar.gz#/VideoStreamV1.0/Source/mpegDecodeSrc/video.c

This is video.c in view mode; [Download] [Up]

/*
 * Copyright (c) 1992 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.
 */
#include <stdio.h>

#include <sys/time.h>

#include "video.h"
#include "util.h"

/* Declarations of functions. */
static void ReconIMBlock();
static void ReconPMBlock();
static void ReconBMBlock();
static void ReconBiMBlock();
static void ReconSkippedBlock();
static void DoPictureDisplay();
static int ParseSeqHead();
static int ParseGOP();
static int ParsePicture();
static int ParseSlice();
static int ParseMacroBlock();
static int ProcessSkippedPFrameMBlocks();
static int ProcessSkippedBFrameMBlocks();

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


double realTimeStart;
int totNumFrames = 0;

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

extern int writeToStdout;

void PrintTimeInfo() 
{
	double spent;
	
	if(!writeToStdout) {
		spent = ReadSysClock()-realTimeStart;
	
		printf( "\nReal Time Spent (After Initializations): %f secs.\n", spent);
		printf( "Avg. Frames/Sec: %f\n", ((double) totNumFrames) / spent);
	}
}

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


  /* Return structure. */

  return new;
}

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

  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);

  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]);
    }
  }

  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(width, height)
     int width, height;
{
  PictImage *new;

  /* Allocate memory space for new structure. */

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


  /* Allocate memory for image spaces. */

    {
    shmemerror:
  
      new->display = (unsigned char *) malloc(width*height*4);
  }

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


/*
 *--------------------------------------------------------------
 *
 * 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;
{
  static int first = 1;
  unsigned  int data;
  int i, status;

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

  if (vid_stream == NULL) {
    return NULL;
  }

  /* 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;
  curBits = *curVidStream->buffer;
  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();
    show_bits32(&data);
    if (data != SEQ_START_CODE) {
      fprintf(stderr, "This is not an MPEG stream.");
      DestroyVidStream(curVidStream);
      exit(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:

    /*Sequence done. Do the right thing. For right now, exit. */

    fprintf(stderr, "\nDone!\n");
    PrintTimeInfo();

    if (loopFlag) longjmp(env, 1);

    DestroyVidStream(curVidStream);
    exit();
    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 (!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();
      }
      fprintf(stderr, "Done.\n");
      goto done;
    }
    else if (status != PARSE_OK)
      goto error;

    if (ParseSlice(vid_stream) != PARSE_OK)
      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;
    }

    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)
        goto error;

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

        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 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;
  int i;

  /* Flush off sequence start code. */

  flush_bits(32);

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

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

  /* Parse off bit rate. */

  get_bits18(&data);
  vid_stream->bit_rate = data;

  /* 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_bits(32);
    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_bits(32);
    if (vid_stream->user_data != NULL) {
      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(vid_stream)
VidStream *vid_stream;
{
  unsigned  int data;

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

  flush_bits(32);

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

  /* Flush header start code. */
  flush_bits(32);

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

  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;

  /* 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_bits(32);

    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_bits(32);
    
    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) {
      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(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 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 sparseFlag;

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

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

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

  /* For each possible block in macroblock. */

  for (i=0; i<6; i++) {

    /* If block exists... */

    if ((vid_stream->mblock.mb_intra) || 
        (vid_stream->mblock.cbp & (1<<(5-i)))) {

      /* Unset Zero Block Flag. */

      zero_block_flag = 0;
    
	
	/* Parse and reconstruct block. */
	
	ParseReconBlock(i, &sparseFlag);
	
	/* Run through inverse DCT. */
	
	j_rev_dct(vid_stream->block.dct_recon, sparseFlag); 
    }

    /* Otherwise, set zero block flag. */

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

  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, cc;
  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. 
  */
/* old code */
/*
  for (rr =0; rr < 8; rr++) {
    for (cc = 0; cc < 8; cc++) {
      if (vid_stream->block.dct_recon[rr][cc] < 0) {
        dest[row*row_size+col] = 0;
      }
      else if (vid_stream->block.dct_recon[rr][cc] > 255) {
	dest[row*row_size+col] = 255;
      }
      else {
        dest[row*row_size+col] = vid_stream->block.dct_recon[rr][cc];
      }
      col++;
    }
    col -= 8;
    row++;*/
/* patch -pk */
   {
       short		*sp = &vid_stream->block.dct_recon[0][0], sv;
       dest += row * row_size + col;
       for(rr = 0; rr < 8; rr++, sp += 8, dest += row_size){
 	  if((sv = sp[0]) < 0){
 	      dest[0] = 0; 
 	  } else if(sv > 255){ 
 	      dest[0] = 255;
 	  } else {
 	      dest[0] = sv;
 	  } 
 	  if((sv = sp[1]) < 0){
 	      dest[1] = 0;
 	  } else if(sv > 255){
 	      dest[1] = 255;
 	  } else {
 	      dest[1] = sv;
 	  } 
 	  if((sv = sp[2]) < 0){
 	      dest[2] = 0;
 	  } else if(sv > 255){
 	      dest[2] = 255;
 	  } else {
 	      dest[2] = sv;
 	  } 
 	  if((sv = sp[3]) < 0){
 	      dest[3] = 0;
 	  } else if(sv > 255){
 	      dest[3] = 255;
 	  } else {
 	      dest[3] = sv;
 	  } 
 	  if((sv = sp[4]) < 0){
 	      dest[4] = 0;
 	  } else if(sv > 255){
 	      dest[4] = 255;
 	  } else {
 	      dest[4] = sv;
 	  } 
 	  if((sv = sp[5]) < 0){
 	      dest[5] = 0;
 	  } else if(sv > 255){
 	      dest[5] = 255;
 	  } else {
 	      dest[5] = sv;
 	  } 
 	  if((sv = sp[6]) < 0){
 	      dest[6] = 0;
 	  } else if(sv > 255){
 	      dest[6] = 255;
 	  } else {
 	      dest[6] = sv;
 	  } 
 	  if((sv = sp[7]) < 0){
 	      dest[7] = 0;
 	  } else if(sv > 255){
 	      dest[7] = 255;
 	  } else {
 	      dest[7] = sv;
 	  } 
        }
  }
}


/*
 *--------------------------------------------------------------
 *
 * 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, cc;
  unsigned char *dest, *past;
  int right_for, down_for, right_half_for, down_half_for;
  unsigned char *rindex1, *rindex2;
  unsigned char *index;
  int val;
  short int *blockvals;

  /* 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) {

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

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

  else {

    /* Construct motion vectors. */

    right_for = recon_right_for >> 2;
    down_for = recon_down_for >> 2;
    right_half_for = recon_right_for & 0x2;
    down_half_for = recon_down_for & 0x2;

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

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

  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)) {
    for (rr = 0; rr < 8; rr++) {
      for (cc = 0; cc < 8; cc++) {
	val = (int)(*rindex1++);
	if (!zflag) {
	  val += (int)(*blockvals++);
	}
	
	if (val > 255) val = 255;
	else if (val < 0) val = 0;
	*index++ = val;
      }
      index += row_size - 8;
      rindex1 += row_size - 8;
    }   
  }
  else {
    rindex2 = rindex1 + right_half_for + (down_half_for * row_size);
    for (rr = 0; rr < 8; rr++) {
      for (cc = 0; cc < 8; cc++) {
	val = ((*rindex1++) + (*rindex2++)) >> 1;
	
	if (!zflag) {
	  val += *blockvals++;
	}
	
	if (val > 255) val = 255;
	else if (val < 0) val = 0;
	*index++ = val;
      }
      index += row_size - 8;
      rindex1 += row_size - 8;
      rindex2 += row_size - 8;
    }   
  }
}

/*
 *--------------------------------------------------------------
 *
 * 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, cc;
  unsigned char *dest, *future;
  int right_back, down_back, right_half_back, down_half_back;
  unsigned char  *rindex1, *rindex2;
  unsigned char  *index;
  int val;
  short int *blockvals;

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

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

  else {

    /* Construct motion vectors. */

    right_back = recon_right_back >> 2;
    down_back = recon_down_back >> 2;
    right_half_back = recon_right_back & 0x2;
    down_half_back = recon_down_back & 0x2;

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

  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)) {
    for (rr = 0; rr < 8; rr++) {
      for (cc = 0; cc < 8; cc++) {

	val = *rindex1++;

	if (!zflag) {
	  val += *blockvals++;
	}

	if (val > 255) val = 255;
	else if (val < 0) val = 0;
	*index++ = val;
	
      }
      index += row_size - 8;
      rindex1 += row_size - 8;
    }
  }
  else {
    rindex2 = rindex1 + right_half_back + (down_half_back*row_size);
    for (rr = 0; rr < 8; rr++) {
      for (cc = 0; cc < 8; cc++) {

	val = ((*rindex1++) + (*rindex2++)) >> 1;
	
	if (!zflag) {
	  val += *blockvals++;
	}
	
	if (val > 255) val = 255;
	else if (val < 0) val = 0;
	*index++ = val;
	
      }
      index += row_size - 8;
      rindex1 += row_size - 8;
      rindex2 += row_size - 8;
    }
  }
}


/*
 *--------------------------------------------------------------
 *
 * 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, cc;
  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 *indexptr, *rindex1ptr, *bindex1ptr, *rindex2ptr, *bindex2ptr;
  short int *dct_reconptr;
  int val;

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

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

  else {

    /* Construct motion vectors. */

    right_for = recon_right_for >> 2;
    down_for = recon_down_for >> 2;
    right_half_for = recon_right_for & 0x2;
    down_half_for = recon_down_for & 0x2;

    right_back = recon_right_back >> 2;
    down_back = recon_down_back >> 2;
    right_half_back = recon_right_back & 0x2;
    down_half_back = recon_down_back & 0x2;
    
    /* 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);

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

  indexptr = dest+(row*row_size)+col;
  rindex1ptr = past+(row+down_for)*row_size+col+right_for;
  bindex1ptr = future+(row+down_back)*row_size+col+right_back;

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

  if ((!down_half_for) && (!right_half_for) && (!down_half_back) && (!right_half_back)) {
    row_size -= 8;
    for (rr = 0; rr < 8; rr++) {
      for (cc = 0; cc < 8; cc++) {
	
	val = (((*rindex1ptr++) + (*bindex1ptr++)) >> 1);
	
	if (!zflag) {
	  val += *dct_reconptr++;
	}
	
	if (val > 255) val = 255;
	else if (val < 0) val = 0;
	*indexptr++ = val;
      }
      
      indexptr += row_size;
      rindex1ptr += row_size;
      bindex1ptr += row_size;
    }
  }
  else {

    rindex2ptr = rindex1ptr + right_half_for + (row_size * down_half_for);
    bindex2ptr = bindex1ptr + right_half_back + (row_size * down_half_back);
    row_size -= 8;

    for (rr = 0; rr < 8; rr++) {
      for (cc = 0; cc < 8; cc++) {
	
	val = 
	  (((*rindex1ptr++) +
	    (*bindex1ptr++) + (*rindex2ptr++) + (*bindex2ptr++)) >> 2);
	
	if (!zflag) {
	  val += *dct_reconptr++;
	}
	
	if (val > 255) val = 255;
	else if (val < 0) val = 0;
	*indexptr++ = val;
	
      }
    
      indexptr += row_size;
      rindex1ptr += row_size;
      bindex1ptr += row_size;
      rindex2ptr += row_size;
      bindex2ptr += 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 int
ProcessSkippedPFrameMBlocks(vid_stream)
     VidStream *vid_stream;
{
  int row_size, half_row, mb_row, mb_col, row, col, rr;
  int addr;
  unsigned char *dest, *src, *dest1, *src1;

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

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

  /* 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 = vid_stream->current->luminance+(row*row_size)+col;
    src = vid_stream->future->luminance+(row*row_size)+col;

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

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

      memcpy(dest, src, 16);
      dest += row_size;
      src += row_size;
    }
    
    /* Divide row,col to get upper left pixel of macroblock in Cr
       and Cb planes.
       */
    
    row = row >> 1; col = col >> 1;
    
    /* For each row in Cr, and Cb planes... */

    dest = vid_stream->current->Cr+(row*half_row)+col;
    src = vid_stream->future->Cr+(row*half_row)+col;
    dest1 = vid_stream->current->Cb+(row*half_row)+col;
    src1 = vid_stream->future->Cb+(row*half_row)+col;

    for (rr = 0; rr < 8; rr++) {
      
      /* Copy pixel values from last I or P picture. */
      
      memcpy(dest, src, 8);
      memcpy(dest1, src1, 8);

      dest += half_row; src += half_row;
      dest1 += half_row; src1 += half_row;
    }
  }
  
  vid_stream->mblock.recon_right_for_prev = 0;
  vid_stream->mblock.recon_down_for_prev = 0;

  return PARSE_OK;
}



/*
 *--------------------------------------------------------------
 *
 * 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 int
ProcessSkippedBFrameMBlocks(vid_stream)
     VidStream *vid_stream;
{
  int row_size, half_row, mb_row, mb_col, row, col, rr, cc;
  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];
  unsigned char *src1, *src2, *src1a, *src2a;
  unsigned char *dest, *dest1;

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

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

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


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

    c_right_for = recon_right_for >> 2;
    c_down_for = recon_down_for >> 2;
    c_right_half_for = recon_right_for & 0x2;
    c_down_half_for = recon_down_for & 0x2;

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

    c_right_back = recon_right_back >> 2;
    c_down_back = recon_down_back >> 2;
    c_right_half_back = recon_right_back & 0x2;
    c_down_half_back = recon_down_back & 0x2;

  }


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

    /* 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, (row>>1),
			(col>>1), (row_size>>1),
			c_right_for, c_down_for, c_right_half_for, c_down_half_for, 8);
      ReconSkippedBlock(vid_stream->past->Cb, forw_cb, (row>>1),
			(col>>1), (row_size>>1),
			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, (row>>1),
			(col>>1), (row_size>>1),
			c_right_back, c_down_back, 
			c_right_half_back, c_down_half_back, 8);
      ReconSkippedBlock(vid_stream->future->Cb, back_cb, (row>>1),
			(col>>1), (row_size>>1),
			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)) {

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

      for (rr = 0; rr < 16; rr++) {
	memcpy(dest, forw_lum+(rr<<4), 16);
	dest += row_size;
      }

      row /= 2; col /= 2;
      dest = vid_stream->current->Cr+(row*half_row)+col;
      dest1 = vid_stream->current->Cb+(row*half_row)+col;

      for (rr = 0; rr < 8; rr++) {
	memcpy(dest, forw_cr+(rr<<3), 8);
	memcpy(dest1, forw_cb+(rr<<3), 8);

	dest += half_row; dest1 += half_row;
      }
    }

    else if  (vid_stream->mblock.bpict_past_back && 
	      (!vid_stream->mblock.bpict_past_forw)) {
      
      dest = vid_stream->current->luminance+(row*row_size)+col;

      for (rr = 0; rr < 16; rr++) {
	memcpy(dest, back_lum+(rr<<4), 16);
	dest += row_size;
      }

      row /= 2; col /= 2;

      dest = vid_stream->current->Cr+(row*half_row)+col;
      dest1 = vid_stream->current->Cb+(row*half_row)+col;

      for (rr = 0; rr < 8; rr++) {
	memcpy(dest, back_cr+(rr<<3), 8);
	memcpy(dest1, back_cb+(rr<<3), 8);

	dest += half_row; dest1 += half_row;
      }
    }
    else {

      dest = vid_stream->current->luminance+(row*row_size)+col;
      src1 = forw_lum; src2 = back_lum;

      for (rr = 0; rr < 16; rr++) {
	for (cc = 0; cc < 16; cc++) {
	  *dest++ = 
	    ((*src1++) + (*src2++)) >> 1;
	}
	dest += row_size - 16;
      }

      row /= 2; col /= 2;

      dest = vid_stream->current->Cr+(row*half_row)+col;
      dest1 = vid_stream->current->Cb+(row*half_row)+col;
      src1 = forw_cr; src2 = back_cr;
      src1a = forw_cb; src2a = back_cb;

      for (rr = 0; rr < 8; rr++) {
	for (cc = 0; cc < 8; cc++) {
	  *dest++ = ((*src1++) + (*src2++)) >> 1;
	  *dest1++ = ((*src1a++) + (*src2a++)) >> 1;
	}
	dest += half_row - 8;
	dest1 += half_row - 8;
      }
    }
  }

  return PARSE_OK;
}



/*
 *--------------------------------------------------------------
 *
 * 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, cc;
  unsigned char *source2;

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

  if ((!right_half) && (!down_half)) {
    row_size -= width;
    for (rr =0 ; rr < width; rr++) {
      for (cc = 0; cc < width; cc++) {
	*dest++ = *source++;
      }
      source += row_size;
    }
  }
  else {
    source2 = source + right_half + (row_size * down_half);
    row_size -= width;
    for (rr =0 ; rr < width; rr++) {
      for (cc = 0; cc < width; cc++) {
	*dest++ = ((*source++) + (*source2++)) >> 1;
      }
      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 void 
DoPictureDisplay(vid_stream)
VidStream *vid_stream;
{
	/* Convert to colormap space and dither. */
	ColorDitherImage(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 ((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);
	}
	return;
}

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

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.