/*===========================================================================*
 * iframe.c								     *
 *									     *
 *	Procedures concerned with the I-frame encoding			     *
 *									     *
 * EXPORTED PROCEDURES:							     *
 *	GenIFrame							     *
 *	SetSlicesPerFrame						     *
 *	SetBlocksPerSlice						     *
 *	SetIQScale							     *
 *	ResetIFrameStats						     *
 *	ShowIFrameSummary						     *
 *	EstimateSecondsPerIFrame					     *
 *	EncodeYDC							     *
 *	EncodeCDC							     *
 *									     *
 *===========================================================================*/

/*
 * Copyright (c) 1993 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.
 */

/*
 *  $Header: /n/picasso/users/keving/encode/src/RCS/iframe.c,v 1.7 1993/12/22 19:19:01 keving Exp keving $
 *  $Log: iframe.c,v $
 * Revision 1.7  1993/12/22  19:19:01  keving
 * nothing
 *
 * Revision 1.6  1993/07/22  22:23:43  keving
 * nothing
 *
 * Revision 1.5  1993/06/30  20:06:09  keving
 * nothing
 *
 * Revision 1.4  1993/06/03  21:08:08  keving
 * nothing
 *
 * Revision 1.3  1993/03/04  22:24:06  keving
 * nothing
 *
 * Revision 1.2  1993/02/19  18:10:02  keving
 * nothing
 *
 * Revision 1.1  1993/02/18  22:56:39  keving
 * nothing
 *
 *
 */


/*==============*
 * HEADER FILES *
 *==============*/

#include <sys/times.h>
#include <sys/param.h>
#include "all.h"
#include "mtypes.h"
#include "frames.h"
#include "prototypes.h"
#include "mpeg.h"
#include "param.h"
#include "mheaders.h"
#include "fsize.h"
#include "parallel.h"
#include "postdct.h"

#ifndef HZ
#define HZ 60
#endif /* HZ */

/*==================*
 * STATIC VARIABLES *
 *==================*/

static  int	lastNumBits = 0;
static  int	lastIFrame = 0;
static int numBlocks = 0;
static int numBits;
static int numFrames = 0;
static int numFrameBits = 0;
static int32 totalTime = 0;
static float	totalSNR = 0.0;
static float	totalPSNR = 0.0;


/*==================*
 * GLOBAL VARIABLES *
 *==================*/

int	qscaleI;
int	slicesPerFrame;
int	blocksPerSlice;
int	fCode;
boolean	printSNR = FALSE;
boolean	decodeRefFrames = FALSE;


/*=====================*
 * EXPORTED PROCEDURES *
 *=====================*/
extern void	PrintItoIBitRate _ANSI_ARGS_((BitBucket *bb, int frameNum));


/*===========================================================================*
 *
 * SetFCode
 *
 *	set the forward_f_code and backward_f_code according to the search
 *	range.  Must be called AFTER pixelFullSearch and searchRange have
 *	been initialized.  Irrelevant for I-frames, but computation is
 *	negligible (done only once, as well)
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    fCode
 *
 *===========================================================================*/
void
SetFCode()
{
    int	    range;

    if ( pixelFullSearch ) {
	range = searchRange;
    } else {
	range = searchRange*2;
    }

    if ( range < 256 ) {
	if ( range < 64 ) {
	    if ( range < 32 ) {
		fCode = 1;
	    } else {
		fCode = 2;
	    }
	} else {
	    if ( range < 128 ) {
		fCode = 3;
	    } else {
		fCode = 4;
	    }
	}
    } else {
	if ( range < 1024 ) {
	    if ( range < 512 ) {
		fCode = 5;
	    } else {
		fCode = 6;
	    }
	} else {
	    if ( range < 2048 ) {
		fCode = 7;
	    } else {
		fprintf(stdout, "ERROR:  INVALID SEARCH RANGE!!!\n");
		exit(1);
	    }
	}
    }
}


/*===========================================================================*
 *
 * SetSlicesPerFrame
 *
 *	set the number of slices per frame
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    slicesPerFrame
 *
 *===========================================================================*/
