/* ************************************************************************* ** INTEL Corporation Proprietary Information ** ** This listing is supplied under the terms of a license ** agreement with INTEL Corporation and may not be copied ** nor disclosed except in accordance with the terms of ** that agreement. ** ** Copyright (c) 1995 Intel Corporation. ** All Rights Reserved. ** ** ************************************************************************* */ /***************************************************************************** * exbrc.cpp * * Description: * Bit rate control routines for H.261 and H.263. The bit rate is controlled * by changing QUANT value at the GOB level (H.261) or picture and GOB level * (H.26X). InitBRC() must be called at the time encoder is instanced; it * initializes some data values in BRCState structure. CalcPQUANT() computes the new * quant. value at the picture level; it must always be called. * CalcMBQUANT computes the new quant. value at the MB level; it need not be * called if quant. adjustment is done at the picture level. * * * Routines: * InitBRC * CalcPQUANT * CalcMBQUANT * Prototypes in: * e3enc.h * Note * Encoder must update BRCState->uLastINTRAFrmSz, BRCState->uLastINTERFrmSz, and * BRCState->uTargetFrmSize. */ /* * $Header: S:\h26x\src\enc\exbrc.cpv 1.15 31 Oct 1996 14:59:26 MBODART $ * $Log: S:\h26x\src\enc\exbrc.cpv $ // // Rev 1.15 31 Oct 1996 14:59:26 MBODART // Prevent recent changes from inadvertantly affecting H.261. // // Rev 1.14 31 Oct 1996 10:05:38 KLILLEVO // changed from DBOUT to DbgLog // // // Rev 1.13 29 Aug 1996 09:31:54 CZHU // Map intra-coded GOB to simpliar quality of inter-coded neighbours // // Rev 1.12 14 Aug 1996 16:46:22 CZHU // Adjust QP for intra frames other than the first Key frames. // // Rev 1.11 12 Mar 1996 13:26:54 KLILLEVO // new rate control with adaptive bit usage profile // // Rev 1.10 05 Feb 1996 17:15:12 TRGARDOS // Added code to do custom quantizer selection for // still frames // // Rev 1.9 01 Dec 1995 15:27:06 DBRUCKS // I removed the QP_mean affects to the global_adj value. // This resulted in removing any affect of the target frame rate on // the global adj value. // // Rev 1.8 28 Nov 1995 15:01:04 TRGARDOS // Initialized target frame rate in BRCinit. // // Rev 1.7 27 Nov 1995 19:26:00 TRGARDOS // Cleaned up bit rate control functions to be generic h26x bit rate // controller. Based off of macro blocks instead of GOBS now. // // Rev 1.6 26 Oct 1995 19:50:54 TRGARDOS // Fixed a small mistake in the global adjust calculation // and changed frame rate to a parameter. // // Rev 1.5 25 Oct 1995 23:22:36 SINGX // Changed BRC back to we just get frame rate from client // and compute global adjust ourselves. // // Rev 1.4 25 Oct 1995 20:14:40 TRGARDOS // Added code to use global adjustment passed from client. // // Rev 1.3 12 Oct 1995 12:04:42 TRGARDOS // Added QP_mean initialization in initBRC and added clipping // to all calculations of the new QP. // // Rev 1.2 11 Oct 1995 19:35:00 TRGARDOS // Modified bit rate controller. // // Rev 1.1 09 Oct 1995 11:48:10 TRGARDOS // Added float typecasting. // // Rev 1.0 06 Oct 1995 16:41:22 AGUPTA2 // Initial revision. */ // PhilF-: In the LAN case and QCIF mode, it looks like even with the smallest quantizer // we may be way below the max allowed at 30fps. Therefore, with little motion, // the bitrate looks constant at a low bitrate value. When high motion comes in, // even with the same small quantizer we will remain below the max. So we will // use that small quantizer, and the size of those compressed frames will get bigger // because of the higher motion -> this explains why we don't have a straight // line in the LAN case when looking at StatView... #include "precomp.h" U8 clampQP(int iUnclampedQP) { return ((iUnclampedQP < 2) ? 2 : (iUnclampedQP > 31) ? 31 : iUnclampedQP); } /**************************************************************************** * InitBRC * Parameter: * BRCState: T_H263EncoderCatalog ptr * Initializes some some variables in the encoder catalog. * Note * Must be called when the encoder is instanced. */ void InitBRC(BRCStateStruct *BRCState, U8 DefIntraQP, U8 DefInterQP, int numMBs) { FX_ENTRY("InitBRC"); BRCState->NumMBs = numMBs; BRCState->u8INTRA_QP = DefIntraQP; BRCState->u8INTER_QP = DefInterQP; BRCState->uLastINTRAFrmSz = 0; BRCState->uLastINTERFrmSz = 0; BRCState->QP_mean = DefIntraQP; BRCState->TargetFrameRate = (float) 0.0; BRCState->u8StillQnt = 0; DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: Bitrate controller initial state:\r\n numMBs = %ld macroblocks\r\n u8INTRA_QP = %ld\r\n u8INTER_QP = %ld\r\n", _fx_, BRCState->NumMBs, BRCState->u8INTRA_QP, BRCState->u8INTER_QP)); DEBUGMSG(ZONE_BITRATE_CONTROL, (" uLastINTRAFrmSz = %ld bytes\r\n uLastINTERFrmSz = %ld bytes\r\n QP_mean = %ld\r\n TargetFrameRate = %ld.%ld fps\r\n", BRCState->uLastINTRAFrmSz, BRCState->uLastINTERFrmSz, BRCState->QP_mean, (DWORD)BRCState->TargetFrameRate, (DWORD)((BRCState->TargetFrameRate - (float)(DWORD)BRCState->TargetFrameRate) * 10.0f))); } /**************************************************************************** * @doc INTERNAL H263FUNC * * @func U8 | CalcPQUANT | This function computes the PQUANT value to * use for the current frame. This is done by using the target frame size * and the results achieved with the previous frame. * * @parm BRCStateStruct * | BRCState | Specifies a pointer to the current * state of the bitrate controller. * * @parm EnumPicCodType | PicCodType | Specifies the type of the current * frame. If set to INTRAPIC, then the current frame is an I-frame. It * set to INTERPIC, then it is a P-frame or a PB-frame. * * @rdesc The PQUANT value. * * @comm H.261 does not have PQUANT. So, H261 encoder can call this routine * once and use the value returned as GQUANT for all GOBs. Or, it can * call CalcMBQUANT for all GOBs. * * This routine MUST be called for every frame for which QUANT adjustment * is required. CalcMBQUANT() might not be called. * * @xref ***************************************************************************/ U8 CalcPQUANT(BRCStateStruct *BRCState, EnumPicCodType PicCodType) { FX_ENTRY("CalcPQUANT"); if (PicCodType == INTERPIC) { if (BRCState->uLastINTERFrmSz != 0) { // Calculate the global adjustment parameter // Use the average QP for the last P-frame as the starting point // The quantizer increases faster than it decreases if (BRCState->uLastINTERFrmSz > BRCState->uTargetFrmSize) { BRCState->Global_Adj = ((float)((int)BRCState->uLastINTERFrmSz - (int)BRCState->uTargetFrmSize)) / (float)BRCState->uTargetFrmSize; DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: New u8INTER_QP = %ld, Global_Adj = +%ld.%ld (based on uLastINTERFrmSz = %ld bits, uTargetFrmSize = %ld bits, QP_mean = %ld)\r\n", _fx_, clampQP((int)(BRCState->QP_mean * (1 + BRCState->Global_Adj) + (float)0.5)), (DWORD)BRCState->Global_Adj, (DWORD)((BRCState->Global_Adj - (float)(DWORD)BRCState->Global_Adj) * 100.0f), (DWORD)BRCState->uLastINTERFrmSz << 3, (DWORD)BRCState->uTargetFrmSize << 3, (DWORD)BRCState->QP_mean)); } else { BRCState->Global_Adj = ((float)((int)BRCState->uLastINTERFrmSz - (int)BRCState->uTargetFrmSize)) / ((float) 2.0 * BRCState->uTargetFrmSize); DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: New u8INTER_QP = %ld, Global_Adj = -%ld.%ld (based on uLastINTERFrmSz = %ld bits, uTargetFrmSize = %ld bits, QP_mean = %ld)\r\n", _fx_,clampQP((int)(BRCState->QP_mean * (1 + BRCState->Global_Adj) + (float)0.5)), (DWORD)(BRCState->Global_Adj * -1.0f), (DWORD)((BRCState->Global_Adj - (float)(DWORD)(BRCState->Global_Adj * -1.0f)) * -100.0f), (DWORD)BRCState->uLastINTERFrmSz << 3, (DWORD)BRCState->uTargetFrmSize << 3, (DWORD)BRCState->QP_mean)); } BRCState->u8INTER_QP = clampQP((int)(BRCState->QP_mean * (1 + BRCState->Global_Adj) + (float)0.5)); } else { // This the first P-frame - use default value BRCState->u8INTER_QP = clampQP((unsigned char) BRCState->QP_mean); BRCState->Global_Adj = (float)0.0; DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: First u8INTER_QP = %ld\r\n", _fx_, BRCState->u8INTER_QP)); } return BRCState->u8INTER_QP; } else if (PicCodType == INTRAPIC) { if (BRCState->uLastINTRAFrmSz != 0) { // Calculate the global adjustment parameter // Use the average QP for the last I-frame as the starting point // Assume lighting & other conditions haven't changed too much since last I-frame // The quantizer increases faster than it decreases if (BRCState->uLastINTRAFrmSz > BRCState->uTargetFrmSize) { BRCState->Global_Adj = ((float) ((int)BRCState->uLastINTRAFrmSz - (int)BRCState->uTargetFrmSize) ) / ((float)BRCState->uTargetFrmSize); DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: New u8INTRA_QP = %ld, Global_Adj = +%ld.%ld (based on uLastINTRAFrmSz = %ld bits, uTargetFrmSize = %ld bits)\r\n", _fx_, clampQP((int)(BRCState->u8INTRA_QP * (1 + BRCState->Global_Adj) + (float)0.5)), (DWORD)BRCState->Global_Adj, (DWORD)((BRCState->Global_Adj - (float)(DWORD)BRCState->Global_Adj) * 100.0f), (DWORD)BRCState->uLastINTRAFrmSz << 3, (DWORD)BRCState->uTargetFrmSize << 3)); } else { // This the first I-frame - use default value BRCState->Global_Adj = ((float) ((int)BRCState->uLastINTRAFrmSz - (int)BRCState->uTargetFrmSize) ) / ((float) 2.0 * BRCState->uTargetFrmSize); DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: New u8INTRA_QP = %ld, Global_Adj = -%ld.%ld (based on uLastINTRAFrmSz = %ld bits, uTargetFrmSize = %ld bits)\r\n", _fx_, clampQP((int)(BRCState->u8INTRA_QP * (1 + BRCState->Global_Adj) + (float)0.5)), (DWORD)(BRCState->Global_Adj * -1.0f), (DWORD)((BRCState->Global_Adj - (float)(DWORD)(BRCState->Global_Adj * -1.0f)) * -100.0f), (DWORD)BRCState->uLastINTRAFrmSz << 3, (DWORD)BRCState->uTargetFrmSize << 3)); } BRCState->u8INTRA_QP = clampQP((int)(BRCState->u8INTRA_QP * (1 + BRCState->Global_Adj) + (float)0.5)); } else { DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: First u8INTRA_QP = %ld\r\n", _fx_, clampQP(BRCState->u8INTRA_QP))); } return clampQP(BRCState->u8INTRA_QP); } else { ERRORMESSAGE(("%s: Unknown frame type\r\n", _fx_)); return clampQP(BRCState->u8INTRA_QP); // return any valid value } } /**************************************************************************** * @doc INTERNAL H263FUNC * * @func U8 | CalcMBQUANT | This function computes the GQUANT value to * use for the current GOB. This is done by using the target frame size and * the running average of the GQUANTs computed for the previous GOBs in * the current frame. * * @parm BRCStateStruct * | BRCState | Specifies a pointer to the current * state of the bitrate controller. * * @parm U32 | uCumPrevFrmSize | Specifies the cumulated size of the previous * GOBs in the previous frame. * * @parm U32 | uPrevFrmSize | Specifies the total size of the previous * frame. * * @parm U32 | uCumFrmSize | Specifies the cumulated size of the previous * GOBs. * * @parm EnumPicCodType | PicCodType | Specifies the type of the current * frame. If set to INTRAPIC, then the current frame is an I-frame. It * set to INTERPIC, then it is a P-frame or a PB-frame. * * @rdesc The GQUANT value. * * @xref ***************************************************************************/ U8 CalcMBQUANT(BRCStateStruct *BRCState, U32 uCumPrevFrmSize, U32 uPrevFrmSize, U32 uCumFrmSize, EnumPicCodType PicCodType) { FX_ENTRY("CalcMBQUANT"); float Local_Adj; int TargetCumSize; if (PicCodType == INTERPIC) { // Calculate the local adjustment parameter by looking at how well we've // been doing so far with the previous GOBs TargetCumSize = (int)uCumPrevFrmSize * BRCState->uTargetFrmSize / uPrevFrmSize; // If this is the first GOB there's no local adjustment to compute Local_Adj = TargetCumSize ? (float)((int)uCumFrmSize - TargetCumSize) / (float)TargetCumSize : 0.0f; BRCState->u8INTER_QP = clampQP((int)(BRCState->QP_mean * (1 + BRCState->Global_Adj + Local_Adj) + (float)0.5)); #ifdef _DEBUG if (Local_Adj >= 0L) { DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, (" %s: New u8INTER_QP = %ld, Local_Adj = +%ld.%ld (based on uLastINTERFrmSz = %ld bits, uTargetFrmSize = %ld bits, uCumPrevFrmSize = %ld, uPrevFrmSize = %ld, QP_mean = %ld)\r\n", _fx_, BRCState->u8INTER_QP, (DWORD)Local_Adj, (DWORD)((Local_Adj - (float)(DWORD)Local_Adj) * 100.0f), (DWORD)BRCState->uLastINTERFrmSz << 3, (DWORD)BRCState->uTargetFrmSize << 3, uCumPrevFrmSize, uPrevFrmSize, (DWORD)BRCState->QP_mean)); } else { DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, (" %s: New u8INTER_QP = %ld, Local_Adj = -%ld.%ld (based on uLastINTERFrmSz = %ld bits, uTargetFrmSize = %ld bits, uCumPrevFrmSize = %ld, uPrevFrmSize = %ld, QP_mean = %ld)\r\n", _fx_, BRCState->u8INTER_QP, (DWORD)(Local_Adj * -1.0f), (DWORD)((Local_Adj - (float)(DWORD)(Local_Adj * -1.0f)) * -100.0f), (DWORD)BRCState->uLastINTERFrmSz << 3, (DWORD)BRCState->uTargetFrmSize << 3, uCumPrevFrmSize, uPrevFrmSize, (DWORD)BRCState->QP_mean)); } #endif return BRCState->u8INTER_QP; } else if (PicCodType == INTRAPIC) { // The previous I-frame is so old that there isn't much point in doing local // adjustments - so only consider the global changes DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, (" %s: New u8INTRA_QP = %ld\r\n", _fx_, BRCState->u8INTRA_QP)); return BRCState->u8INTRA_QP; } else { ERRORMESSAGE(("%s: Unknown frame type\r\n", _fx_)); return BRCState->u8INTRA_QP; // return some valid value } }