/*===========================================================================* * bframe.c * * * * Procedures concerned with the B-frame encoding * * * * EXPORTED PROCEDURES: * * GenBFrame * * ResetBFrameStats * * ShowBFrameSummary * * EstimateSecondsPerBFrame * * ComputeBMotionLumBlock * * SetBQScale * * GetBQScale * * * *===========================================================================*/ /* * Copyright (c) 1995 The Regents of the University of California. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without written agreement is * hereby granted, provided that the above copyright notice and the following * two paragraphs appear in all copies of this software. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ /* * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/bframe.c,v 1.20 1995/08/14 22:28:11 smoot Exp $ * $Log: bframe.c,v $ * Revision 1.20 1995/08/14 22:28:11 smoot * renamed index to idx * added option to not skip in B frames * * Revision 1.19 1995/08/07 21:52:11 smoot * added Color to skip routine * fixed full/half bug in intial loop * added comments * removed buggy "extra skips" code * * Revision 1.18 1995/06/21 22:22:24 smoot * generalized time checking, fixed bug in specifics filesm * and added TUNEing stuff * * Revision 1.17 1995/04/14 23:08:02 smoot * reorganized to ease rate control experimentation * * Revision 1.16 1995/02/24 23:49:10 smoot * added Spec version 2 * * Revision 1.15 1995/01/30 19:45:45 smoot * Fixed a cr/cb screwup * * Revision 1.14 1995/01/23 02:46:43 darryl * initialized variable * * Revision 1.13 1995/01/19 23:07:12 eyhung * Changed copyrights * * Revision 1.12 1995/01/16 07:44:11 eyhung * Added realQuiet * * Revision 1.11 1994/12/07 00:40:36 smoot * Added seperate P and B search ranges * * Revision 1.10 1994/11/24 00:35:47 smoot * fixed bug (divide by 0) in B fram statsitics * * Revision 1.9 1994/11/14 22:26:48 smoot * Merged specifics and rate control. * * Revision 1.8 1994/11/01 05:01:16 darryl * with rate control changes added * * Revision 2.0 1994/10/24 02:38:51 darryl * will be adding the experiment code * * Revision 1.1 1994/09/27 00:16:04 darryl * Initial revision * * Revision 1.7 1994/03/15 00:27:11 keving * nothing * * Revision 1.6 1993/12/22 19:19:01 keving * nothing * * Revision 1.5 1993/07/30 19:24:04 keving * nothing * * Revision 1.4 1993/07/22 22:23:43 keving * nothing * * Revision 1.3 1993/06/30 20:06:09 keving * nothing * * Revision 1.2 1993/06/03 21:08:08 keving * nothing * * Revision 1.1 1993/02/19 19:14:28 keving * nothing * */ /*==============* * HEADER FILES * *==============*/ #include "all.h" #include #include #include "mtypes.h" #include "bitio.h" #include "frames.h" #include "prototypes.h" #include "fsize.h" #include "param.h" #include "mheaders.h" #include "postdct.h" #include "rate.h" #include "opts.h" /*==================* * STATIC VARIABLES * *==================*/ static int numBIBlocks = 0; static int numBBBlocks = 0; static int numBSkipped = 0; static int numBIBits = 0; static int numBBBits = 0; static int numFrames = 0; static int numFrameBits = 0; static int32 totalTime = 0; static int qscaleB; static float totalSNR = 0.0; static float totalPSNR = 0.0; static int numBFOBlocks = 0; /* forward only */ static int numBBABlocks = 0; /* backward only */ static int numBINBlocks = 0; /* interpolate */ static int numBFOBits = 0; static int numBBABits = 0; static int numBINBits = 0; /*====================* * EXTERNAL VARIABLES * *====================*/ extern Block **dct, **dctr, **dctb; extern dct_data_type **dct_data; #define NO_MOTION 0 #define MOTION 1 #define SKIP 2 /* used in useMotion in dct_data */ /*===============================* * INTERNAL PROCEDURE prototypes * *===============================*/ static boolean MotionSufficient _ANSI_ARGS_((MpegFrame *curr, LumBlock currBlock, MpegFrame *prev, MpegFrame *next, int by, int bx, int mode, int fmy, int fmx, int bmy, int bmx)); static void ComputeBMotionBlock _ANSI_ARGS_((MpegFrame *prev, MpegFrame *next, int by, int bx, int mode, int fmy, int fmx, int bmy, int bmx, Block motionBlock, int type)); static void ComputeBDiffDCTs _ANSI_ARGS_((MpegFrame *current, MpegFrame *prev, MpegFrame *next, int by, int bx, int mode, int fmy, int fmx, int bmy, int bmx, int *pattern)); static boolean DoBIntraCode _ANSI_ARGS_((MpegFrame *current, MpegFrame *prev, MpegFrame *next, int by, int bx, int mode, int fmy, int fmx, int bmy, int bmx)); static int ComputeBlockColorDiff _ANSI_ARGS_((Block current, Block motionBlock)); /*=====================* * EXPORTED PROCEDURES * *=====================*/ /*===========================================================================* * * GenBFrame * * generate a B-frame from previous and next frames, adding the result * to the given bit bucket * * RETURNS: frame appended to bb * * SIDE EFFECTS: none * *===========================================================================*/ void GenBFrame(bb, curr, prev, next) BitBucket *bb; MpegFrame *curr; MpegFrame *prev; MpegFrame *next; { extern int **bfmvHistogram; extern int **bbmvHistogram; FlatBlock fba[6], fb[6]; Block dec[6]; int32 y_dc_pred, cr_dc_pred, cb_dc_pred; int x, y; int fMotionX = 0, fMotionY = 0; int bMotionX = 0, bMotionY = 0; int oldFMotionX = 0, oldFMotionY = 0; int oldBMotionX = 0, oldBMotionY = 0; int oldMode = MOTION_FORWARD; int mode = MOTION_FORWARD; int offsetX, offsetY; int tempX, tempY; int fMotionXrem = 0, fMotionXquot = 0; int fMotionYrem = 0, fMotionYquot = 0; int bMotionXrem = 0, bMotionXquot = 0; int bMotionYrem = 0, bMotionYquot = 0; int pattern; int numIBlocks = 0, numBBlocks = 0; int numSkipped = 0, totalBits; int numIBits = 0, numBBits = 0; boolean lastIntra = TRUE; boolean motionForward, motionBackward; int totalFrameBits; int32 startTime, endTime; int lastX, lastY; int lastBlockX, lastBlockY; register int ix, iy; LumBlock currentBlock; int fy, fx; boolean make_skip_block; int mbAddrInc = 1; int mbAddress; int slicePos; float snr[3], psnr[3]; int idx; int QScale; BlockMV *info; int bitstreamMode, newQScale; int rc_blockStart=0; boolean overflowChange=FALSE; int overflowValue = 0; if (collect_quant) {fprintf(collect_quant_fp, "# B\n");} if (dct == NULL) AllocDctBlocks(); numFrames++; totalFrameBits = bb->cumulativeBits; startTime = time_elapsed(); /* Rate Control */ bitstreamMode = getRateMode(); if (bitstreamMode == FIXED_RATE) { targetRateControl(curr); } QScale = GetBQScale(); Mhead_GenPictureHeader(bb, B_FRAME, curr->id, fCodeB); /* Check for Qscale change */ if (specificsOn) { newQScale = SpecLookup(curr->id, 0, 0 /* junk */, &info, QScale); if (newQScale != -1) { QScale = newQScale; } /* check for slice */ newQScale = SpecLookup(curr->id, 1, 1, &info, QScale); if (newQScale != -1) { QScale = newQScale; } } Mhead_GenSliceHeader(bb, 1, QScale, NULL, 0); Frame_AllocBlocks(curr); BlockifyFrame(curr); if ( printSNR ) { Frame_AllocDecoded(curr, FALSE); } /* for I-blocks */ y_dc_pred = cr_dc_pred = cb_dc_pred = 128; totalBits = bb->cumulativeBits; if ( ! pixelFullSearch ) { if ( ! prev->halfComputed && (prev != NULL)) { ComputeHalfPixelData(prev); } if ( ! next->halfComputed ) { ComputeHalfPixelData(next); } } lastBlockX = Fsize_x>>3; lastBlockY = Fsize_y>>3; lastX = lastBlockX-2; lastY = lastBlockY-2; mbAddress = 0; /* find motion vectors and do dcts */ /* In this first loop, all MVs are in half-pixel scope, (if FULL is set then they will be multiples of 2). This is not true in the second loop. */ for (y = 0; y < lastBlockY; y += 2) { for (x = 0; x < lastBlockX; x += 2) { slicePos = (mbAddress % blocksPerSlice); /* compute currentBlock */ BLOCK_TO_FRAME_COORD(y, x, fy, fx); for ( iy = 0; iy < 16; iy++ ) { for ( ix = 0; ix < 16; ix++ ) { currentBlock[iy][ix] = (int16)curr->orig_y[fy+iy][fx+ix]; } } if (slicePos == 0) { oldFMotionX = 0; oldFMotionY = 0; oldBMotionX = 0; oldBMotionY = 0; oldMode = MOTION_FORWARD; lastIntra = TRUE; } /* STEP 1: Select Forward, Backward, or Interpolated motion vectors */ /* see if old motion is good enough */ /* but force last block to be non-skipped */ /* can only skip if: * 1) not the last block in frame * 2) not the last block in slice * 3) not the first block in slice * 4) previous block was not intra-coded */ if ( ((y < lastY) || (x < lastX)) && (slicePos+1 != blocksPerSlice) && (slicePos != 0) && (! lastIntra) && (BSkipBlocks) ) { make_skip_block = MotionSufficient(curr, currentBlock, prev, next, y, x, oldMode, oldFMotionY, oldFMotionX, oldBMotionY, oldBMotionX); } else { make_skip_block = FALSE; } if ( make_skip_block ) { skip_it: /* skipped macro block */ dct_data[y][x].useMotion = SKIP; } else { if (specificsOn) { (void) SpecLookup(curr->id, 2, mbAddress, &info, QScale); if (info == (BlockMV*)NULL) goto gosearch; else { switch (info->typ) { case TYP_SKIP: goto skip_it; case TYP_FORW: fMotionX = info->fx; fMotionY = info->fy; mode = MOTION_FORWARD; break; case TYP_BACK: bMotionX = info->bx; bMotionY = info->by; mode = MOTION_BACKWARD; break; case TYP_BOTH: fMotionX = info->fx; fMotionY = info->fy; bMotionX = info->bx; bMotionY = info->by; mode = MOTION_INTERPOLATE; break; default: fprintf(stderr,"Unreachable code in GenBFrame!\n"); goto gosearch; } goto skipsearch; }} gosearch: /* do bsearch */ mode = BMotionSearch(currentBlock, prev, next, y, x, &fMotionY, &fMotionX, &bMotionY, &bMotionX, mode); skipsearch: /* STEP 2: INTRA OR NON-INTRA CODING */ if ( IntraPBAllowed && DoBIntraCode(curr, prev, next, y, x, mode, fMotionY, fMotionX, bMotionY, bMotionX) ) { /* output I-block inside a B-frame */ numIBlocks++; oldFMotionX = 0; oldFMotionY = 0; oldBMotionX = 0; oldBMotionY = 0; lastIntra = TRUE; dct_data[y][x].useMotion = NO_MOTION; oldMode = MOTION_FORWARD; /* calculate forward dct's */ if (collect_quant && (collect_quant_detailed & 1)) fprintf(collect_quant_fp, "l\n"); mp_fwd_dct_block2(curr->y_blocks[y][x], dct[y][x]); mp_fwd_dct_block2(curr->y_blocks[y][x+1], dct[y][x+1]); mp_fwd_dct_block2(curr->y_blocks[y+1][x], dct[y+1][x]); mp_fwd_dct_block2(curr->y_blocks[y+1][x+1], dct[y+1][x+1]); if (collect_quant && (collect_quant_detailed & 1)) {fprintf(collect_quant_fp, "c\n");} mp_fwd_dct_block2(curr->cb_blocks[y>>1][x>>1], dctb[y>>1][x>>1]); mp_fwd_dct_block2(curr->cr_blocks[y>>1][x>>1], dctr[y>>1][x>>1]); } else { /* dct P/Bi/B block */ pattern = 63; lastIntra = FALSE; numBBlocks++; dct_data[y][x].mode = mode; oldMode = mode; dct_data[y][x].fmotionX = fMotionX; dct_data[y][x].fmotionY = fMotionY; dct_data[y][x].bmotionX = bMotionX; dct_data[y][x].bmotionY = bMotionY; switch (mode) { case MOTION_FORWARD: numBFOBlocks++; oldFMotionX = fMotionX; oldFMotionY = fMotionY; break; case MOTION_BACKWARD: numBBABlocks++; oldBMotionX = bMotionX; oldBMotionY = bMotionY; break; case MOTION_INTERPOLATE: numBINBlocks++; oldFMotionX = fMotionX; oldFMotionY = fMotionY; oldBMotionX = bMotionX; oldBMotionY = bMotionY; break; default: fprintf(stderr, "PROGRAMMER ERROR: Illegal mode: %d\n", mode); exit(1); } ComputeBDiffDCTs(curr, prev, next, y, x, mode, fMotionY, fMotionX, bMotionY, bMotionX, &pattern); dct_data[y][x].pattern = pattern; dct_data[y][x].useMotion = MOTION; if ( computeMVHist ) { assert(fMotionX+searchRangeB+1 >= 0); assert(fMotionY+searchRangeB+1 >= 0); assert(fMotionX+searchRangeB+1 <= 2*searchRangeB+2); assert(fMotionY+searchRangeB+1 <= 2*searchRangeB+2); assert(bMotionX+searchRangeB+1 >= 0); assert(bMotionY+searchRangeB+1 >= 0); assert(bMotionX+searchRangeB+1 <= 2*searchRangeB+2); assert(bMotionY+searchRangeB+1 <= 2*searchRangeB+2); bfmvHistogram[fMotionX+searchRangeB+1][fMotionY+searchRangeB+1]++; bbmvHistogram[bMotionX+searchRangeB+1][bMotionY+searchRangeB+1]++; } } /* motion-block */ } /* not skipped */ mbAddress++; }} /* reset everything */ oldFMotionX = 0; oldFMotionY = 0; oldBMotionX = 0; oldBMotionY = 0; oldMode = MOTION_FORWARD; lastIntra = TRUE; y_dc_pred = cr_dc_pred = cb_dc_pred = 128; mbAddress = 0; /* Now generate the frame */ for (y = 0; y < lastBlockY; y += 2) { for (x = 0; x < lastBlockX; x += 2) { slicePos = (mbAddress % blocksPerSlice); if ( (slicePos == 0) && (mbAddress != 0) ) { if (specificsOn) { /* Make sure no slice Qscale change */ newQScale = SpecLookup(curr->id,1,mbAddress/blocksPerSlice, &info, QScale); if (newQScale != -1) QScale = newQScale; } Mhead_GenSliceEnder(bb); Mhead_GenSliceHeader(bb, 1+(y>>1), QScale, NULL, 0); /* reset everything */ oldFMotionX = 0; oldFMotionY = 0; oldBMotionX = 0; oldBMotionY = 0; oldMode = MOTION_FORWARD; lastIntra = TRUE; y_dc_pred = cr_dc_pred = cb_dc_pred = 128; mbAddrInc = 1+(x>>1); } /* Determine if new Qscale needed for Rate Control purposes */ if (bitstreamMode == FIXED_RATE) { rc_blockStart = bb->cumulativeBits; newQScale = needQScaleChange(QScale, curr->y_blocks[y][x], curr->y_blocks[y][x+1], curr->y_blocks[y+1][x], curr->y_blocks[y+1][x+1]); if (newQScale > 0) { QScale = newQScale; } } if (specificsOn) { newQScale = SpecLookup(curr->id, 2, mbAddress, &info, QScale); if (newQScale != -1) { QScale = newQScale; }} if (dct_data[y][x].useMotion == NO_MOTION) { GEN_I_BLOCK(B_FRAME, curr, bb, mbAddrInc, QScale); mbAddrInc = 1; numIBits += (bb->cumulativeBits-totalBits); totalBits = bb->cumulativeBits; /* reset because intra-coded */ oldFMotionX = 0; oldFMotionY = 0; oldBMotionX = 0; oldBMotionY = 0; oldMode = MOTION_FORWARD; lastIntra = TRUE; if ( printSNR ) { /* need to decode block we just encoded */ /* and reverse the DCT transform */ for ( idx = 0; idx < 6; idx++ ) { Mpost_UnQuantZigBlock(fb[idx], dec[idx], QScale, TRUE); mpeg_jrevdct((int16 *)dec[idx]); } /* now, unblockify */ BlockToData(curr->decoded_y, dec[0], y, x); BlockToData(curr->decoded_y, dec[1], y, x+1); BlockToData(curr->decoded_y, dec[2], y+1, x); BlockToData(curr->decoded_y, dec[3], y+1, x+1); BlockToData(curr->decoded_cb, dec[4], y>>1, x>>1); BlockToData(curr->decoded_cr, dec[5], y>>1, x>>1); } } else if (dct_data[y][x].useMotion == SKIP) { numSkipped++; mbAddrInc++; /* decode skipped block */ if ( printSNR ) { int fmy, fmx, bmy, bmx; for ( idx = 0; idx < 6; idx++ ) { memset((char *)dec[idx], 0, sizeof(Block)); } if ( pixelFullSearch ) { fmy = 2*oldFMotionY; fmx = 2*oldFMotionX; bmy = 2*oldBMotionY; bmx = 2*oldBMotionX; } else { fmy = oldFMotionY; fmx = oldFMotionX; bmy = oldBMotionY; bmx = oldBMotionX; } /* now add the motion block */ AddBMotionBlock(dec[0], prev->decoded_y, next->decoded_y, y, x, mode, fmy, fmx, bmy, bmx); AddBMotionBlock(dec[1], prev->decoded_y, next->decoded_y, y, x+1, mode, fmy, fmx, bmy, bmx); AddBMotionBlock(dec[2], prev->decoded_y, next->decoded_y, y+1, x, mode, fmy, fmx, bmy, bmx); AddBMotionBlock(dec[3], prev->decoded_y, next->decoded_y, y+1, x+1, mode, fmy, fmx, bmy, bmx); AddBMotionBlock(dec[4], prev->decoded_cb, next->decoded_cb, y>>1, x>>1, mode, fmy/2, fmx/2, bmy/2, bmx/2); AddBMotionBlock(dec[5], prev->decoded_cr, next->decoded_cr, y>>1, x>>1, mode, fmy/2, fmx/2, bmy/2, bmx/2); /* now, unblockify */ BlockToData(curr->decoded_y, dec[0], y, x); BlockToData(curr->decoded_y, dec[1], y, x+1); BlockToData(curr->decoded_y, dec[2], y+1, x); BlockToData(curr->decoded_y, dec[3], y+1, x+1); BlockToData(curr->decoded_cb, dec[4], y>>1, x>>1); BlockToData(curr->decoded_cr, dec[5], y>>1, x>>1); } } else /* B block */ { int fCode = fCodeB; pattern = dct_data[y][x].pattern; fMotionX = dct_data[y][x].fmotionX; fMotionY = dct_data[y][x].fmotionY; bMotionX = dct_data[y][x].bmotionX; bMotionY = dct_data[y][x].bmotionY; if ( pixelFullSearch ) { fMotionX /= 2; fMotionY /= 2; bMotionX /= 2; bMotionY /= 2; } /* create flat blocks and update pattern if necessary */ calc_blocks: /* Note DoQuant references QScale, overflowChange, overflowValue, pattern, and the calc_blocks label */ DoQuant(0x20, dct[y][x], fba[0]); DoQuant(0x10, dct[y][x+1], fba[1]); DoQuant(0x08, dct[y+1][x], fba[2]); DoQuant(0x04, dct[y+1][x+1], fba[3]); DoQuant(0x02, dctb[y>>1][x>>1], fba[4]); DoQuant(0x01, dctr[y>>1][x>>1], fba[5]); motionForward = (dct_data[y][x].mode != MOTION_BACKWARD); motionBackward = (dct_data[y][x].mode != MOTION_FORWARD); /* Encode Vectors */ if ( motionForward ) { /* transform the fMotion vector into the appropriate values */ offsetX = fMotionX - oldFMotionX; offsetY = fMotionY - oldFMotionY; ENCODE_MOTION_VECTOR(offsetX, offsetY, fMotionXquot, fMotionYquot, fMotionXrem, fMotionYrem, FORW_F); oldFMotionX = fMotionX; oldFMotionY = fMotionY; } if ( motionBackward ) { /* transform the bMotion vector into the appropriate values */ offsetX = bMotionX - oldBMotionX; offsetY = bMotionY - oldBMotionY; ENCODE_MOTION_VECTOR(offsetX, offsetY, bMotionXquot, bMotionYquot, bMotionXrem, bMotionYrem, BACK_F); oldBMotionX = bMotionX; oldBMotionY = bMotionY; } oldMode = dct_data[y][x].mode; if ( printSNR ) { /* Need to decode */ if ( pixelFullSearch ) { fMotionX *= 2; fMotionY *= 2; bMotionX *= 2; bMotionY *= 2; } for ( idx = 0; idx < 6; idx++ ) { if ( pattern & (1 << (5-idx)) ) { Mpost_UnQuantZigBlock(fba[idx], dec[idx], QScale, FALSE); mpeg_jrevdct((int16 *)dec[idx]); } else { memset((char *)dec[idx], 0, sizeof(Block)); } } /* now add the motion block */ AddBMotionBlock(dec[0], prev->decoded_y, next->decoded_y, y, x, mode, fMotionY, fMotionX, bMotionY, bMotionX); AddBMotionBlock(dec[1], prev->decoded_y, next->decoded_y, y, x+1, mode, fMotionY, fMotionX, bMotionY, bMotionX); AddBMotionBlock(dec[2], prev->decoded_y, next->decoded_y, y+1, x, mode, fMotionY, fMotionX, bMotionY, bMotionX); AddBMotionBlock(dec[3], prev->decoded_y, next->decoded_y, y+1, x+1, mode, fMotionY, fMotionX, bMotionY, bMotionX); AddBMotionBlock(dec[4], prev->decoded_cb, next->decoded_cb, y>>1, x>>1, mode, fMotionY/2, fMotionX/2, bMotionY/2, bMotionX/2); AddBMotionBlock(dec[5], prev->decoded_cr, next->decoded_cr, y>>1, x>>1, mode, fMotionY/2, fMotionX/2, bMotionY/2, bMotionX/2); /* now, unblockify */ BlockToData(curr->decoded_y, dec[0], y, x); BlockToData(curr->decoded_y, dec[1], y, x+1); BlockToData(curr->decoded_y, dec[2], y+1, x); BlockToData(curr->decoded_y, dec[3], y+1, x+1); BlockToData(curr->decoded_cb, dec[4], y>>1, x>>1); BlockToData(curr->decoded_cr, dec[5], y>>1, x>>1); } /* reset because non-intra-coded */ y_dc_pred = cr_dc_pred = cb_dc_pred = 128; lastIntra = FALSE; mode = dct_data[y][x].mode; /* DBG_PRINT(("MB Header(%d,%d)\n", x, y)); */ Mhead_GenMBHeader(bb, 3 /* pict_code_type */, mbAddrInc /* addr_incr */, QScale /* q_scale */, fCodeB /* forw_f_code */, fCodeB /* back_f_code */, fMotionXrem /* horiz_forw_r */, fMotionYrem /* vert_forw_r */, bMotionXrem /* horiz_back_r */, bMotionYrem /* vert_back_r */, motionForward /* motion_forw */, fMotionXquot /* m_horiz_forw */, fMotionYquot /* m_vert_forw */, motionBackward /* motion_back */, bMotionXquot /* m_horiz_back */, bMotionYquot /* m_vert_back */, pattern /* mb_pattern */, FALSE /* mb_intra */); mbAddrInc = 1; /* now output the difference */ for ( tempX = 0; tempX < 6; tempX++ ) { if ( GET_ITH_BIT(pattern, 5-tempX) ) { Mpost_RLEHuffPBlock(fba[tempX], bb); } } switch (mode) { case MOTION_FORWARD: numBFOBits += (bb->cumulativeBits-totalBits); break; case MOTION_BACKWARD: numBBABits += (bb->cumulativeBits-totalBits); break; case MOTION_INTERPOLATE: numBINBits += (bb->cumulativeBits-totalBits); break; default: fprintf(stderr, "PROGRAMMER ERROR: Illegal mode: %d\n", mode); exit(1); } numBBits += (bb->cumulativeBits-totalBits); totalBits = bb->cumulativeBits; if (overflowChange) { /* undo an overflow-caused Qscale change */ overflowChange = FALSE; QScale -= overflowValue; overflowValue = 0; } } /* if I-block, skip, or B */ mbAddress++; /* Rate Control */ if (bitstreamMode == FIXED_RATE) { incMacroBlockBits( bb->cumulativeBits - rc_blockStart); rc_blockStart = bb->cumulativeBits; MB_RateOut(TYPE_BFRAME); } } } if ( printSNR ) { BlockComputeSNR(curr,snr,psnr); totalSNR += snr[0]; totalPSNR += psnr[0]; } Mhead_GenSliceEnder(bb); /* Rate Control */ if (bitstreamMode == FIXED_RATE) { updateRateControl(TYPE_BFRAME); } endTime = time_elapsed(); totalTime += (endTime-startTime); if ( ( ! childProcess) && showBitRatePerFrame ) { /* ASSUMES 30 FRAMES PER SECOND */ fprintf(bitRateFile, "%5d\t%8d\n", curr->id, 30*(bb->cumulativeBits-totalFrameBits)); } if ( (! childProcess) && frameSummary && !realQuiet) { fprintf(stdout, "FRAME %d (B): I BLOCKS: %5d; B BLOCKS: %5d SKIPPED: %5d (%ld seconds)\n", curr->id, numIBlocks, numBBlocks, numSkipped, (long)((endTime-startTime)/TIME_RATE)); if ( printSNR ) fprintf(stdout, "FRAME %d: SNR: %.1f\t%.1f\t%.1f\tPSNR: %.1f\t%.1f\t%.1f\n", curr->id, snr[0], snr[1], snr[2], psnr[0], psnr[1], psnr[2]); } numFrameBits += (bb->cumulativeBits-totalFrameBits); numBIBlocks += numIBlocks; numBBBlocks += numBBlocks; numBSkipped += numSkipped; numBIBits += numIBits; numBBBits += numBBits; } /*===========================================================================* * * SetBQScale * * set the B-frame Q-scale * * RETURNS: nothing * * SIDE EFFECTS: qscaleB * *===========================================================================*/ void SetBQScale(qB) int qB; { qscaleB = qB; } /*===========================================================================* * * GetBQScale * * get the B-frame Q-scale * * RETURNS: the Q-scale * * SIDE EFFECTS: none * *===========================================================================*/ int GetBQScale() { return qscaleB; } /*===========================================================================* * * ResetBFrameStats * * reset the B-frame stats * * RETURNS: nothing * * SIDE EFFECTS: none * *===========================================================================*/ void ResetBFrameStats() { numBIBlocks = 0; numBBBlocks = 0; numBSkipped = 0; numBIBits = 0; numBBBits = 0; numFrames = 0; numFrameBits = 0; totalTime = 0; } /*===========================================================================* * * ShowBFrameSummary * * print out statistics on all B-frames * * RETURNS: time taken for B-frames (in seconds) * * SIDE EFFECTS: none * *===========================================================================*/ float ShowBFrameSummary(inputFrameBits, totalBits, fpointer) int inputFrameBits; int32 totalBits; FILE *fpointer; { if ( numFrames == 0 ) { return 0.0; } fprintf(fpointer, "-------------------------\n"); fprintf(fpointer, "*****B FRAME SUMMARY*****\n"); fprintf(fpointer, "-------------------------\n"); if ( numBIBlocks != 0 ) { fprintf(fpointer, " I Blocks: %5d (%6d bits) (%5d bpb)\n", numBIBlocks, numBIBits, numBIBits/numBIBlocks); } else { fprintf(fpointer, " I Blocks: %5d\n", 0); } if ( numBBBlocks != 0 ) { fprintf(fpointer, " B Blocks: %5d (%6d bits) (%5d bpb)\n", numBBBlocks, numBBBits, numBBBits/numBBBlocks); fprintf(fpointer, " B types: %5d (%4d bpb) forw %5d (%4d bpb) back %5d (%4d bpb) bi\n", numBFOBlocks, (numBFOBlocks==0)?0:numBFOBits/numBFOBlocks, numBBABlocks, (numBBABlocks==0)?0:numBBABits/numBBABlocks, numBINBlocks, (numBINBlocks==0)?0:numBINBits/numBINBlocks); } else { fprintf(fpointer, " B Blocks: %5d\n", 0); } fprintf(fpointer, " Skipped: %5d\n", numBSkipped); 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/TIME_RATE), (float)((float)(TIME_RATE*numFrames)/(float)totalTime), (long)((float)TIME_RATE*(float)numFrames*(float)inputFrameBits/(24.0*(float)totalTime)), (long)((float)TIME_RATE*(float)numFrames*(float)inputFrameBits/(256.0*24.0*(float)totalTime))); } return (float)totalTime/TIME_RATE; } /*===========================================================================* * * ComputeBMotionLumBlock * * compute the luminance block resulting from motion compensation * * RETURNS: motionBlock modified * * SIDE EFFECTS: none * * PRECONDITION: the motion vectors must be valid! * *===========================================================================*/ void ComputeBMotionLumBlock(prev, next, by, bx, mode, fmy, fmx, bmy, bmx, motionBlock) MpegFrame *prev; MpegFrame *next; int by; int bx; int mode; int fmy; int fmx; int bmy; int bmx; LumBlock motionBlock; { LumBlock prevBlock, nextBlock; register int y, x; switch(mode) { case MOTION_FORWARD: ComputeMotionLumBlock(prev, by, bx, fmy, fmx, motionBlock); break; case MOTION_BACKWARD: ComputeMotionLumBlock(next, by, bx, bmy, bmx, motionBlock); break; case MOTION_INTERPOLATE: ComputeMotionLumBlock(prev, by, bx, fmy, fmx, prevBlock); ComputeMotionLumBlock(next, by, bx, bmy, bmx, nextBlock); for ( y = 0; y < 16; y++ ) { for ( x = 0; x < 16; x++ ) { motionBlock[y][x] = (prevBlock[y][x]+nextBlock[y][x]+1)/2; } } break; default: fprintf(stderr, "Bad mode!\nProgrammer error!\n"); break; } } /*===========================================================================* * * EstimateSecondsPerBFrame * * estimate the seconds to compute a B-frame * * RETURNS: the time, in seconds * * SIDE EFFECTS: none * *===========================================================================*/ float EstimateSecondsPerBFrame() { if ( numFrames == 0 ) { return 20.0; } else { return (float)totalTime/((float)TIME_RATE*(float)numFrames); } } /*=====================* * INTERNAL PROCEDURES * *=====================*/ /*===========================================================================* * * ComputeBMotionBlock * * compute the block resulting from motion compensation * * RETURNS: motionBlock is modified * * SIDE EFFECTS: none * * PRECONDITION: the motion vectors must be valid! * *===========================================================================*/ static void ComputeBMotionBlock(prev, next, by, bx, mode, fmy, fmx, bmy, bmx, motionBlock, type) MpegFrame *prev; MpegFrame *next; int by; int bx; int mode; int fmy; int fmx; int bmy; int bmx; Block motionBlock; int type; { Block prevBlock, nextBlock; register int y, x; switch(mode) { case MOTION_FORWARD: if ( type == LUM_BLOCK ) { ComputeMotionBlock(prev->ref_y, by, bx, fmy, fmx, motionBlock); } else if ( type == CB_BLOCK ) { ComputeMotionBlock(prev->ref_cb, by, bx, fmy, fmx, motionBlock); } else if ( type == CR_BLOCK ) { ComputeMotionBlock(prev->ref_cr, by, bx, fmy, fmx, motionBlock); } break; case MOTION_BACKWARD: if ( type == LUM_BLOCK ) { ComputeMotionBlock(next->ref_y, by, bx, bmy, bmx, motionBlock); } else if ( type == CB_BLOCK ) { ComputeMotionBlock(next->ref_cb, by, bx, bmy, bmx, motionBlock); } else if ( type == CR_BLOCK ) { ComputeMotionBlock(next->ref_cr, by, bx, bmy, bmx, motionBlock); } break; case MOTION_INTERPOLATE: if ( type == LUM_BLOCK ) { ComputeMotionBlock(prev->ref_y, by, bx, fmy, fmx, prevBlock); ComputeMotionBlock(next->ref_y, by, bx, bmy, bmx, nextBlock); } else if ( type == CB_BLOCK ) { ComputeMotionBlock(prev->ref_cb, by, bx, fmy, fmx, prevBlock); ComputeMotionBlock(next->ref_cb, by, bx, bmy, bmx, nextBlock); } else if ( type == CR_BLOCK ) { ComputeMotionBlock(prev->ref_cr, by, bx, fmy, fmx, prevBlock); ComputeMotionBlock(next->ref_cr, by, bx, bmy, bmx, nextBlock); } for ( y = 0; y < 8; y++ ) { for ( x = 0; x < 8; x++ ) { motionBlock[y][x] = (prevBlock[y][x]+nextBlock[y][x]+1)/2; } } break; } } /*===========================================================================* * * ComputeBDiffDCTs * * compute the DCT of the error term * * RETURNS: appropriate blocks of current will contain the DCTs * * SIDE EFFECTS: none * * PRECONDITION: the motion vectors must be valid! * *===========================================================================*/ static void ComputeBDiffDCTs(current, prev, next, by, bx, mode, fmy, fmx, bmy, bmx, pattern) MpegFrame *current; MpegFrame *prev; MpegFrame *next; int by; int bx; int mode; int fmy; int fmx; int bmy; int bmx; int *pattern; { Block motionBlock; if ( *pattern & 0x20 ) { ComputeBMotionBlock(prev, next, by, bx, mode, fmy, fmx, bmy, bmx, motionBlock, LUM_BLOCK); if (! ComputeDiffDCTBlock(current->y_blocks[by][bx], dct[by][bx], motionBlock)) { *pattern ^= 0x20; } } if ( *pattern & 0x10 ) { ComputeBMotionBlock(prev, next, by, bx+1, mode, fmy, fmx, bmy, bmx, motionBlock, LUM_BLOCK); if (! ComputeDiffDCTBlock(current->y_blocks[by][bx+1], dct[by][bx+1], motionBlock)) { *pattern ^= 0x10; } } if ( *pattern & 0x8 ) { ComputeBMotionBlock(prev, next, by+1, bx, mode, fmy, fmx, bmy, bmx, motionBlock, LUM_BLOCK); if (! ComputeDiffDCTBlock(current->y_blocks[by+1][bx], dct[by+1][bx], motionBlock)) { *pattern ^= 0x8; } } if ( *pattern & 0x4 ) { ComputeBMotionBlock(prev, next, by+1, bx+1, mode, fmy, fmx, bmy, bmx, motionBlock, LUM_BLOCK); if (! ComputeDiffDCTBlock(current->y_blocks[by+1][bx+1], dct[by+1][bx+1], motionBlock)) { *pattern ^= 0x4; } } if ( *pattern & 0x2 ) { ComputeBMotionBlock(prev, next, by>>1, bx>>1, mode, fmy/2, fmx/2, bmy/2, bmx/2, motionBlock, CB_BLOCK); if (! ComputeDiffDCTBlock(current->cb_blocks[by >> 1][bx >> 1], dctb[by >> 1][bx >> 1], motionBlock)) { *pattern ^= 0x2; } } if ( *pattern & 0x1 ) { ComputeBMotionBlock(prev, next, by>>1, bx>>1, mode, fmy/2, fmx/2, bmy/2, bmx/2, motionBlock, CR_BLOCK); if (! ComputeDiffDCTBlock(current->cr_blocks[by >> 1][bx >> 1], dctr[by >> 1][bx >> 1], motionBlock)) { *pattern ^= 0x1; } } } /*===========================================================================* * * USER-MODIFIABLE * * DoBIntraCode * * decides if this block should be coded as intra-block * * RETURNS: TRUE if intra-coding should be used; FALSE otherwise * * SIDE EFFECTS: none * * PRECONDITION: the motion vectors must be valid! * *===========================================================================*/ static boolean DoBIntraCode(current, prev, next, by, bx, mode, fmy, fmx, bmy, bmx) MpegFrame *current; MpegFrame *prev; MpegFrame *next; int by; int bx; int mode; int fmy; int fmx; int bmy; int bmx; { int x, y; int32 sum = 0, vard = 0, varc = 0, dif; int32 currPixel, prevPixel; LumBlock motionBlock; int fy, fx; ComputeBMotionLumBlock(prev, next, by, bx, mode, fmy, fmx, bmy, bmx, motionBlock); MOTION_TO_FRAME_COORD(by, bx, 0, 0, fy, fx); for ( y = 0; y < 16; y++ ) { for ( x = 0; x < 16; x++ ) { currPixel = current->orig_y[fy+y][fx+x]; prevPixel = motionBlock[y][x]; sum += currPixel; varc += currPixel*currPixel; dif = currPixel - prevPixel; vard += dif*dif; } } vard >>= 8; /* divide by 256; assumes mean is close to zero */ varc = (varc>>8) - (sum>>8)*(sum>>8); if ( vard <= 64 ) { return FALSE; } else if ( vard < varc ) { return FALSE; } else { return TRUE; } } static int ComputeBlockColorDiff(current, motionBlock) Block current, motionBlock; { register int x, y, diff_total = 0, diff_tmp; for ( y = 0; y < 8; y++ ) { for ( x = 0; x < 8; x++ ) { diff_tmp = current[y][x] - motionBlock[y][x]; diff_total += ABS(diff_tmp); } } return diff_total; } /*===========================================================================* * * USER-MODIFIABLE * * MotionSufficient * * decides if this motion vector is sufficient without DCT coding * * RETURNS: TRUE if no DCT is needed; FALSE otherwise * * SIDE EFFECTS: none * * PRECONDITION: the motion vectors must be valid! * *===========================================================================*/ static boolean MotionSufficient(curr, currBlock, prev, next, by, bx, mode, fmy, fmx, bmy, bmx) MpegFrame *curr; LumBlock currBlock; MpegFrame *prev; MpegFrame *next; int by, bx; int mode; int fmy, fmx; int bmy, bmx; { LumBlock mLumBlock; Block mColorBlock; int lumErr, colorErr; /* check bounds */ if ( mode != MOTION_BACKWARD ) { if ( (by*DCTSIZE+(fmy-1)/2 < 0) || ((by+2)*DCTSIZE+(fmy+1)/2-1 >= Fsize_y) ) { return FALSE; } if ( (bx*DCTSIZE+(fmx-1)/2 < 0) || ((bx+2)*DCTSIZE+(fmx+1)/2-1 >= Fsize_x) ) { return FALSE; } } if ( mode != MOTION_FORWARD ) { if ( (by*DCTSIZE+(bmy-1)/2 < 0) || ((by+2)*DCTSIZE+(bmy+1)/2-1 >= Fsize_y) ) { return FALSE; } if ( (bx*DCTSIZE+(bmx-1)/2 < 0) || ((bx+2)*DCTSIZE+(bmx+1)/2-1 >= Fsize_x) ) { return FALSE; } } /* check Lum */ ComputeBMotionLumBlock(prev, next, by, bx, mode, fmy, fmx, bmy, bmx, mLumBlock); lumErr = LumBlockMAD(currBlock, mLumBlock, 0x7fffffff); if (lumErr > 512) { return FALSE; } /* check color */ ComputeBMotionBlock(prev, next, by>>1, bx>>1, mode, fmy/2, fmx/2, bmy/2, bmx/2, mColorBlock, CR_BLOCK); colorErr = ComputeBlockColorDiff(curr->cr_blocks[by >> 1][bx >> 1], mColorBlock); ComputeBMotionBlock(prev, next, by>>1, bx>>1, mode, fmy/2, fmx/2, bmy/2, bmx/2, mColorBlock, CB_BLOCK); colorErr += ComputeBlockColorDiff(curr->cr_blocks[by >> 1][bx >> 1], mColorBlock); return (colorErr < 256); /* lumErr checked above */ }