void
SetSlicesPerFrame(number)
    int number;
{
    slicesPerFrame = number;
}


/*===========================================================================*
 *
 * SetBlocksPerSlice
 *
 *	set the number of blocks per slice, based on slicesPerFrame
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    blocksPerSlice
 *
 *===========================================================================*/
void
SetBlocksPerSlice()
{
    int	    totalBlocks;

    totalBlocks = (Fsize_y>>4)*(Fsize_x>>4);

    if ( slicesPerFrame > totalBlocks ) {
	blocksPerSlice = 1;
    } else {
	blocksPerSlice = totalBlocks/slicesPerFrame;
    }
}


/*===========================================================================*
 *
 * SetIQScale
 *
 *	set the I-frame Q-scale
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    qscaleI
 *
 *===========================================================================*/
void
SetIQScale(qI)
int qI;
{
    qscaleI = qI;
}


/*===========================================================================*
 *
 * GenIFrame
 *
 *	generate an I-frame; appends result to bb
 *
 * RETURNS:	I-frame appended to bb
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
void
GenIFrame(bb, current)
    BitBucket *bb;
    MpegFrame *current;
{
    register int x, y;
    register int index;
    FlatBlock	fb[6];
    Block	dec[6];
    int32 y_dc_pred, cr_dc_pred, cb_dc_pred;
    int totalBits;
    int	totalFrameBits;
    struct tms timeBuffer;
    int32    startTime, endTime;
    int	    frameBlocks;
    float   snr[3], psnr[3];

    /* set-up for statistics */
    numFrames++;
    totalFrameBits = bb->cumulativeBits;
    if ( ( ! childProcess) && showBitRatePerFrame ) {
	if ( lastNumBits == 0 ) {
	    lastNumBits = bb->cumulativeBits;
	    lastIFrame = current->id;
	} else {
	/* ASSUMES 30 FRAMES PER SECOND */
	    fprintf(stdout, "I-to-I (frames %5d to %5d) bitrate:  %8d\n",
		    lastIFrame, current->id-1,
		    ((bb->cumulativeBits-lastNumBits)*30)/
		    (current->id-lastIFrame));
	    fprintf(bitRateFile, "I-to-I (frames %5d to %5d) bitrate:  %8d\n",
		    lastIFrame, current->id-1,
		    ((bb->cumulativeBits-lastNumBits)*30)/
		    (current->id-lastIFrame));
	    lastNumBits = bb->cumulativeBits;	    
	    lastIFrame = current->id;
	}
    }

    times(&timeBuffer);
    startTime = timeBuffer.tms_utime + timeBuffer.tms_stime;

    Frame_AllocBlocks(current);
    BlockifyFrame(current);

    DBG_PRINT(("Generating iframe\n"));

    Mhead_GenPictureHeader(bb, I_FRAME, current->id, fCode);
    Mhead_GenSliceHeader(bb, 1, qscaleI, NULL, 0);

    if ( referenceFrame == DECODED_FRAME ) {
	Frame_AllocDecoded(current, TRUE);
    } else if ( printSNR ) {
	Frame_AllocDecoded(current, FALSE);
    }

    y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
    totalBits = bb->cumulativeBits;
    frameBlocks = 0;
    for (y = 0; y < (Fsize_y >> 3); y += 2) {
	for (x = 0; x < (Fsize_x >> 3); x += 2) {
	    /* DCT this macroblock */
	    mp_fwd_dct_block(current->y_blocks[y][x]);
	    mp_fwd_dct_block(current->y_blocks[y][x+1]);
	    mp_fwd_dct_block(current->y_blocks[y+1][x]);
	    mp_fwd_dct_block(current->y_blocks[y+1][x+1]);
	    mp_fwd_dct_block(current->cr_blocks[y>>1][x>>1]);
	    mp_fwd_dct_block(current->cb_blocks[y>>1][x>>1]);

	    if ( (frameBlocks % blocksPerSlice == 0) && (frameBlocks != 0) ) {
		/* create a new slice */
		Mhead_GenSliceEnder(bb);
		Mhead_GenSliceHeader(bb, 1+(y>>1), qscaleI, NULL, 0);
		y_dc_pred = cr_dc_pred = cb_dc_pred = 128;

		GEN_I_BLOCK(I_FRAME, current, bb, 1+(x>>1), qscaleI);
	    } else {
		GEN_I_BLOCK(I_FRAME, current, bb, 1, qscaleI);
	    }

	    if ( decodeRefFrames ) {
		/* need to decode block we just encoded */
		Mpost_UnQuantZigBlock(fb[0], dec[0], qscaleI, TRUE);
		Mpost_UnQuantZigBlock(fb[1], dec[1], qscaleI, TRUE);
		Mpost_UnQuantZigBlock(fb[2], dec[2], qscaleI, TRUE);
		Mpost_UnQuantZigBlock(fb[3], dec[3], qscaleI, TRUE);
		Mpost_UnQuantZigBlock(fb[4], dec[4], qscaleI, TRUE);
		Mpost_UnQuantZigBlock(fb[5], dec[5], qscaleI, TRUE);

		/* now, reverse the DCT transform */
		for ( index = 0; index < 6; index++ ) {
		    mpeg_jrevdct((int16 *)dec[index]);		
		}

		/* now, unblockify */
		BlockToData(current->decoded_y, dec[0], y, x);
		BlockToData(current->decoded_y, dec[1], y, x+1);
		BlockToData(current->decoded_y, dec[2], y+1, x);
		BlockToData(current->decoded_y, dec[3], y+1, x+1);
		BlockToData(current->decoded_cb, dec[4], y>>1, x>>1);
		BlockToData(current->decoded_cr, dec[5], y>>1, x>>1);
	    }

	    numBlocks++;
	    frameBlocks++;
	}
    }

    if ( printSNR ) {
        ComputeSNR(current->orig_y, current->decoded_y, Fsize_y, Fsize_x,
		   &snr[0], &psnr[0]);
        ComputeSNR(current->orig_cb, current->decoded_cb,
		   Fsize_y>>1, Fsize_x>>1, &snr[1], &psnr[1]);
        ComputeSNR(current->orig_cr, current->decoded_cr,
		   Fsize_y>>1, Fsize_x>>1, &snr[2], &psnr[2]);

	totalSNR += snr[0];
	totalPSNR += psnr[0];
    }

    if ( (referenceFrame == DECODED_FRAME) && NonLocalRefFrame(current->id) ) {
	if ( remoteIO ) {
	    SendDecodedFrame(current);
	} else {
	    WriteDecodedFrame(current);
	}

	/* now, tell decode server it is ready */
	NotifyDecodeServerReady(current->id);
    }

    numBits += (bb->cumulativeBits-totalBits);

    DBG_PRINT(("End of frame\n"));

    Mhead_GenSliceEnder(bb);

    times(&timeBuffer);
    endTime = timeBuffer.tms_utime + timeBuffer.tms_stime;
    totalTime += (endTime-startTime);

    numFrameBits += (bb->cumulativeBits-totalFrameBits);

    if ( ( ! childProcess) && showBitRatePerFrame ) {
	/* ASSUMES 30 FRAMES PER SECOND */
	fprintf(bitRateFile, "%5d\t%8d\n", current->id,
		30*(bb->cumulativeBits-totalFrameBits));
    }
    
    if ( (! childProcess) && frameSummary ) {

/* ASSUMES 30 FRAMES PER SECOND */
	fprintf(stdout, "FRAME %d (I):  %ld seconds  (%d bits/s output)\n", 
		current->id, (long)((endTime-startTime)/HZ),
		30*(bb->cumulativeBits-totalFrameBits));
	if ( printSNR ) {
	    fprintf(stdout, "FRAME %d:  SNR:  %.1f\t%.1f\t%.1f\tPSNR:  %.1f\t%.1f\t%.1f\n",
		    current->id, snr[0], snr[1], snr[2],
		    psnr[0], psnr[1], psnr[2]);
	}
    }
}


