/*===========================================================================* * bframe.c * * Procedures concerned with the B-frame encoding *===========================================================================*/ /* * 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 FILES * *==============*/ #include "all.h" #include #include #include "ppm.h" #include "mtypes.h" #include "bitio.h" #include "frames.h" #include "prototypes.h" #include "block.h" #include "fsize.h" #include "param.h" #include "mheaders.h" #include "postdct.h" #include "rate.h" #include "opts.h" #include "specifics.h" extern int **bfmvHistogram; extern int **bbmvHistogram; /*==================* * STATIC VARIABLES * *==================*/ static int32 totalTime = 0; static int qscaleB; static float totalSNR = 0.0; static float totalPSNR = 0.0; static struct bframeStats { unsigned int BSkipped; unsigned int BIBits; unsigned int BBBits; unsigned int Frames; unsigned int FrameBits; unsigned int BIBlocks; unsigned int BBBlocks; unsigned int BFOBlocks; /* forward only */ unsigned int BBABlocks; /* backward only */ unsigned int BINBlocks; /* interpolate */ unsigned int BFOBits; unsigned int BBABits; unsigned int BINBits; } bframeStats = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /*====================* * EXTERNAL VARIABLES * *====================*/ extern Block **dct, **dctr, **dctb; #define NO_MOTION 0 #define MOTION 1 #define SKIP 2 /* used in useMotion in dct_data */ /*=====================* * INTERNAL PROCEDURES * *=====================*/ static void zeroMotion(motion * const motionP) { motionP->fwd.y = motionP->fwd.x = motionP->bwd.y = motionP->bwd.x = 0; } static motion halfMotion(motion const motion) { struct motion half; half.fwd.y = motion.fwd.y / 2; half.fwd.x = motion.fwd.x / 2; half.bwd.y = motion.bwd.y / 2; half.bwd.x = motion.bwd.x / 2; return half; } /*===========================================================================* * * compute the block resulting from motion compensation * * RETURNS: motionBlock is modified * * SIDE EFFECTS: none * * PRECONDITION: the motion vectors must be valid! * *===========================================================================*/ static void ComputeBMotionBlock(MpegFrame * const prev, MpegFrame * const next, int const by, int const bx, int const mode, motion const motion, Block * const motionBlockP, int const type) { Block prevBlock, nextBlock; switch(mode) { case MOTION_FORWARD: switch (type) { case LUM_BLOCK: ComputeMotionBlock(prev->ref_y, by, bx, motion.fwd, motionBlockP); break; case CB_BLOCK: ComputeMotionBlock(prev->ref_cb, by, bx, motion.fwd, motionBlockP); break; case CR_BLOCK: ComputeMotionBlock(prev->ref_cr, by, bx, motion.fwd, motionBlockP); } break; case MOTION_BACKWARD: switch (type) { case LUM_BLOCK: ComputeMotionBlock(next->ref_y, by, bx, motion.bwd, motionBlockP); break; case CB_BLOCK: ComputeMotionBlock(next->ref_cb, by, bx, motion.bwd, motionBlockP); break; case CR_BLOCK: ComputeMotionBlock(next->ref_cr, by, bx, motion.bwd, motionBlockP); break; } break; case MOTION_INTERPOLATE: switch (type) { case LUM_BLOCK: ComputeMotionBlock(prev->ref_y, by, bx, motion.fwd, &prevBlock); ComputeMotionBlock(next->ref_y, by, bx, motion.bwd, &nextBlock); break; case CB_BLOCK: ComputeMotionBlock(prev->ref_cb, by, bx, motion.fwd, &prevBlock); ComputeMotionBlock(next->ref_cb, by, bx, motion.bwd, &nextBlock); break; case CR_BLOCK: ComputeMotionBlock(prev->ref_cr, by, bx, motion.fwd, &prevBlock); ComputeMotionBlock(next->ref_cr, by, bx, motion.bwd, &nextBlock); break; } { unsigned int y; for (y = 0; y < 8; ++y) { int16 * const blockRow = (*motionBlockP)[y]; int16 * const prevRow = prevBlock[y]; int16 * const nextRow = nextBlock[y]; unsigned int x; for (x = 0; x < 8; ++x) blockRow[x] = (prevRow[x] + nextRow[x] + 1) / 2; } } break; } } /*===========================================================================* * * 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(MpegFrame * const current, MpegFrame * const prev, MpegFrame * const next, int const by, int const bx, int const mode, motion const motion, int * const patternP) { Block motionBlock; if (*patternP & 0x20) { boolean significantDiff; ComputeBMotionBlock(prev, next, by, bx, mode, motion, &motionBlock, LUM_BLOCK); ComputeDiffDCTBlock(current->y_blocks[by][bx], dct[by][bx], motionBlock, &significantDiff); if (!significantDiff) *patternP ^= 0x20; } if (*patternP & 0x10) { boolean significantDiff; ComputeBMotionBlock(prev, next, by, bx+1, mode, motion, &motionBlock, LUM_BLOCK); ComputeDiffDCTBlock(current->y_blocks[by][bx+1], dct[by][bx+1], motionBlock, &significantDiff); if (!significantDiff) *patternP ^= 0x10; } if (*patternP & 0x8) { boolean significantDiff; ComputeBMotionBlock(prev, next, by+1, bx, mode, motion, &motionBlock, LUM_BLOCK); ComputeDiffDCTBlock(current->y_blocks[by+1][bx], dct[by+1][bx], motionBlock, &significantDiff); if (!significantDiff) *patternP ^= 0x8; } if (*patternP & 0x4) { boolean significantDiff; ComputeBMotionBlock(prev, next, by+1, bx+1, mode, motion, &motionBlock, LUM_BLOCK); ComputeDiffDCTBlock(current->y_blocks[by+1][bx+1], dct[by+1][bx+1], motionBlock, &significantDiff); if (!significantDiff) *patternP ^= 0x4; } if (*patternP & 0x2) { boolean significantDiff; ComputeBMotionBlock(prev, next, by/2, bx/2, mode, halfMotion(motion), &motionBlock, CB_BLOCK); ComputeDiffDCTBlock(current->cb_blocks[by/2][bx/2], dctb[by/2][bx/2], motionBlock, &significantDiff); if (!significantDiff) *patternP ^= 0x2; } if (*patternP & 0x1) { boolean significantDiff; ComputeBMotionBlock(prev, next, by/2, bx/2, mode, halfMotion(motion), &motionBlock, CR_BLOCK); ComputeDiffDCTBlock(current->cr_blocks[by/2][bx/2], dctr[by/2][bx/2], motionBlock, &significantDiff); if (!significantDiff) *patternP ^= 0x1; } } /*===========================================================================* * * 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(MpegFrame * const current, MpegFrame * const prev, MpegFrame * const next, int const by, int const bx, int const mode, motion const motion) { boolean retval; unsigned int y; int32 sum = 0, vard = 0, varc = 0; LumBlock motionBlock; int fy, fx; ComputeBMotionLumBlock(prev, next, by, bx, mode, motion, &motionBlock); MotionToFrameCoord(by, bx, 0, 0, &fy, &fx); for (y = 0; y < 16; ++y) { unsigned int x; for (x = 0; x < 16; ++x) { int32 const currPixel = current->orig_y[fy+y][fx+x]; int32 const prevPixel = motionBlock.l[y][x]; int32 const dif = currPixel - prevPixel; sum += currPixel; varc += SQR(currPixel); vard += SQR(dif); } } vard >>= 8; /* divide by 256; assumes mean is close to zero */ varc = (varc>>8) - (sum>>8)*(sum>>8); if (vard <= 64) retval = FALSE; else if (vard < varc) retval = FALSE; else retval = TRUE; return retval; } static int ComputeBlockColorDiff(Block current, Block motionBlock) { unsigned int y; int diffTotal; diffTotal = 0; for (y = 0; y < 8; ++y) { unsigned int x; for (x = 0; x < 8; ++x) { int const diffTmp = current[y][x] - motionBlock[y][x]; diffTotal += ABS(diffTmp); } } return diffTotal; } /*=====================* * EXPORTED PROCEDURES * *=====================*/ /*===========================================================================* * 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(MpegFrame * const curr, const LumBlock * const currBlockP, MpegFrame * const prev, MpegFrame * const next, int const by, int const bx, int const mode, motion const motion) { LumBlock mLumBlock; Block mColorBlock; int lumErr, colorErr; /* check bounds */ if ( mode != MOTION_BACKWARD ) { if ( (by*DCTSIZE+(motion.fwd.y-1)/2 < 0) || ((by+2)*DCTSIZE+(motion.fwd.y+1)/2-1 >= Fsize_y) ) { return FALSE; } if ( (bx*DCTSIZE+(motion.fwd.x-1)/2 < 0) || ((bx+2)*DCTSIZE+(motion.fwd.x+1)/2-1 >= Fsize_x) ) { return FALSE; } } if ( mode != MOTION_FORWARD ) { if ( (by*DCTSIZE+(motion.bwd.y-1)/2 < 0) || ((by+2)*DCTSIZE+(motion.bwd.y+1)/2-1 >= Fsize_y) ) { return FALSE; } if ( (bx*DCTSIZE+(motion.bwd.x-1)/2 < 0) || ((bx+2)*DCTSIZE+(motion.bwd.x+1)/2-1 >= Fsize_x) ) { return FALSE; } } /* check Lum */ ComputeBMotionLumBlock(prev, next, by, bx, mode, motion, &mLumBlock); lumErr = LumBlockMAD(currBlockP, &mLumBlock, 0x7fffffff); if (lumErr > 512) { return FALSE; } /* check color */ ComputeBMotionBlock(prev, next, by/2, bx/2, mode, halfMotion(motion), &mColorBlock, CR_BLOCK); colorErr = ComputeBlockColorDiff(curr->cr_blocks[by/2][bx/2], mColorBlock); ComputeBMotionBlock(prev, next, by/2, bx/2, mode, halfMotion(motion), &mColorBlock, CB_BLOCK); colorErr += ComputeBlockColorDiff(curr->cr_blocks[by/2][bx/2], mColorBlock); return (colorErr < 256); /* lumErr checked above */ } struct stats { int IBlocks; int BBlocks; int Skipped; int totalBits; int IBits; int BBits; }; static void initializeStats(struct stats * const statsP) { statsP->IBlocks = 0; statsP->BBlocks = 0; statsP->Skipped = 0; statsP->totalBits = 0; statsP->IBits = 0; statsP->BBits = 0; } static void checkSpecifics(MpegFrame * const curr, int const mbAddress, int const QScale, boolean * const skipItP, boolean * const doBsearchP, int * const modeP, motion * const motionP) { /*---------------------------------------------------------------------------- We return *modeP iff we return *doBsearchP == FALSE. We return *motionP iff we return *skipItP == FALSE. -----------------------------------------------------------------------------*/ BlockMV * info; SpecLookup(curr->id, 2, mbAddress, &info, QScale); if (info == NULL) { *doBsearchP = TRUE; *skipItP = FALSE; } else { *doBsearchP = FALSE; switch (info->typ) { case TYP_SKIP: *skipItP = TRUE; break; case TYP_FORW: *skipItP = FALSE; motionP->fwd.y = info->fy; motionP->fwd.x = info->fx; *modeP = MOTION_FORWARD; break; case TYP_BACK: *skipItP = FALSE; motionP->bwd.y = info->by; motionP->bwd.x = info->bx; *modeP = MOTION_BACKWARD; break; case TYP_BOTH: *skipItP = FALSE; motionP->fwd.y = info->fy; motionP->fwd.x = info->fx; motionP->bwd.y = info->by; motionP->bwd.x = info->bx; *modeP = MOTION_INTERPOLATE; break; default: pm_error("Unreachable code in GenBFrame!"); } } } static void makeNonSkipBlock(int const y, int const x, MpegFrame * const curr, MpegFrame * const prev, MpegFrame * const next, bool const specificsOn, int const mbAddress, int const QScale, const LumBlock * const currentBlockP, int * const modeP, int * const oldModeP, boolean const IntrPBAllowed, boolean * const lastIntraP, motion * const motionP, motion * const oldMotionP, struct stats * const statsP) { motion motion; int mode; boolean skipIt; boolean doBsearch; if (specificsOn) checkSpecifics(curr, mbAddress, QScale, &skipIt, &doBsearch, &mode, &motion); else { skipIt = FALSE; doBsearch = TRUE; } if (skipIt) dct_data[y][x].useMotion = SKIP; else { if (doBsearch) { motion = *motionP; /* start with old motion */ mode = BMotionSearch(currentBlockP, prev, next, y, x, &motion, mode); } /* STEP 2: INTRA OR NON-INTRA CODING */ if (IntraPBAllowed && DoBIntraCode(curr, prev, next, y, x, mode, motion)) { /* output I-block inside a B-frame */ ++statsP->IBlocks; zeroMotion(oldMotionP); *lastIntraP = TRUE; dct_data[y][x].useMotion = NO_MOTION; *oldModeP = 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 */ int pattern; pattern = 63; *lastIntraP = FALSE; ++statsP->BBlocks; dct_data[y][x].mode = mode; *oldModeP = mode; dct_data[y][x].fmotionY = motion.fwd.y; dct_data[y][x].fmotionX = motion.fwd.x; dct_data[y][x].bmotionY = motion.bwd.y; dct_data[y][x].bmotionX = motion.bwd.x; switch (mode) { case MOTION_FORWARD: ++bframeStats.BFOBlocks; oldMotionP->fwd = motion.fwd; break; case MOTION_BACKWARD: ++bframeStats.BBABlocks; oldMotionP->bwd = motion.bwd; break; case MOTION_INTERPOLATE: ++bframeStats.BINBlocks; *oldMotionP = motion; break; default: pm_error("INTERNAL ERROR: Illegal mode: %d", mode); } ComputeBDiffDCTs(curr, prev, next, y, x, mode, motion, &pattern); dct_data[y][x].pattern = pattern; dct_data[y][x].useMotion = MOTION; if ( computeMVHist ) { assert(motion.fwd.x + searchRangeB + 1 >= 0); assert(motion.fwd.y + searchRangeB + 1 >= 0); assert(motion.fwd.x + searchRangeB + 1 <= 2*searchRangeB + 2); assert(motion.fwd.y + searchRangeB + 1 <= 2*searchRangeB + 2); assert(motion.bwd.x + searchRangeB + 1 >= 0); assert(motion.bwd.y + searchRangeB + 1 >= 0); assert(motion.bwd.x + searchRangeB + 1 <= 2*searchRangeB + 2); assert(motion.bwd.y + searchRangeB + 1 <= 2*searchRangeB + 2); ++bfmvHistogram[motion.fwd.x + searchRangeB + 1] [motion.fwd.y + searchRangeB + 1]; ++bbmvHistogram[motion.bwd.x + searchRangeB + 1] [motion.bwd.y + searchRangeB + 1]; } } /* motion-block */ } *motionP = motion; *modeP = mode; } /*===========================================================================* * * 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(BitBucket * const bb, MpegFrame * const curr, MpegFrame * const prev, MpegFrame * const next) { FlatBlock fba[6], fb[6]; Block dec[6]; int32 y_dc_pred, cr_dc_pred, cb_dc_pred; int x, y; struct motion motion; struct motion oldMotion; int oldMode = MOTION_FORWARD; int mode = MOTION_FORWARD; int offsetX, offsetY; struct motion motionRem; struct motion motionQuot; struct stats stats; boolean lastIntra = TRUE; boolean motionForward, motionBackward; int totalFrameBits; int32 startTime, endTime; int lastX, lastY; int lastBlockX, lastBlockY; 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; assert(prev != NULL); assert(next != NULL); initializeStats(&stats); if (collect_quant) {fprintf(collect_quant_fp, "# B\n");} if (dct == NULL) AllocDctBlocks(); ++bframeStats.Frames; 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; stats.totalBits = bb->cumulativeBits; if ( ! pixelFullSearch ) { if ( ! prev->halfComputed ) { ComputeHalfPixelData(prev); } if ( ! next->halfComputed ) { ComputeHalfPixelData(next); } } lastBlockX = Fsize_x / 8; lastBlockY = Fsize_y / 8; lastX = lastBlockX - 2; lastY = lastBlockY - 2; mbAddress = 0; /* Start with zero motion assumption */ zeroMotion(&motion); zeroMotion(&oldMotion); zeroMotion(&motionRem); zeroMotion(&motionQuot); /* 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.l[iy][ix] = (int16)curr->orig_y[fy+iy][fx+ix]; } } if (slicePos == 0) { zeroMotion(&oldMotion); 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, ¤tBlock, prev, next, y, x, oldMode, oldMotion); } else make_skip_block = FALSE; if (make_skip_block) { /* skipped macro block */ dct_data[y][x].useMotion = SKIP; } else makeNonSkipBlock(y, x, curr, prev, next, specificsOn, mbAddress, QScale, ¤tBlock, &mode, &oldMode, IntraPBAllowed, &lastIntra, &motion, &oldMotion, &stats); ++mbAddress; } } /* reset everything */ zeroMotion(&oldMotion); 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 */ zeroMotion(&oldMotion); 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; stats.IBits += (bb->cumulativeBits - stats.totalBits); stats.totalBits = bb->cumulativeBits; /* reset because intra-coded */ zeroMotion(&oldMotion); 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) { ++stats.Skipped; mbAddrInc++; /* decode skipped block */ if (printSNR) { struct motion motion; for (idx = 0; idx < 6; ++idx) memset((char *)dec[idx], 0, sizeof(Block)); if (pixelFullSearch) { motion.fwd.y = 2 * oldMotion.fwd.y; motion.fwd.x = 2 * oldMotion.fwd.x; motion.bwd.y = 2 * oldMotion.bwd.y; motion.bwd.x = 2 * oldMotion.bwd.x; } else motion = oldMotion; /* now add the motion block */ AddBMotionBlock(dec[0], prev->decoded_y, next->decoded_y, y, x, mode, motion); AddBMotionBlock(dec[1], prev->decoded_y, next->decoded_y, y, x+1, mode, motion); AddBMotionBlock(dec[2], prev->decoded_y, next->decoded_y, y+1, x, mode, motion); AddBMotionBlock(dec[3], prev->decoded_y, next->decoded_y, y+1, x+1, mode, motion); AddBMotionBlock(dec[4], prev->decoded_cb, next->decoded_cb, y/2, x/2, mode, halfMotion(motion)); AddBMotionBlock(dec[5], prev->decoded_cr, next->decoded_cr, y/2, x/2, mode, halfMotion(motion)); /* 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/2, x/2); BlockToData(curr->decoded_cr, dec[5], y/2, x/2); } } else /* B block */ { int const fCode = fCodeB; int pattern; pattern = dct_data[y][x].pattern; motion.fwd.y = dct_data[y][x].fmotionY; motion.fwd.x = dct_data[y][x].fmotionX; motion.bwd.y = dct_data[y][x].bmotionY; motion.bwd.x = dct_data[y][x].bmotionX; if (pixelFullSearch) motion = halfMotion(motion); /* 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/2][x/2], fba[4]); DoQuant(0x01, dctr[y/2][x/2], 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 */ offsetY = motion.fwd.y - oldMotion.fwd.y; offsetX = motion.fwd.x - oldMotion.fwd.x; encodeMotionVector(offsetX, offsetY, &motionQuot.fwd, &motionRem.fwd, FORW_F, fCode); oldMotion.fwd = motion.fwd; } if (motionBackward) { /* transform the bMotion vector into the appropriate values */ offsetY = motion.bwd.y - oldMotion.bwd.y; offsetX = motion.bwd.x - oldMotion.bwd.x; encodeMotionVector(offsetX, offsetY, &motionQuot.bwd, &motionRem.bwd, BACK_F, fCode); oldMotion.bwd = motion.bwd; } oldMode = dct_data[y][x].mode; if (printSNR) { /* Need to decode */ if (pixelFullSearch) { motion.fwd.x *= 2; motion.fwd.y *= 2; motion.bwd.x *= 2; motion.bwd.y *= 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, motion); AddBMotionBlock(dec[1], prev->decoded_y, next->decoded_y, y, x+1, mode, motion); AddBMotionBlock(dec[2], prev->decoded_y, next->decoded_y, y+1, x, mode, motion); AddBMotionBlock(dec[3], prev->decoded_y, next->decoded_y, y+1, x+1, mode, motion); AddBMotionBlock(dec[4], prev->decoded_cb, next->decoded_cb, y/2, x/2, mode, halfMotion(motion)); AddBMotionBlock(dec[5], prev->decoded_cr, next->decoded_cr, y/2, x/2, mode, halfMotion(motion)); /* 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/2, x/2); BlockToData(curr->decoded_cr, dec[5], y/2, x/2); } /* 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 */, motionRem.fwd.x /* horiz_forw_r */, motionRem.fwd.y /* vert_forw_r */, motionRem.bwd.x /* horiz_back_r */, motionRem.bwd.y /* vert_back_r */, motionForward /* motion_forw */, motionQuot.fwd.x /* m_horiz_forw */, motionQuot.fwd.y /* m_vert_forw */, motionBackward /* motion_back */, motionQuot.bwd.x /* m_horiz_back */, motionQuot.bwd.y /* m_vert_back */, pattern /* mb_pattern */, FALSE /* mb_intra */); mbAddrInc = 1; /* now output the difference */ { unsigned int x; for (x = 0; x < 6; ++x) { if (GET_ITH_BIT(pattern, 5-x)) Mpost_RLEHuffPBlock(fba[x], bb); } } switch (mode) { case MOTION_FORWARD: bframeStats.BFOBits += (bb->cumulativeBits - stats.totalBits); break; case MOTION_BACKWARD: bframeStats.BBABits += (bb->cumulativeBits - stats.totalBits); break; case MOTION_INTERPOLATE: bframeStats.BINBits += (bb->cumulativeBits - stats.totalBits); break; default: pm_error("PROGRAMMER ERROR: Illegal mode: %d", mode); } stats.BBits += (bb->cumulativeBits - stats.totalBits); stats.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 ( showBitRatePerFrame ) { /* ASSUMES 30 FRAMES PER SECOND */ fprintf(bitRateFile, "%5d\t%8d\n", curr->id, 30*(bb->cumulativeBits-totalFrameBits)); } if ( frameSummary && !realQuiet) { fprintf(stdout, "FRAME %d (B): " "I BLOCKS: %5d; B BLOCKS: %5d SKIPPED: %5d (%ld seconds)\n", curr->id, stats.IBlocks, stats.BBlocks, stats.Skipped, (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]); } bframeStats.FrameBits += (bb->cumulativeBits-totalFrameBits); bframeStats.BIBlocks += stats.IBlocks; bframeStats.BBBlocks += stats.BBlocks; bframeStats.BSkipped += stats.Skipped; bframeStats.BIBits += stats.IBits; bframeStats.BBBits += stats.BBits; } /*===========================================================================* * * 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() { bframeStats.BIBlocks = 0; bframeStats.BBBlocks = 0; bframeStats.BSkipped = 0; bframeStats.BIBits = 0; bframeStats.BBBits = 0; bframeStats.Frames = 0; bframeStats.FrameBits = 0; totalTime = 0; } float BFrameTotalTime(void) { return (float)totalTime/(float)TIME_RATE; } void ShowBFrameSummary(unsigned int const inputFrameBits, unsigned int const totalBits, FILE * const fpointer) { if (bframeStats.Frames > 0) { fprintf(fpointer, "-------------------------\n"); fprintf(fpointer, "*****B FRAME SUMMARY*****\n"); fprintf(fpointer, "-------------------------\n"); if (bframeStats.BIBlocks > 0) { fprintf(fpointer, " I Blocks: %5d (%6d bits) (%5d bpb)\n", bframeStats.BIBlocks, bframeStats.BIBits, bframeStats.BIBits/bframeStats.BIBlocks); } else fprintf(fpointer, " I Blocks: %5d\n", 0); if (bframeStats.BBBlocks > 0) { fprintf(fpointer, " B Blocks: %5d (%6d bits) (%5d bpb)\n", bframeStats.BBBlocks, bframeStats.BBBits, bframeStats.BBBits/bframeStats.BBBlocks); fprintf(fpointer, " B types: %5d (%4d bpb) " "forw %5d (%4d bpb) back %5d (%4d bpb) bi\n", bframeStats.BFOBlocks, (bframeStats.BFOBlocks==0) ? 0 : bframeStats.BFOBits/bframeStats.BFOBlocks, bframeStats.BBABlocks, (bframeStats.BBABlocks==0) ? 0 : bframeStats.BBABits/bframeStats.BBABlocks, bframeStats.BINBlocks, (bframeStats.BINBlocks==0) ? 0 : bframeStats.BINBits/bframeStats.BINBlocks); } else fprintf(fpointer, " B Blocks: %5d\n", 0); fprintf(fpointer, " Skipped: %5d\n", bframeStats.BSkipped); fprintf(fpointer, " Frames: %5d (%6d bits) " "(%5d bpf) (%2.1f%% of total)\n", bframeStats.Frames, bframeStats.FrameBits, bframeStats.FrameBits/bframeStats.Frames, 100.0*(float)bframeStats.FrameBits/(float)totalBits); fprintf(fpointer, " Compression: %3d:1 (%9.4f bpp)\n", bframeStats.Frames*inputFrameBits/bframeStats.FrameBits, 24.0*(float)bframeStats.FrameBits/ (float)(bframeStats.Frames*inputFrameBits)); if (printSNR) fprintf(fpointer, " Avg Y SNR/PSNR: %.1f %.1f\n", totalSNR/(float)bframeStats.Frames, totalPSNR/(float)bframeStats.Frames); 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*bframeStats.Frames)/ (float)totalTime), (long)((float)TIME_RATE*(float)bframeStats.Frames * (float)inputFrameBits/(24.0*(float)totalTime)), (long)((float)TIME_RATE*(float)bframeStats.Frames * (float)inputFrameBits/(256.0*24.0 * (float)totalTime))); } } } /*===========================================================================* * * compute the luminance block resulting from motion compensation * * RETURNS: motionBlock modified * * SIDE EFFECTS: none * * PRECONDITION: the motion vectors must be valid! * *===========================================================================*/ void ComputeBMotionLumBlock(MpegFrame * const prev, MpegFrame * const next, int const by, int const bx, int const mode, motion const motion, LumBlock * const motionBlockP) { switch(mode) { case MOTION_FORWARD: ComputeMotionLumBlock(prev, by, bx, motion.fwd, motionBlockP); break; case MOTION_BACKWARD: ComputeMotionLumBlock(next, by, bx, motion.bwd, motionBlockP); break; case MOTION_INTERPOLATE: { LumBlock prevBlock, nextBlock; unsigned int y; ComputeMotionLumBlock(prev, by, bx, motion.fwd, &prevBlock); ComputeMotionLumBlock(next, by, bx, motion.bwd, &nextBlock); for (y = 0; y < 16; ++y) { unsigned int x; for (x = 0; x < 16; ++x) motionBlockP->l[y][x] = (prevBlock.l[y][x] + nextBlock.l[y][x] + 1) / 2; } } break; default: pm_error("Bad mode! Programmer error!"); } /* switch */ } /*===========================================================================* * * estimate the seconds to compute a B-frame * * RETURNS: the time, in seconds * * SIDE EFFECTS: none * *===========================================================================*/ float EstimateSecondsPerBFrame() { if (bframeStats.Frames == 0) return 20.0; else return (float)totalTime/((float)TIME_RATE*(float)bframeStats.Frames); }