diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/ppm/ppmtompeg/bframe.c | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/ppm/ppmtompeg/bframe.c')
-rw-r--r-- | converter/ppm/ppmtompeg/bframe.c | 1347 |
1 files changed, 1347 insertions, 0 deletions
diff --git a/converter/ppm/ppmtompeg/bframe.c b/converter/ppm/ppmtompeg/bframe.c new file mode 100644 index 00000000..5dfb76d3 --- /dev/null +++ b/converter/ppm/ppmtompeg/bframe.c @@ -0,0 +1,1347 @@ +/*===========================================================================* + * 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 <sys/param.h> +#include <assert.h> + +#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; +extern dct_data_type **dct_data; +#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, + boolean 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); +} + + |