/*===========================================================================*
 *
 * ResetIFrameStats
 *
 *	reset the I-frame statistics
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
void
ResetIFrameStats()
{
    numBlocks = 0;
    numBits = 0;
    numFrames = 0;
    numFrameBits = 0;
    totalTime = 0;
}


/*===========================================================================*
 *
 * ShowIFrameSummary
 *
 *	prints out statistics on all I-frames
 *
 * RETURNS:	time taken for I-frames (in seconds)
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
float
ShowIFrameSummary(inputFrameBits, totalBits, fpointer)
    int inputFrameBits;
    int32 totalBits;
    FILE *fpointer;
{
    if ( numFrames == 0 ) {
	return 0.0;
    }

    fprintf(fpointer, "-------------------------\n");
    fprintf(fpointer, "*****I FRAME SUMMARY*****\n");
    fprintf(fpointer, "-------------------------\n");

    fprintf(fpointer, "  Blocks:    %5d     (%6d bits)     (%5d bpb)\n",
	    numBlocks, numBits, numBits/numBlocks);
    fprintf(fpointer, "  Frames:    %5d     (%6d bits)     (%5d bpf)     (%2.1f%% of total)\n",
	    numFrames, numFrameBits, numFrameBits/numFrames,
	    100.0*(float)numFrameBits/(float)totalBits);
    fprintf(fpointer, "  Compression:  %3d:1     (%9.4f bpp)\n",
	    numFrames*inputFrameBits/numFrameBits,
	    24.0*(float)numFrameBits/(float)(numFrames*inputFrameBits));
    if ( printSNR )
	fprintf(fpointer, "  Avg Y SNR/PSNR:  %.1f     %.1f\n",
		totalSNR/(float)numFrames, totalPSNR/(float)numFrames);
    if ( totalTime == 0 ) {
	fprintf(fpointer, "  Seconds:  NONE\n");
    } else {
	fprintf(fpointer, "  Seconds:  %9ld     (%9.4f fps)  (%9ld pps)  (%9ld mps)\n",
		(long)(totalTime/HZ),
		(float)((float)(HZ*numFrames)/(float)totalTime),
		(long)((float)HZ*(float)numFrames*(float)inputFrameBits/(24.0*(float)totalTime)),
		(long)((float)HZ*(float)numFrames*(float)inputFrameBits/(256.0*24.0*(float)totalTime)));
    }

    return (float)totalTime/(float)HZ;
}


/*===========================================================================*
 *
 * EstimateSecondsPerIFrame
 *
 *	estimates the number of seconds required per I-frame
 *
 * RETURNS:	seconds (floating point value)
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
float
EstimateSecondsPerIFrame()
{
    return (float)totalTime/((float)HZ*(float)numFrames);
}


/*===========================================================================*
 *
 * EncodeYDC
 *
 *	Encode the DC portion of a DCT of a luminance block
 *
 * RETURNS:	result appended to bb
 *
 * SIDE EFFECTS:    updates pred_term
 *
 *===========================================================================*/
void
EncodeYDC(dc_term, pred_term, bb)
    int32 dc_term;
    int32 *pred_term;
    BitBucket *bb;
{
    int ydiff, ydiff_abs;

    ydiff = (dc_term - (*pred_term));
    if (ydiff > 255) {
	ydiff = 255;
    } else if (ydiff < -255) {
	ydiff = -255;
    }

    ydiff_abs = ABS(ydiff);

    if (ydiff_abs == 0) {
	Bitio_Write(bb, 0x4, 3);
    } else if (ydiff_abs & 0x80) {
	Bitio_Write(bb, 0x7e, 7);
	if (ydiff > 0) {
	    Bitio_Write(bb, ydiff_abs, 8);
	} else
	    Bitio_Write(bb, ~ydiff_abs, 8);
    } else if (ydiff_abs & 0x40) {
	Bitio_Write(bb, 0x3e, 6);
	if (ydiff > 0) {
	    Bitio_Write(bb, ydiff_abs, 7);
	} else {
	    Bitio_Write(bb, ~ydiff_abs, 7);
	}
    } else if (ydiff_abs & 0x20) {
	Bitio_Write(bb, 0x1e, 5);
	if (ydiff > 0) {
	    Bitio_Write(bb, ydiff_abs, 6);
	} else {
	    Bitio_Write(bb, ~ydiff_abs, 6);
	}
    } else if (ydiff_abs & 0x10) {
	Bitio_Write(bb, 0xe, 4);
	if (ydiff > 0) {
	    Bitio_Write(bb, ydiff_abs, 5);
	} else {
	    Bitio_Write(bb, ~ydiff_abs, 5);
	}
    } else if (ydiff_abs & 0x08) {
	Bitio_Write(bb, 0x6, 3);
	if (ydiff > 0) {
	    Bitio_Write(bb, ydiff_abs, 4);
	} else {
	    Bitio_Write(bb, ~ydiff_abs, 4);
	}
    } else if (ydiff_abs & 0x04) {
	Bitio_Write(bb, 0x5, 3);
	if (ydiff > 0) {
	    Bitio_Write(bb, ydiff_abs, 3);
	} else {
	    Bitio_Write(bb, ~ydiff_abs, 3);
	}
    } else if (ydiff_abs & 0x02) {
	Bitio_Write(bb, 0x1, 2);
	if (ydiff > 0) {
	    Bitio_Write(bb, ydiff_abs, 2);
	} else {
	    Bitio_Write(bb, ~ydiff_abs, 2);
	}
    } else if (ydiff_abs & 0x01) {
	Bitio_Write(bb, 0x0, 2);
	if (ydiff > 0) {
	    Bitio_Write(bb, ydiff_abs, 1);
	} else {
	    Bitio_Write(bb, ~ydiff_abs, 1);
	}
    } else {
	fprintf(stderr, "ERROR in EncodeYDC\n");
	exit(1);
    }

    (*pred_term) += ydiff;
}


/*===========================================================================*
 *
 * EncodeCDC
 *
 *	Encode the DC portion of a DCT of a chrominance block
 *
 * RETURNS:	result appended to bb
 *
 * SIDE EFFECTS:    updates pred_term
 *
 *===========================================================================*/
void
EncodeCDC(dc_term, pred_term, bb)
    int32 dc_term;
    int32 *pred_term;
    BitBucket *bb;
{
    int cdiff, cdiff_abs;

    cdiff = (dc_term - (*pred_term));
    if (cdiff > 255) {
	cdiff = 255;
    } else if (cdiff < -255) {
	cdiff = -255;
    }

    cdiff_abs = ABS(cdiff);

    if (cdiff_abs == 0) {
	Bitio_Write(bb, 0x0, 2);
    } else if (cdiff_abs & 0x80) {
	Bitio_Write(bb, 0xfe, 8);
	if (cdiff > 0) {
	    Bitio_Write(bb, cdiff_abs, 8);
	} else {
	    Bitio_Write(bb, ~cdiff_abs, 8);
	}
    } else if (cdiff_abs & 0x40) {
	Bitio_Write(bb, 0x7e, 7);
	if (cdiff > 0) {
	    Bitio_Write(bb, cdiff_abs, 7);
	} else {
	    Bitio_Write(bb, ~cdiff_abs, 7);
	}
    } else if (cdiff_abs & 0x20) {
	Bitio_Write(bb, 0x3e, 6);
	if (cdiff > 0) {
	    Bitio_Write(bb, cdiff_abs, 6);
	} else {
	    Bitio_Write(bb, ~cdiff_abs, 6);
	}
    } else if (cdiff_abs & 0x10) {
	Bitio_Write(bb, 0x1e, 5);
	if (cdiff > 0) {
	    Bitio_Write(bb, cdiff_abs, 5);
	} else {
	    Bitio_Write(bb, ~cdiff_abs, 5);
	}
    } else if (cdiff_abs & 0x08) {
	Bitio_Write(bb, 0xe, 4);
	if (cdiff > 0) {
	    Bitio_Write(bb, cdiff_abs, 4);
	} else {
	    Bitio_Write(bb, ~cdiff_abs, 4);
	}
    } else if (cdiff_abs & 0x04) {
	Bitio_Write(bb, 0x6, 3);
	if (cdiff > 0) {
	    Bitio_Write(bb, cdiff_abs, 3);
	} else {
	    Bitio_Write(bb, ~cdiff_abs, 3);
	}
    } else if (cdiff_abs & 0x02) {
	Bitio_Write(bb, 0x2, 2);
	if (cdiff > 0) {
	    Bitio_Write(bb, cdiff_abs, 2);
	} else {
	    Bitio_Write(bb, ~cdiff_abs, 2);
	}
    } else if (cdiff_abs & 0x01) {
	Bitio_Write(bb, 0x1, 2);
	if (cdiff > 0) {
	    Bitio_Write(bb, cdiff_abs, 1);
	} else {
	    Bitio_Write(bb, ~cdiff_abs, 1);
	}
    } else {
	fprintf(stderr, "ERROR in EncodeCDC\n");
	exit(1);
    }

    (*pred_term) += cdiff;
}


void
ComputeSNR(origData, newData, ySize, xSize, snr, psnr)
     register uint8 **origData;
     register uint8 **newData;
     int ySize;
     int xSize;
     float *snr;
     float *psnr;
{
    register int32	tempInt;
    register int y, x;
    int32	varOrig = 0;
    int32	varDiff = 0;
    double	ratio = 0.0;

    for ( y = 0; y < ySize; y++ ) {
	for ( x = 0; x < xSize; x++ ) {
	    tempInt = (origData[y][x]-newData[y][x]);
	    varDiff += (tempInt*tempInt);
	}
    }

    for ( y = 0; y < ySize; y++ ) {
	varOrig = 0;
	for ( x = 0; x < xSize; x++ ) {
	    tempInt = origData[y][x];
	    varOrig += (tempInt*tempInt);
	}
	ratio += (double)varOrig/(double)varDiff;
    }

    *snr = 10.0*log10(ratio);
    *psnr = 20.0*log10(255.0/sqrt((double)varDiff/(double)(ySize*xSize)));
}


void
WriteDecodedFrame(frame)
    MpegFrame *frame;
{
    FILE    *fpointer;
    char    fileName[256];
    int	width, height;
    register int y;

    /* need to save decoded frame to disk because it might be accessed
       by another process */

    width = Fsize_x;
    height = Fsize_y;

    sprintf(fileName, "%s.decoded.%d", outputFileName, frame->id);
    fprintf(stdout, "outputting to %s\n", fileName);
    fflush(stdout);

    fpointer = fopen(fileName, "w");

	for ( y = 0; y < height; y++ ) {
	    fwrite(frame->decoded_y[y], 1, width, fpointer);
	}

	for (y = 0; y < (height >> 1); y++) {			/* U */
	    fwrite(frame->decoded_cb[y], 1, width >> 1, fpointer);
	}

	for (y = 0; y < (height >> 1); y++) {			/* V */
	    fwrite(frame->decoded_cr[y], 1, width >> 1, fpointer);
	}

    fclose(fpointer);
}


void
PrintItoIBitRate(bb, frameNum)
    BitBucket *bb;
    int	    frameNum;
{
    if ( ( ! childProcess) && showBitRatePerFrame ) {
	/* ASSUMES 30 FRAMES PER SECOND */
	fprintf(stdout, "I-to-I (frames %5d to %5d) bitrate:  %8d\n",
		lastIFrame, frameNum-1,
		((bb->cumulativeBits-lastNumBits)*30)/
		(frameNum-lastIFrame));
	fprintf(bitRateFile, "I-to-I (frames %5d to %5d) bitrate:  %8d\n",
		lastIFrame, frameNum-1,
		((bb->cumulativeBits-lastNumBits)*30)/
		(frameNum-lastIFrame));
    }
}
