Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3077 lines
103 KiB

/* *************************************************************************
** 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-1996 Intel Corporation.
** All Rights Reserved.
**
** *************************************************************************
*/
/*****************************************************************************
*
* e1enc.cpp
*
* DESCRIPTION:
* Specific encoder compression functions.
*
* Routines: Prototypes in:
* H263InitEncoderInstance
* H263Compress
* H263TermEncoderInstance
*/
// $Header: S:\h26x\src\enc\e1enc.cpv 1.78 15 Apr 1997 10:24:48 AGUPTA2 $
// $Log: S:\h26x\src\enc\e1enc.cpv $
//
// Rev 1.78 15 Apr 1997 10:24:48 AGUPTA2
// Added checks to ensure 1) bit-stream is less than (8,32)K and 2)
// extended bitstream is less than allocated buffer size.
//
// Rev 1.77 24 Jan 1997 17:12:06 RHAZRA
// We were computing the size of the bitstream to be one more than
// the real size. Now fixed.
//
// Rev 1.76 17 Dec 1996 09:07:16 SCDAY
// changed an ASSERT to handle Memory Layout changes
//
// Rev 1.75 16 Dec 1996 17:36:04 MBODART
// Applied Raj's changes for tweaking ME parameters under RTP.
// Also restructured some code for cleanliness.
//
// Rev 1.74 13 Dec 1996 17:19:02 MBODART
// Bumped the ME SLF parameters for MMX.
//
// Rev 1.73 13 Dec 1996 15:17:02 MBODART
// Adjusted the motion estimation IA spatial loop filter parameters.
// Still need to tune the MMX parameters.
//
// Rev 1.72 05 Dec 1996 10:56:14 MBODART
// Added h261test.ini options for playing with motion estimation parameters.
//
// Rev 1.71 04 Dec 1996 13:23:34 MBODART
// Tweaked some DbgLog messages to aid bit rate tuning.
// Removed some unused code.
//
// Rev 1.70 21 Nov 1996 10:50:32 RHAZRA
// Changed recompression strategy to be more pessimistic with key
// frames since a buffer overflow on a keyframe can cause a host
// of secondary effects.
//
// Rev 1.69 18 Nov 1996 17:11:38 MBODART
// Replaced all debug message invocations with Active Movie's DbgLog.
//
// Rev 1.68 18 Nov 1996 09:02:34 RHAZRA
// Now no DWORD 0 at the end of the bitstream for both the MMX and IA
// codecs.
//
// Rev 1.67 15 Nov 1996 09:47:22 RHAZRA
// #ifdef ed out the addition of a DWORD of zeros at the end of the bitstream.
//
// Rev 1.66 13 Nov 1996 11:37:20 RHAZRA
// Added MMX autosensing
//
// Rev 1.65 21 Oct 1996 10:45:50 RHAZRA
// Changed interface to EDTQ function to keep in sync with exmme.asm which
// now has EMV
//
// Rev 1.64 21 Oct 1996 09:00:18 RHAZRA
// MMX integration
//
// Rev 1.62 16 Sep 1996 13:17:44 RHAZRA
// Added support for SLF to be adaptively turned on and off via
// coding thresholds and differentials.
//
// Rev 1.61 06 Sep 1996 15:04:52 MBODART
// Added performance counters for NT's perfmon.
// New files: cxprf.cpp, cxprf.h, cxprfmac.h.
// New directory: src\perf
// Updated files: e1enc.{h,cpp}, d1dec.{h,cpp}, cdrvdefs.h, h261* makefiles.
//
// Rev 1.60 26 Aug 1996 10:09:02 RHAZRA
// Added checking to ensure that RTP BS Info stream is rewound on
// GOB recompression only if RTP is signalled. This fixes the reported
// failure of build 29 on q1502stl.avi @ 100Kbps.
//
// Rev 1.59 21 Aug 1996 19:01:18 RHAZRA
// Added RTP extended bitstream generation
//
// Rev 1.58 21 Jun 1996 10:06:06 AKASAI
// Changes to e1enc.cpp, e1mbenc.cpp, ex5me.asm to support "improved
// bit rate control", changing MacroBlock Quantization within a
// row of MB's in addition to changing the Quantization change
// between rows of macro blocks.
//
// ex5me.asm had a problem with SLF SWD. Brian updated asm code.
//
//
// Rev 1.57 05 Jun 1996 13:56:52 AKASAI
// Changes to e1enc.cpp: Added new parameter to MOTIONESTIMATION which
// allows for 15 pel radius search otherwise (? maybe 7 pels).
//
// Changes to e1enc.h: New parameter to MOTIONESTIMATION and change to
// offsets in MBAcationStream to match changes in e3mbad.inc, ex5me.asm
// and ex5fdct.asm.
//
// Rev 1.56 29 May 1996 13:53:00 AKASAI
// Tuned the Motion Estimation parameters. Video quality
// seems to remain about the same, bit rate increased a little (200 bits
// per frame), CPU usage decreased a little.
//
// Rev 1.55 14 May 1996 12:33:10 AKASAI
// Got an undefined on wsprintf so moved it to a #ifdef DEBUG_RECOMPRESS
// area.
//
// Rev 1.54 14 May 1996 10:33:46 AKASAI
// Two files changed to hopefully eliminate Quantization clamping
// artifacts and to reduce the max buffer overflow case: e1enc.cpp
// and e1mbenc.cpp.
//
// In e1mbenc.cpp when the MQuant level is < 6 I test to see if
// the 0th coefficient is larger than the values representable
// at that Quant level if it is I increase the Quant level until
// the clamping artifact will not occur. Note: I am test only
// the Oth coefficient, there is the possibility that some other
// coefficient is larger but the performance trade off seems to
// indicate this is good for now and if we still see clamping
// artifacts we can add more testing later.
//
// In e1enc.cpp I modified when the Overflow types of warnings are
// turn on as well as changing the rate the Quantization level
// changes at.
//
// Rev 1.53 24 Apr 1996 12:13:50 AKASAI
// Added re-compression strategy to encoder. Had to change e1enc.cpp,
// e1enc.h and e1mbenc.cpp.
// Basic strategy is if spending too many bits in a GOB quantize the
// next GOB at a higher rate. If after compressing the frame too
// many bits have been used, re-compress the last GOB at a higher
// QUANT level if that still doesn't work send a "Skip" GOB.
// Needed to add extra parameter to GOB+Q_RLE_VLC_WriteBS because
// CalcMBQuant kept decreasing the QUANT when we were in trouble with
// possibly overflowing the buffer.
//
// Rev 1.52 22 Apr 1996 10:54:24 AKASAI
// Two files changed e1enc.cpp and e1mbenc.cpp to try and support
// allowing the Quantization values to go down to 2 instead of
// CLAMP to 6.
// This is part 1 of implementing the re-compression (what to do
// if exceed max compressed buffer size 8KBytes QCIF, 32KBytes FCIF).
// Also changed in e1enc was to limit request uFrameSize to 8KB or
// 32KB. Problem was if user specified too large of a datarate
// request frame size would be larger than the allowed buffer size.
// If you try to compress qnoise10.avi or fnoise5.avi you get an
// ASSERT error until rest of re-compression is implemented.
//
// Rev 1.51 19 Apr 1996 14:26:26 SCDAY
// Added adaptive bit usage profile (Karl's BRC changes)
//
// Rev 1.50 15 Apr 1996 14:10:30 AKASAI
// Updated range to allow for +/- 15 pel search. There was and assert
// if MV outside +/- 15 (in half pel numbers) now assert if [-32,31].
//
// Rev 1.49 11 Apr 1996 16:00:02 AKASAI
// Updated H261 encoder to new interface and macroblock action stream
// data structure in e3mbad.inc for FORWARDDCT. Files updated together
// e1enc.cpp, e1enc.h, ex5fdct.asm, e3mbad.inc.
//
// Added IFNDEF H261 in ex5fdct so that code used only in H263 is
// not assembled for H261.
//
// Rev 1.48 11 Apr 1996 13:02:04 SCDAY
// Fixed zero dirty buffer problem
//
// Rev 1.45 10 Apr 1996 13:06:40 SCDAY
// Changed clearing of bitstream buffer to zero only the "dirty"
// part of the buffer rather than the entire buffer
//
// Rev 1.44 05 Apr 1996 14:36:28 SCDAY
//
// Added ASM version of UV SLF
//
// Rev 1.43 04 Apr 1996 13:45:32 AKASAI
// Added 2 Bytes, 16-bits of zeros at end of bitstream to help 16-bit
// decoder find end of frame. Under testing we saw green blocks at
// end of frame.
//
// Rev 1.42 27 Mar 1996 15:09:52 SCDAY
// Moved declarations/definition of H26X_YUV12toEncYUV12 to excolcnv.cpp
// to incorporate latest H263 changes and SCD 'C' code optimization
//
// Rev 1.41 20 Mar 1996 14:21:04 Sylvia_C_Day
// Added lower level timing stats for SLF_UV
//
// Rev 1.40 26 Feb 1996 10:09:34 AKASAI
// Corrected PicFreeze bit last fix set it to always ON instead of the
// correct usage. ON for Keys OFF for deltas.
// Also fixed 2 other bits that were set incorrectly. HI_RES and SPARE.
// SPARE should always be 1, HI_RES should be OFF (which is 1 for this
// bit).
//
// Rev 1.39 14 Feb 1996 14:53:56 AKASAI
// Added work around for Blazer team to set PicFreeze to ON when
// encoding a KEY FRAME.
//
// Rev 1.38 06 Feb 1996 09:46:00 AKASAI
// Updated Copyright to include 1996.
//
// Rev 1.37 05 Feb 1996 15:24:04 AKASAI
// Changes to support new BRC interface. Tested with RING3 codec.
//
// Rev 1.36 09 Jan 1996 08:52:34 AKASAI
//
// Added U&V plane loop filter. To enable make sure SLF_WORK_AROUND
// is defined in makefile.
//
// Rev 1.35 08 Jan 1996 10:11:58 DBRUCKS
// Disable half pel interpolation of U & V motion vectors in fdct
// Change to use divide and not shift when calculating U & V motion vectors
// in order that we truncate towards zero as the spec requires.
//
// Rev 1.34 29 Dec 1995 18:12:54 DBRUCKS
//
// add clamp_n_to(qp,6,31) to avoid clipping artifacts.
// add code to assign Y2,3,4-PrevPtr based on Y1-PrevPtr for SLF blocks
//
// Rev 1.33 27 Dec 1995 16:51:54 DBRUCKS
// move the increment of InterCodeCnt to e1mbenc.cpp
// cleanup based on H263 v11
// remove unused definitions
//
// Rev 1.32 26 Dec 1995 17:44:52 DBRUCKS
// moved statistics to e1stat
//
// Rev 1.31 20 Dec 1995 16:46:02 DBRUCKS
// get Spox to compile with the timing code
//
// Rev 1.30 20 Dec 1995 15:35:08 DBRUCKS
// get to build without ENC_STATS define
//
// Rev 1.29 20 Dec 1995 14:56:50 DBRUCKS
// add timing stats
//
// Rev 1.28 18 Dec 1995 15:38:02 DBRUCKS
// improve stats
//
// Rev 1.27 13 Dec 1995 13:58:18 DBRUCKS
//
// moved trace and cnvt_fdct_output to exutil.cpp
// removed BitRev as it was not used
// changed to call terminate if init was called with Initialized == True
// implemented TR
//
// Rev 1.26 07 Dec 1995 12:53:38 DBRUCKS
//
// add an ifdef so the ring0 release build succeeds
// change the quality to quant from conversion to use 3 to 31
// fix mb first state initialization for CIF
//
// Rev 1.25 06 Dec 1995 09:43:38 DBRUCKS
//
// initialize blazer COMPINSTINFO variables
// integrate blazer bit rate control into Compress
// remove LINK_ME
//
// Rev 1.24 04 Dec 1995 10:26:28 DBRUCKS
// cleanup the ini file reading function
//
// Rev 1.23 01 Dec 1995 15:37:56 DBRUCKS
//
// Added the bit rate controller
// set the default options (if no INI file) to:
// RING0: MotionEstimation SpatialLoopFilter FixedQuantization of 8
// RING3: MotionEstimation SpatialLoopFilter VfW driven Bit Rate Control
//
// Rev 1.20 28 Nov 1995 13:21:30 DBRUCKS
// add BRC options
// change to read options from an ini file - h261.ini
//
// Rev 1.19 27 Nov 1995 17:53:42 DBRUCKS
// add spatial loop filtering
//
// Rev 1.18 27 Nov 1995 16:41:44 DBRUCKS
// replace B and Future planes with SLF
// remove the scratch space
//
// Rev 1.17 22 Nov 1995 18:21:42 DBRUCKS
// Add an #ifdef around the MOTIONESTIMATION call in order that the IASpox
// environment not be required to call MOTIONESTIMATION when advancing the
// tip. Unless LINK_ME is defined MOTIONESTIMATION will not be called.
//
// Rev 1.16 22 Nov 1995 17:37:36 DBRUCKS
// cleanup me changes
//
// Rev 1.15 22 Nov 1995 15:34:30 DBRUCKS
//
// Motion Estimation works - but needs to be cleaned up
//
// Rev 1.14 20 Nov 1995 12:13:14 DBRUCKS
// Cleanup the encoder terminate function
// Integrate in the picture checksum code (CHECKSUM_PICTURE)
//
// Rev 1.13 17 Nov 1995 14:25:24 BECHOLS
// Made modifications so that this file can be made for ring 0.
//
// Rev 1.12 15 Nov 1995 19:05:22 AKASAI
// Cleaned up some warning messages.
//
// Rev 1.11 15 Nov 1995 14:38:16 AKASAI
//
// Current and Previous frame pointers changed from Addresses to Offsets.
// Change of parameters to call to FOWARDDCT. Some Union thing.
// (Integration point)
//
// Rev 1.10 01 Nov 1995 09:01:12 DBRUCKS
//
// cleanup variable names
// add ZERO_INPUT test option
// make sure all frames end on a byte boundary.
//
// Rev 1.9 27 Oct 1995 17:19:52 DBRUCKS
// initializing PastRef ptrs
//
// Rev 1.8 27 Oct 1995 15:06:26 DBRUCKS
// update cnvt_fdct_output
//
// Rev 1.7 27 Oct 1995 14:31:10 DBRUCKS
// integrate 0-MV delta support based on 263 baseline
//
// Rev 1.6 28 Sep 1995 17:02:34 DBRUCKS
// fix colorIn to not swap left to right
//
// Rev 1.5 28 Sep 1995 15:58:20 DBRUCKS
// remove pragmas
//
// Rev 1.4 28 Sep 1995 14:21:30 DBRUCKS
// fix to match INTRA and INTER enum changes
//
// Rev 1.3 25 Sep 1995 10:22:48 DBRUCKS
// activate call to InitVLC
//
// Rev 1.2 20 Sep 1995 12:38:48 DBRUCKS
// cleanup
//
// Rev 1.0 18 Sep 1995 10:09:30 DBRUCKS
// Initial revision after the archive got corrupted.
//
// Rev 1.4 15 Sep 1995 12:27:32 DBRUCKS
// intra mb header
//
// Rev 1.3 14 Sep 1995 17:16:08 DBRUCKS
// turn on FDCT and some cleanup
//
// Rev 1.2 14 Sep 1995 14:18:52 DBRUCKS
// init mb action stream
//
// Rev 1.1 13 Sep 1995 13:41:50 DBRUCKS
// move the picture header writing into a separate routine.
// implement the gob header writing.
//
// Rev 1.0 12 Sep 1995 15:53:40 DBRUCKS
// initial
//
#define DUMPFILE 0
/* Pick a resiliency strategy.
*/
#define REQUESTED_KEY_FRAME 0
#define PERIODIC_KEY_FRAME 1
#define FAST_RECOVERY 2
#define SLOW_RECOVERY 3
#define MAX_STUFFING_BYTES 10
#define RESILIENCY_STRATEGY PERIODIC_KEY_FRAME
#define PERIODIC_KEY_FRAME_PERIODICITY 15 /* Select periodicity (max 32767) */
#define UNRESTRICTED_MOTION_FRAMES 16 /* Number of frames that don't have an Intra slice. 0 for FAST_RECOVERY.
* Modest amount for SLOW_RECOVERY. Unimportant for other strategies. */
#include "precomp.h"
#ifdef ENCODE_STATS
#define ENCODE_STATS_FILENAME "encstats.txt"
#endif
#define PITCHL 384L
#define DEFAULT_DCSTEP 8
#define DEFAULT_QUANTSTEP 36
#define DEFAULT_QUANTSTART 30
#define LEFT 0
#define INNERCOL 1
#define NEARRIGHT 2
#define RIGHT 3
#define TOP 0
#define INNERROW 4
#define NEARBOTTOM 8
#define BOTTOM 12
#define FCIF_NUM_OF_GOBS 12
#define QCIF_NUM_OF_GOBS 3
#if defined(_DEBUG) || defined(DEBUG_RECOMPRESS) || defined(DEBUG_ENC) || defined(DEBUG_BRC)
char string[128];
#endif
/* Look up table for quarter pel to half pel conversion of chroma MV's. */
const char QtrPelToHalfPel[64] =
{ -16, -15, -15, -15, -14, -13, -13, -13, -12, -11, -11, -11, -10, -9, -9, -9, -8,
-7, -7, -7, -6, -5, -5, -5, -4, -3, -3, -3, -2, -1, -1, -1, 0,
1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 5, 6, 7, 7, 7, 8,
9, 9, 9, 10, 11, 11, 11, 12, 13, 13, 13, 14, 15, 15, 15 };
/* The GOB number arrays contain the GOB numbers for QCIF and CIF. The lists are zero terminated.
*/
static U32 uCIFGOBNumbers[] = {1,2,3,4,5,6,7,8,9,10,11,12,0};
static U32 uQCIFGOBNumbers[] = {1,3,5,0};
/* The starting INTER Motion Estimation states are different for QCIF and CIF.
*/
#define MAX_ME_STATE_ROW_NUMBER 8
#define BAD_ME_ROW (MAX_ME_STATE_ROW_NUMBER + 1)
static U8 u8FirstInterMEStateRows[MAX_ME_STATE_ROW_NUMBER+1][11] =
{ /* 1 2 3 4 5 6 7 8 9 10 11 */
{UpperLeft, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperRight},
{LeftEdge, CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,RightEdge},
{LowerLeft, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerRight},
{UpperLeft, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge},
{UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperEdge, UpperRight},
{LeftEdge, CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock},
{CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,CentralBlock,RightEdge},
{LowerLeft, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge},
{LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerRight},
};
//RTP: resiliency tables.
static U8 u8FirstInterMENoVerMVStateRows[MAX_ME_STATE_ROW_NUMBER+1][11] =
{ /* 1 2 3 4 5 6 7 8 9 10 11 */
{NoVertLeftEdge, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertRightEdge},
{NoVertLeftEdge, NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertRightEdge},
{NoVertLeftEdge, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertRightEdge},
{NoVertLeftEdge, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock},
{NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertRightEdge},
{NoVertLeftEdge, NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock},
{NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertCentralBlock,NoVertRightEdge},
{NoVertLeftEdge, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock},
{NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertRightEdge},
};
static U8 u8FirstInterMENoPosVerMVStateRows[MAX_ME_STATE_ROW_NUMBER+1][11] =
{ /* 1 2 3 4 5 6 7 8 9 10 11 */
{NoVertLeftEdge, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertRightEdge},
{LowerLeft, LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerRight},
{LowerLeft, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerRight},
{NoVertLeftEdge, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock},
{NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertCentralBlock, NoVertRightEdge},
{LowerLeft, LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge},
{LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerEdge,LowerRight},
{LowerLeft, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge},
{LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerEdge, LowerRight},
};
static U8 u8MEPad[1];
static U8 u8QCIFFirstInterMEStateRowNumbers[12] =
{0,1,1, 1,1,1, 1,1,2, BAD_ME_ROW,BAD_ME_ROW,BAD_ME_ROW};
static U8 u8CIFFirstInterMEStateRowNumbers[40] =
{3,5,5, 4,6,6, 5,5,5, 6,6,6, 5,5,5, 6,6,6, 5,5,5, 6,6,6, 5,5,5, 6,6,6, 5,5,7, 6,6,8, BAD_ME_ROW,BAD_ME_ROW,BAD_ME_ROW,BAD_ME_ROW};
/* The starting offsets for each of the GOBs
* - odd GOBs: NumberOfGOBsAbove * PITCH * 3MacroBlocksToAGOB * NumberOfLinesToAMacroBlock
* - even GOBs: oddGobValue + 11MacroBlocksToAGOB * NumberOfColumnsToAMacroBlock
*/
static U32 uStartingYOffsets[16] =
{
0, /* not used */
0*PITCH*3*16, 0*PITCH*3*16+11*16, // 1 and 2
1*PITCH*3*16, 1*PITCH*3*16+11*16, // 3 and 4
2*PITCH*3*16, 2*PITCH*3*16+11*16, // 5 and 6
3*PITCH*3*16, 3*PITCH*3*16+11*16, // 7 and 8
4*PITCH*3*16, 4*PITCH*3*16+11*16, // 9 and 10
5*PITCH*3*16, 5*PITCH*3*16+11*16, // 11 and 12
0, 0, 0 /* not used */
};
static U32 uStartingUOffsets[16] =
{
0, /* not used */
0*PITCH*3*8, 0*PITCH*3*8+11*8, // 1 and 2
1*PITCH*3*8, 1*PITCH*3*8+11*8, // 3 and 4
2*PITCH*3*8, 2*PITCH*3*8+11*8, // 5 and 6
3*PITCH*3*8, 3*PITCH*3*8+11*8, // 7 and 8
4*PITCH*3*8, 4*PITCH*3*8+11*8, // 9 and 10
5*PITCH*3*8, 5*PITCH*3*8+11*8, // 11 and 12
0, 0, 0
};
/* Table to limit Quant changes between Rows of Marco Blocks */
U8 MaxChangeRowMBTbl[32] =
{ 0, /* Not Used */
1, /* Not Used when clamp to (2,31) */
1, /* 2 */
2, /* 3 */
2, /* 4 */
3, /* 5 */
3, /* 6 */
3, /* 7 */
3, /* 8 */
3, /* 9 */
3, /* 10 */
3, /* 11 */
3, /* 12 */
3, /* 13 */
3, /* 14 */
3, /* 15 */
3, /* 16 */
3, /* 17 */
3, /* 18 */
3, /* 19 */
3, /* 20 */
3, /* 21 */
3, /* 22 */
3, /* 23 */
4, /* 24 */
4, /* 25 */
4, /* 26 */
4, /* 27 */
4, /* 28 */
4, /* 29 */
4, /* 30 */
4 /* 31 */
};
U8 INTERCODECNT_ADJUST[11]=
// Packet loss (in %)
// 0-9 10-19 20-29 30-39 40-49 50-59 60-69 70-79 80-89 90-99 100
// Refresh limit
{ 132, 100, 80, 75, 60, 45, 20, 10, 5, 3, 1 };
U32 EMPTYTHRESHOLD_ADJUST[5]=
// Packet loss (in %)
// 0-24 25-49 50-74 75-99 100
// Multiplier
{ 1, 2, 3, 4, 10};
/* Static Function declarations
*/
static void WriteBeginPictureHeaderToStream(T_H263EncoderCatalog *, U8 ** ,U8 *);
#ifdef CHECKSUM_PICTURE
static void WritePictureChecksum(YVUCheckSum *, U8 ** ,U8 *, U8);
#endif
static void WriteEndPictureHeaderToStream(T_H263EncoderCatalog *, U8 ** ,U8 *);
static void WriteGOBHeaderToStream(U32,unsigned int, U8 ** ,U8 *);
static void CalcGOBChromaVecs(T_H263EncoderCatalog *, UN, T_CONFIGURATION *);
static void GetEncoderOptions(T_H263EncoderCatalog *);
static void StartupBRC(T_H263EncoderCatalog *, U32, U32, float);
static void CalculateQP_mean(T_H263EncoderCatalog * EC);
/* Global Data definition
*/
#pragma data_seg ("H263EncoderTbl") /* Put in the data segment named "H263EncoderTbl" */
#pragma data_seg ()
/*****************************************************************************
* EXTERNAL FUNCTIONS
****************************************************************************/
/*****************************************************************************
*
* H263InitEncoderGlobal
*
* This function initializes the global tables used by the H261 encoder. Note
* that in 16-bit Windows, these tables are copied to the per-instance data
* segment, so that they can be used without segment override prefixes.
* In 32-bit Windows, the tables are left in their staticly allocated locations.
*
* Returns an ICERR value
*/
LRESULT H263InitEncoderGlobal(void)
{
/*
* Initialize fixed length tables for INTRADC
*/
InitVLC();
return ICERR_OK;
} /* end H263InitEncoderGlobal() */
/*****************************************************************************
*
* H263InitEncoderInstance
*
* This function allocates and initializes the per-instance tables used by
* the H261 encoder. Note that in 16-bit Windows, the non-instance-specific
* global tables are copied to the per-instance data segment, so that they
* can be used without segment override prefixes.
*
* Returns an ICERR value;
*/
LRESULT H263InitEncoderInstance(LPCODINST lpCompInst)
{
LRESULT lResult = ICERR_ERROR;
U32 uGOBNumber;
U32 uSize;
UN unIndex;
UN unStartingMB;
T_H263EncoderInstanceMemory * P32Inst;
T_H263EncoderCatalog * EC;
U32 * puGOBNumbers;
int iMBNumber;
UN bEncoderInstLocked = 0;
int iNumMBs;
// RTP: declarations
T_CONFIGURATION *pConfiguration;
UN uIntraQP;
UN uInterQP;
/* If we were already initialized then we need to terminate the instance.
* This happens when two BEGIN's are called without an END.
*
* Note: We can't just clear the memory because that will throw out the
* decoder memory. because it will clear the decoder instance which
* contains the decoder catalog pointer.
*/
if(lpCompInst->Initialized)
{
lResult = H263TermEncoderInstance(lpCompInst);
if (lResult != ICERR_OK)
{
DBOUT("Warning an error occurred terminating the encoder before reinitializing");
}
}
/* Calculate size of encoder instance memory needed. */
//uSize = sizeof(T_H263EncoderInstanceMemory) + 32;
uSize = sizeof(T_H263EncoderInstanceMemory) + sizeof(T_MBlockActionStream);
/*
* Allocate the memory.
*/
lpCompInst->hEncoderInst = GlobalAlloc(GHND, uSize);
lpCompInst->EncoderInst = (LPVOID) GlobalLock(lpCompInst->hEncoderInst);
if (lpCompInst->hEncoderInst == NULL || lpCompInst->EncoderInst == NULL)
{
lResult = ICERR_MEMORY;
goto done;
}
bEncoderInstLocked = 1;
/* Calculate the 32 bit instance pointer starting at the next
* 32 byte boundary.
*/
P32Inst = (T_H263EncoderInstanceMemory *)
((((U32) lpCompInst->EncoderInst) +
(sizeof(T_MBlockActionStream) - 1)) &
~(sizeof(T_MBlockActionStream) - 1));
/* The encoder catalog is at the start of the per-instance data.
*/
EC = &(P32Inst->EC);
// RTP: initialization
/* Initialize the Configuration information
*/
pConfiguration = &(lpCompInst->Configuration);
#if 0
if (LoadConfiguration(pConfiguration) == FALSE)
{
GetConfigurationDefaults(pConfiguration);
}
#endif
pConfiguration->bInitialized = TRUE;
pConfiguration->bCompressBegin = TRUE;
#ifdef _DEBUG
DBOUT("Encoder Configuration Options:");
wsprintf(string,"bRTPHeader=%d", (int)pConfiguration->bRTPHeader);
DBOUT(string);
wsprintf(string,"unPacketSize=%d", (int)pConfiguration->unPacketSize);
DBOUT(string);
wsprintf(string,"bEncoderResiliency=%d", (int)pConfiguration->bEncoderResiliency);
DBOUT(string);
wsprintf(string,"bDisallowPosVerMVs=%d", (int)pConfiguration->bDisallowPosVerMVs);
DBOUT(string);
wsprintf(string,"bDisallowAllVerMVs=%d", (int)pConfiguration->bDisallowAllVerMVs);
DBOUT(string);
wsprintf(string,"unPercentForcedUpdate=%d", (int)pConfiguration->unPercentForcedUpdate);
DBOUT(string);
wsprintf(string,"unDefaultIntraQuant=%d", (int)pConfiguration->unDefaultIntraQuant);
DBOUT(string);
wsprintf(string,"unDefaultInterQuant=%d", (int)pConfiguration->unDefaultInterQuant);
DBOUT(string);
#endif
/* Initialize encoder catalog.
*/
EC->FrameHeight = lpCompInst->yres;
EC->FrameWidth = lpCompInst->xres;
EC->FrameSz = lpCompInst->FrameSz;
EC->NumMBRows = EC->FrameHeight >> 4;
EC->NumMBPerRow = EC->FrameWidth >> 4;
EC->NumMBs = EC->NumMBRows * EC->NumMBPerRow;
/* Get the Options
*/
GetEncoderOptions(EC);
// RTP: resiliency initialization
if(pConfiguration->bEncoderResiliency &&
pConfiguration->unPercentForcedUpdate &&
pConfiguration->unPacketLoss)
{
EC->uNumberForcedIntraMBs = ((EC->NumMBs * pConfiguration->unPercentForcedUpdate) + 50) / 100;
EC->uNextIntraMB = 0;
}
/* Store pointers to current frame in the catalog.
*/
EC->pU8_CurrFrm = P32Inst->u8CurrentPlane;
EC->pU8_CurrFrm_YPlane = EC->pU8_CurrFrm + 16;
EC->pU8_CurrFrm_UPlane = EC->pU8_CurrFrm_YPlane + YU_OFFSET;
EC->pU8_CurrFrm_VPlane = EC->pU8_CurrFrm_UPlane + UV_OFFSET;
/* Store pointers to the previous frame in the catalog.
*/
EC->pU8_PrevFrm = P32Inst->u8PreviousPlane;
EC->pU8_PrevFrm_YPlane = EC->pU8_PrevFrm + 16*PITCH + 16;
EC->pU8_PrevFrm_UPlane = EC->pU8_PrevFrm_YPlane + YU_OFFSET;
EC->pU8_PrevFrm_VPlane = EC->pU8_PrevFrm_UPlane + UV_OFFSET;
/* Store pointers to the Spatial Loop Filter frame in the catalog.
*/
EC->pU8_SLFFrm = P32Inst->u8SLFPlane;
EC->pU8_SLFFrm_YPlane = EC->pU8_SLFFrm + 16;
EC->pU8_SLFFrm_UPlane = EC->pU8_SLFFrm_YPlane + YU_OFFSET;
EC->pU8_SLFFrm_VPlane = EC->pU8_SLFFrm_UPlane + UV_OFFSET;
/* Store pointers to the Signature frame in the catalog
*/
EC->pU8_Signature = P32Inst->u8Signature;
EC->pU8_Signature_YPlane = EC->pU8_Signature + 16*PITCH + 16;
// Store pointer to the RunValSign triplets for Luma and Chroma
EC->pI8_MBRVS_Luma = P32Inst->i8MBRVS_Luma;
EC->pI8_MBRVS_Chroma = P32Inst->i8MBRVS_Chroma;
/* Store pointer to the macroblock action stream in the catalog.
*/
EC->pU8_MBlockActionStream = P32Inst->MBActionStream;
/* Store pointer to the GOB DCT coefficient buffer in the catalog.
*/
EC->pU8_DCTCoefBuf = P32Inst->piGOB_DCTCoefs;
/* Store pointer to the bit stream buffer in the catalog.
*/
EC->pU8_BitStream = P32Inst->u8BitStream;
/* Store pointer to private copy of decoder instance info.
*/
EC->pDecInstanceInfo = &(P32Inst->DecInstanceInfo);
/* Fill the picture header structure.
*/
EC->PictureHeader.Split = OFF;
EC->PictureHeader.DocCamera = OFF;
EC->PictureHeader.PicFreeze = OFF;
EC->PictureHeader.StillImage = (EnumOnOff) 1; // For this bit ON=0, OFF=1
EC->PictureHeader.TR = 31;
if (EC->FrameWidth == 352)
{
ASSERT(EC->FrameHeight == 288);
EC->PictureHeader.SourceFormat = SF_CIF;
EC->u8DefINTRA_QP = 20;
EC->u8DefINTER_QP = 13;
}
else
{
ASSERT(EC->FrameWidth == 176 && EC->FrameHeight == 144);
EC->PictureHeader.SourceFormat = SF_QCIF;
EC->u8DefINTRA_QP = 15;
EC->u8DefINTER_QP = 10;
}
EC->PictureHeader.Spare = 1; // Spare bits set to 1
EC->PictureHeader.PEI = 0;
EC->PictureHeader.PQUANT = 8; // for now
EC->PictureHeader.PicCodType = INTRAPIC; // for now
#ifndef RING0
/* Save the timing info pointer - only if do this if not Ring0 because
* structure in P32Inst is only declared if not Ring0.
*/
EC->pEncTimingInfo = P32Inst->EncTimingInfo;
#endif
/* Force the first frame to a key frame
*/
EC->bMakeNextFrameKey = TRUE;
/* Initialize table with Bit Usage Profile */
// for (iNumMBs = 0; iNumMBs <= 33 ; iNumMBs++)
for (iNumMBs = 0; iNumMBs <= (int)EC->NumMBs ; iNumMBs++)
{
EC->uBitUsageProfile[iNumMBs] = iNumMBs; // assume linear distribution at first
}
/* Check assumptions about structure sizes and boundary
* alignment.
*/
ASSERT( sizeof(T_Blk) == sizeof_T_Blk )
ASSERT( sizeof(T_MBlockActionStream) == sizeof_T_MBlockActionStream )
/* Encoder instance memory should start on a 32 byte boundary.
*/
ASSERT( ( (unsigned int)P32Inst & 0x1f) == 0)
/* MB Action Stream should be on a 16 byte boundary.
*/
ASSERT( ( (unsigned int)EC->pU8_MBlockActionStream & 0xf) == 0 )
/* Block structure array should be on a 16 byte boundary.
*/
ASSERT( ( (unsigned int) &(EC->pU8_MBlockActionStream->BlkY1) & 0xf) == 0)
/* Current Frame Buffers should be on a 32 byte boundaries.
*/
ASSERT( ( (unsigned int)EC->pU8_CurrFrm_YPlane & 0x1f) == 0)
ASSERT( ( (unsigned int)EC->pU8_CurrFrm_UPlane & 0x1f) == 0)
ASSERT( ( (unsigned int)EC->pU8_CurrFrm_VPlane & 0x1f) == 0)
/* Previous Frame Buffers should be on 16 byte boundaries.
*/
ASSERT( ( (unsigned int)EC->pU8_PrevFrm_YPlane & 0x1f) == 0x10)
ASSERT( ( (unsigned int)EC->pU8_PrevFrm_UPlane & 0x1f) == 0x10)
ASSERT( ( (unsigned int)EC->pU8_PrevFrm_VPlane & 0x1f) == 0x10)
/* Spatial Loop Filter Frame Buffers should be on a 32 byte boundary.
*/
ASSERT( ( (unsigned int)EC->pU8_SLFFrm_YPlane & 0x1f) == 0)
ASSERT( ( (unsigned int)EC->pU8_SLFFrm_UPlane & 0x1f) == 0)
ASSERT( ( (unsigned int)EC->pU8_SLFFrm_VPlane & 0x1f) == 0)
/* Motion Estimation includes a memory layout. Assert that we satisfy it.
*/
ASSERT( ( (EC->pU8_PrevFrm_YPlane - EC->pU8_CurrFrm_YPlane) % 128) == 80)
ASSERT( ( (EC->pU8_SLFFrm_YPlane - EC->pU8_PrevFrm_YPlane) % 4096) == 944)
/* The bitstream should be on a 32 byte boundary
*/
ASSERT( ( (unsigned int)EC->pU8_BitStream & 0x1f) == 0)
/* DCT coefficient array should be on a 32 byte boundary.
*/
ASSERT( ( (unsigned int)EC->pU8_DCTCoefBuf & 0x1f) == 0)
/* Decoder instance structure should be on a DWORD boundary.
*/
ASSERT( ( (unsigned int)EC->pDecInstanceInfo & 0x3 ) == 0 )
/* Initialize MBActionStream
*/
int YBlockOffset, UBlockOffset;
puGOBNumbers = ( EC->PictureHeader.SourceFormat == SF_CIF ) ? uCIFGOBNumbers : uQCIFGOBNumbers;
for (uGOBNumber = *puGOBNumbers++, unStartingMB = 0;
uGOBNumber != 0;
uGOBNumber = *puGOBNumbers++, unStartingMB += 33)
{
YBlockOffset = uStartingYOffsets[uGOBNumber];
UBlockOffset = EC->pU8_CurrFrm_UPlane - EC->pU8_CurrFrm_YPlane + uStartingUOffsets[uGOBNumber];
for (unIndex = 0; unIndex < 33; )
{
iMBNumber = unStartingMB + unIndex;
/* Clear the counter of the number of consecutive times a macroblock has been inter coded.
*/
(EC->pU8_MBlockActionStream[iMBNumber]).InterCodeCnt = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY1.BlkOffset = YBlockOffset;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY2.BlkOffset = YBlockOffset+8;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY3.BlkOffset = YBlockOffset+PITCH*8;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY4.BlkOffset = YBlockOffset+PITCH*8+8;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkU.BlkOffset = UBlockOffset;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkV.BlkOffset = UBlockOffset+UV_OFFSET;
if (! EC->bUseMotionEstimation)
{
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY1.PHMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY1.PVMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY2.PHMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY2.PVMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY3.PHMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY3.PVMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY4.PHMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY4.PVMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkU.PHMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkU.PVMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkV.PHMV = 0;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkV.PVMV = 0;
}
YBlockOffset += 16;
UBlockOffset += 8;
unIndex++;
if (11 == unIndex || 22 == unIndex)
{
/* skip to the next row of macro blocks
* - skip forward number of lines in a MB and back 11 macroblocks
*/
YBlockOffset += PITCH*16 - 11*16;
UBlockOffset += PITCH*8 - 11*8;
}
else if (33 == unIndex)
{
/* Mark the last MB in this GOB.
*/
(EC->pU8_MBlockActionStream[iMBNumber]).CodedBlocks |= 0x40;
}
} /* end for unIndex */
} /* end for uGOBNumber */
ASSERT(unStartingMB == EC->NumMBs);
if (! EC->bUseMotionEstimation)
{
/* Initialize previous frame pointers.
*/
puGOBNumbers = ( EC->PictureHeader.SourceFormat == SF_CIF ) ? uCIFGOBNumbers : uQCIFGOBNumbers;
for (uGOBNumber = *puGOBNumbers++, unStartingMB = 0;
uGOBNumber != 0;
uGOBNumber = *puGOBNumbers++, unStartingMB += 33)
{
YBlockOffset = uStartingYOffsets[uGOBNumber];
UBlockOffset = EC->pU8_PrevFrm_UPlane - EC->pU8_PrevFrm_YPlane + uStartingUOffsets[uGOBNumber];
for (unIndex = 0; unIndex < 33; )
{
iMBNumber = unStartingMB + unIndex;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY1.B4_7.PastRef = EC->pU8_PrevFrm_YPlane + YBlockOffset;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY2.B4_7.PastRef = EC->pU8_PrevFrm_YPlane + YBlockOffset+8;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY3.B4_7.PastRef = EC->pU8_PrevFrm_YPlane + YBlockOffset+PITCH*8;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkY4.B4_7.PastRef = EC->pU8_PrevFrm_YPlane + YBlockOffset+PITCH*8+8;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkU.B4_7.PastRef = EC->pU8_PrevFrm_YPlane + UBlockOffset;
(EC->pU8_MBlockActionStream[iMBNumber]).BlkV.B4_7.PastRef = EC->pU8_PrevFrm_YPlane + UBlockOffset+UV_OFFSET;
YBlockOffset += 16;
UBlockOffset += 8;
unIndex++;
if (11 == unIndex || 22 == unIndex)
{
/* skip to the next row of macro blocks
* - skip forward number of lines in a MB and back 11 macroblocks
*/
YBlockOffset += PITCH*16 - 11*16;
UBlockOffset += PITCH*8 - 11*8;
}
} /* end for unIndex */
} /* end for uGOBNumber */
} /* end ! bUseMotionEstimation */
/* Initialize bit rate controller
*/
// RTP: BRC initialization. Changed call to InitBRC to accomodate
// uIntraQP and uInterQP
if(pConfiguration->bEncoderResiliency && pConfiguration->unPacketLoss)
{
uIntraQP = pConfiguration->unDefaultIntraQuant;
uInterQP = pConfiguration->unDefaultInterQuant;
}
else
{
uIntraQP = EC->u8DefINTRA_QP; //def263INTRA_QP;
uInterQP = EC->u8DefINTER_QP; //def263INTER_QP;
}
InitBRC(&(EC->BRCState), /* Address of the state structure */
uIntraQP, //EC->u8DefINTRA_QP, /* default INTRA Quantization value */
uInterQP, //EC->u8DefINTER_QP, /* default INTER Quantization value */
EC->NumMBs); /* number of Macroblocks */
// RTP: initialize BSInfoStream for RTP header generation
if (pConfiguration->bRTPHeader)
{
H261RTP_InitBsInfoStream(EC, pConfiguration->unPacketSize);
}
/* Finished initializing the encoder
*/
lpCompInst->Initialized = TRUE;
/*
* Create a decoder instance and init it. DecoderInstInfo must be in first 64K.
*/
EC->pDecInstanceInfo->xres = lpCompInst->xres;
EC->pDecInstanceInfo->yres = lpCompInst->yres;
lResult = H263InitDecoderInstance(EC->pDecInstanceInfo, H263_CODEC);
if (lResult != ICERR_OK)
{
DBOUT("Encoder's call to init the decoder failed.");
goto done;
}
lResult = H263InitColorConvertor(EC->pDecInstanceInfo, YUV12ForEnc);
if (lResult != ICERR_OK)
{
DBOUT("Encoder's call to init the color converter failed.");
goto done;
}
lResult = ICERR_OK;
done:
if (bEncoderInstLocked)
{
GlobalUnlock(lpCompInst->hEncoderInst);
}
return lResult;
} /* end H263InitEncoderInstance() */
// Define a variety of parameters to be used with motion estimation.
T_MotionEstimationControls MECatalog[] = {
#define ME_DEFAULT_CTRLS 0
{ 300, 128, 20, 150, 100, 100, 50 },
#define ME_MMX_CTRLS 1
// These parameters are used when MMX is enabled.
// NOTE: Of these, only the SLF parameters are currently used.
// For MMX, the other parameters are hard coded in exmme.asm.
{ 300, 128, 20, 150, 100, 200, 100 },
#define ME_CUSTOM_CTRLS 2
// These parameters are used when EC->bUseCustomMotionEstimation is enabled.
// EC-bUseCustomMotionEstimation, and the individual values here, can be
// set via the "ini" file.
{ 300, 128, 20, 150, 100, 100, 50 }
};
const U32 MAXCIFSIZE = 32768;
const U32 MAXQCIFSIZE = 8192;
/*****************************************************************************
*
* H263Compress
*
* This function drives the compression of one frame
*
*/
LRESULT H263Compress(
LPCODINST lpCompInst, /* ptr to compressor instance info. */
ICCOMPRESS *lpicComp) /* ptr to ICCOMPRESS structure. */
{
FX_ENTRY("H261Compress");
LRESULT lResult = ICERR_ERROR;
U32 uGOBNumber;
U32 uMB;
U8 * pu8CurBitStream; /* pointer to the current location in the bitstream. */
U32 * puGOBNumbers;
UN unGQuant;
UN unLastEncodedGQuant;
UN unSizeBitStream;
U32 uMaxSizeBitStream;
// UN unSize;
UN unStartingMB;
U8 u8BitOffset; /* bit offset in the current byte of the bitstream. */
/* Variables used in recompression */
BOOL bOverFlowWarning = FALSE;
BOOL bOverFlowSevereWarning = FALSE;
BOOL bOverFlowSevereDanger = FALSE;
BOOL bOverFlowed = FALSE;
U32 u32AverageSize;
U32 u32sizeBitBuffer;
U32 u32TooBigSize;
UN unGQuantTmp;
U8 u8GOBcount;
U32 iSWD;
U32 uMAXGOBNumber, uGOBsLeft;
/* save state for if need to recompress last GOB */
U8 * pu8CurBitStreamSave; /* ptr to the curr location in the bitstream. */
U8 u8BitOffsetSave; /* bit offset in the curr byte of the bitstream. */
UN unStartingMBSave;
UN unGQuantSave;
U8 u8CodedBlockSave[33];
U8 u8blocknum;
U8 * pu8FirstInterMEStateRowNumbers;
int inRowNumber;
int inMEStateIndex;
U32 uCumFrmSize = 0;
U32 uFrameCount;
U8 *pU8_temp; /* dummy pointer needed for FDCT */
U8 u8_temp; /* dummy variable needed for FDCT */
#ifdef ENCODE_STATS
U32 uStartLow;
U32 uStartHigh;
U32 uElapsed;
U32 uBefore;
U32 uEncodeFrameSum = 0;
U32 uInputCCSum = 0;
U32 uMotionEstimationSum = 0;
U32 uFDCTSum = 0;
U32 uQRLESum = 0;
U32 uDecodeFrameSum = 0;
U32 uZeroingBufferSum = 0;
// U32 uSLF_UVSum = 0;
int bTimingThisFrame = 0;
ENC_TIMING_INFO * pEncTimingInfo = NULL;
#endif
U32 uIntraSWDTotal;
U32 uIntraSWDBlocks;
U32 uInterSWDTotal;
U32 uInterSWDBlocks;
int MEC_index;
T_MotionEstimationControls MEC;
float fFrameRate;
U32 uFrameSize;
U32 uQuality;
T_H263EncoderInstanceMemory * P32Inst;
T_H263EncoderCatalog * EC;
LPVOID pEncoderInst = NULL;
ENC_BITSTREAM_INFO * pBSInfo;
ICDECOMPRESSEX ICDecExSt;
static ICDECOMPRESSEX DefaultICDecExSt = {
0,
NULL, NULL,
NULL, NULL,
0, 0, 0, 0,
0, 0, 0, 0
};
int uPQUANTMin;
// RTP: declaration
T_CONFIGURATION *pConfiguration = &(lpCompInst->Configuration);
#ifdef CHECKSUM_PICTURE
YVUCheckSum YVUCheckSumStruct;
U8 * pu8SaveCurBitStream;
U8 u8SaveBitOffset;
#endif
// check instance pointer
if (!lpCompInst)
return ICERR_ERROR;
/********************************************************************
* Lock the instance data private to the encoder.
********************************************************************/
pEncoderInst = (LPVOID)GlobalLock(lpCompInst->hEncoderInst);
if (pEncoderInst == NULL)
{
DBOUT("ERROR :: H263Compress :: ICERR_MEMORY");
lResult = ICERR_MEMORY;
goto done;
}
/* Generate pointer to the encoder instance memory.
*/
P32Inst = (T_H263EncoderInstanceMemory *)
((((U32) lpCompInst->EncoderInst) +
(sizeof(T_MBlockActionStream) - 1)) &
~(sizeof(T_MBlockActionStream) - 1));
/* Get pointer to encoder catalog.
*/
EC = &(P32Inst->EC);
// Check pointer to encoder catalog
if (!EC)
return ICERR_ERROR;
pBSInfo = &EC->BSInfo;
/********************************************************************
* Dummy operations.
********************************************************************/
pU8_temp = &u8_temp;
/********************************************************************
* Do per-frame initialization.
********************************************************************/
/* Get the frame number
*/
uFrameCount = pBSInfo->uKeyFrameCount + pBSInfo->uDeltaFrameCount;
#ifdef ENCODE_STATS
if (uFrameCount < DEC_TIMING_INFO_FRAME_COUNT)
{
ASSERT(EC->pEncTimingInfo);
TIMER_START(bTimingThisFrame,uStartLow,uStartHigh);
ASSERT(bTimingThisFrame);
EC->uStartLow = uStartLow;
EC->uStartHigh = uStartHigh;
EC->uStatFrameCount = uFrameCount;
}
else
{
ASSERT(!bTimingThisFrame);
}
EC->bTimingThisFrame = bTimingThisFrame;
#endif
if((lpicComp->dwFlags & ICCOMPRESS_KEYFRAME) ||
(EC->bMakeNextFrameKey == TRUE))
{
EC->PictureHeader.PicCodType = INTRAPIC;
EC->bMakeNextFrameKey = FALSE;
EC->PictureHeader.PicFreeze = ON;
}
else
{
EC->PictureHeader.PicCodType = INTERPIC;
EC->PictureHeader.PicFreeze = OFF;
DBOUT("INTERPIC...")
}
if (EC->PictureHeader.PicCodType == INTRAPIC)
{
/* Initialize macroblocks action stream for INTRA
*/
for (uMB = 0; uMB < EC->NumMBs; uMB++)
{
(EC->pU8_MBlockActionStream[uMB]).CodedBlocks |= 0x3F; /* Set to all nonempty blocks. */
(EC->pU8_MBlockActionStream[uMB]).CodedBlocksB = 0;
(EC->pU8_MBlockActionStream[uMB]).InterCodeCnt = ((U8)uMB)&0xF; /* Seed update pattern */
(EC->pU8_MBlockActionStream[uMB]).FirstMEState = ForceIntra;
if (! EC->bUseMotionEstimation)
{
(EC->pU8_MBlockActionStream[uMB]).BlockType = INTRABLOCK;
}
}
*(lpicComp->lpdwFlags) |= AVIIF_KEYFRAME;
lpicComp->dwFlags |= ICCOMPRESS_KEYFRAME;
}
else // not a key frame, motion vectors present
{
/* Setup to initialize the FirstMEState field. The initial data for
* the FirstMEState is stored compressed in rows of 11 bytes. Different
* rows are chosen for CIF and QCIF initialization.
*/
if ( EC->PictureHeader.SourceFormat == SF_CIF )
{
pu8FirstInterMEStateRowNumbers = u8CIFFirstInterMEStateRowNumbers;
}
else
{
pu8FirstInterMEStateRowNumbers = u8QCIFFirstInterMEStateRowNumbers;
}
inRowNumber = *pu8FirstInterMEStateRowNumbers++;
ASSERT(inRowNumber <= MAX_ME_STATE_ROW_NUMBER);
/* Initialize macroblocks action stream for INTER
*/
for (inMEStateIndex = 0, uMB = 0; uMB < EC->NumMBs; uMB++, inMEStateIndex++)
{
/* There are only 11 bytes in a row of data. So reset the Index and go to
* the next row number.
*/
if (inMEStateIndex == 11)
{
inMEStateIndex = 0;
inRowNumber = *pu8FirstInterMEStateRowNumbers++;
ASSERT(inRowNumber <= MAX_ME_STATE_ROW_NUMBER);
}
(EC->pU8_MBlockActionStream[uMB]).CodedBlocks |= 0x3F; /* Initialize to all nonempty blocks. */
(EC->pU8_MBlockActionStream[uMB]).CodedBlocksB = 0;
if (EC->pU8_MBlockActionStream[uMB].InterCodeCnt >=
(pConfiguration->bRTPHeader
? INTERCODECNT_ADJUST[pConfiguration->unPacketLoss/10]
: 132))
{
(EC->pU8_MBlockActionStream[uMB]).FirstMEState = ForceIntra; /* Force intra blocks. */
(EC->pU8_MBlockActionStream[uMB]).BlockType = INTRABLOCK;
}
else
{ // RTP: resiliency stuff
if (pConfiguration->bDisallowAllVerMVs)
(EC->pU8_MBlockActionStream[uMB]).FirstMEState =
u8FirstInterMENoVerMVStateRows[inRowNumber][inMEStateIndex];
else
{
if (pConfiguration->bDisallowPosVerMVs)
(EC->pU8_MBlockActionStream[uMB]).FirstMEState =
u8FirstInterMENoPosVerMVStateRows[inRowNumber][inMEStateIndex];
else
(EC->pU8_MBlockActionStream[uMB]).FirstMEState =
u8FirstInterMEStateRows[inRowNumber][inMEStateIndex];
}
if (! EC->bUseMotionEstimation)
{
(EC->pU8_MBlockActionStream[uMB]).BlockType = INTERBLOCK;
}
}
}
*(lpicComp->lpdwFlags) &= ~AVIIF_KEYFRAME;
lpicComp->dwFlags &= ~ICCOMPRESS_KEYFRAME;
}
// RTP: resiliency stuff
if (pConfiguration->bEncoderResiliency && pConfiguration->unPacketLoss)
{
UN i;
if (EC->uNumberForcedIntraMBs > 0)
{
for (i=0; i < EC->uNumberForcedIntraMBs; i++)
{
if (EC->uNextIntraMB == EC->NumMBs)
EC->uNextIntraMB=0;
(EC->pU8_MBlockActionStream[EC->uNextIntraMB]).FirstMEState =
ForceIntra;
if (! EC->bUseMotionEstimation)
{
(EC->pU8_MBlockActionStream[uMB]).BlockType = INTRABLOCK;
}
}
}
}
/* Initialize bit stream pointers */
pu8CurBitStream = EC->pU8_BitStream;
u8BitOffset = 0;
/******************************************************************
* RGB to YVU 12 Conversion
******************************************************************/
#ifdef ENCODE_STATS
TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore);
#endif
colorCnvtFrame(EC, lpCompInst, lpicComp,
EC->pU8_CurrFrm_YPlane,
EC->pU8_CurrFrm_UPlane,
EC->pU8_CurrFrm_VPlane);
#ifdef ENCODE_STATS
TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uInputCCSum)
#endif
/********************************************************************
* Setup the bit rate controller
********************************************************************/
// RTP: Configuration setting
// If the Encoder Bit Rate section of the configuration has been
// set ON then, we override quality only or any frame size normally
// sent in and use frame rate and data rate to determine frame
// size.
if (EC->PictureHeader.PicCodType == INTERPIC &&
lpCompInst->Configuration.bBitRateState == TRUE &&
lpCompInst->FrameRate != 0.0f &&
lpicComp->dwFrameSize == 0UL)
{
DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: Changing dwFrameSize from %ld to %ld bits\r\n", _fx_, lpicComp->dwFrameSize << 3, (DWORD)((float)lpCompInst->DataRate / lpCompInst->FrameRate) << 3));
lpicComp->dwFrameSize = (U32)((float)lpCompInst->DataRate / lpCompInst->FrameRate);
}
uFrameSize = lpicComp->dwFrameSize;
#ifdef DEBUG_RECOMPRESS
wsprintf(string, "uFrameSize %d", (int) uFrameSize);
DBOUT(string);
#endif
/* check uFrameSize. Max compressed frame size for QCIF is 8KBytes
* and 32 KBytes for FCIF.
*/
if ( EC->PictureHeader.SourceFormat == SF_CIF )
{
uMaxSizeBitStream = MAXCIFSIZE;
if (uFrameSize > MAXCIFSIZE)
uFrameSize = MAXCIFSIZE;
}
else
{
uMaxSizeBitStream = MAXQCIFSIZE;
if (uFrameSize > MAXQCIFSIZE)
uFrameSize = MAXQCIFSIZE;
}
#ifdef DEBUG_RECOMPRESS
wsprintf(string, "uFrameSize %d", (int) uFrameSize);
DBOUT(string);
#endif
uQuality = lpicComp->dwQuality;
fFrameRate = lpCompInst->FrameRate;
StartupBRC(EC, uFrameSize, uQuality, fFrameRate);
/* QRLE does not do clamping - it passes 8-bits to VLC. Because of
* that we need to be sure that all values can be represented by 255.
* QP-4 represents +-2040 leaving out 2041..2048.
* QP-5 represents +-2550 which has no clamping problems.
* Because QRLE does not do clamping we should limit our QP to 5.
* But I still see some clamping artifacts at 5. See the "Tom" video
* encoded with a fixed quantizer at frame 100. For that reason I
* am limiting my QP to 6 (which is the same value as the 750 encoder).
*
* If you had unlimited time you could look at the first four coefficients
* and pretty safely decide if you can use lower quantizers. Since we
* are supposed to run on a P5/90 we don't have that time.
*/
//CLAMP_N_TO(EC->PictureHeader.PQUANT,6,31);
/* Change Clamp range to allow quant to get to 2 unless fixed quant
* or quality setting used instead of data rate. This does probably
* cause quantization errors at high data rates the question is does
* it cause noticable errors at ISDN data rates when the clips are
* playing at speed? This will be evaluated witn Beta 02 candidate.
* Changes made in e1enc and e1mbenc.
*/
if (EC->BRCState.uTargetFrmSize == 0)
{
CLAMP_N_TO(EC->PictureHeader.PQUANT,6,31);
}
else
{
uPQUANTMin = clampQP((10000L - (int)lpicComp->dwQuality) * 15L / 10000L);
CLAMP_N_TO(EC->PictureHeader.PQUANT, uPQUANTMin, 31);
}
// also set previous GQuant
unLastEncodedGQuant = EC->PictureHeader.PQUANT;
if (EC->bBitRateControl)
{
/* Initialize Cumulative Quantization values
*/
EC->uQP_cumulative = 0;
EC->uQP_count = 0;
}
/* Increment temporal reference.
*/
#ifdef RING0
Increment_TR_UsingFrameRate(&(EC->PictureHeader.TR),
&(EC->fTR_Error),
fFrameRate,
(pBSInfo->uKeyFrameCount + pBSInfo->uDeltaFrameCount) == 0,
0x1F);
#else
Increment_TR_UsingTemporalValue(&(EC->PictureHeader.TR),
&(EC->u8LastTR),
lpicComp->lFrameNum,
(pBSInfo->uKeyFrameCount + pBSInfo->uDeltaFrameCount) == 0,
0x1F);
#endif
// RTP: packet initialization for first GOB
if (pConfiguration->bRTPHeader)
{
H261RTP_GOBUpdateBsInfo(EC, 1, pu8CurBitStream, 0);
}
/********************************************************************
* Write the Picture Header
*******************************************************************/
WriteBeginPictureHeaderToStream(EC, &pu8CurBitStream, &u8BitOffset);
#ifdef CHECKSUM_PICTURE
/* Initialize the checksum record to all zeros.
*/
YVUCheckSumStruct.uYCheckSum = 0;
YVUCheckSumStruct.uVCheckSum = 0;
YVUCheckSumStruct.uUCheckSum = 0;
/* save the pointers
*/
pu8SaveCurBitStream = pu8CurBitStream;
u8SaveBitOffset = u8BitOffset;
/* write the zero checksum
*/
WritePictureChecksum(&YVUCheckSumStruct, &pu8CurBitStream, &u8BitOffset, 0);
#endif
WriteEndPictureHeaderToStream(EC, &pu8CurBitStream, &u8BitOffset);
/********************************************************************
* Inner Loop: Loop through GOBs and macroblocks.
********************************************************************/
puGOBNumbers = ( EC->PictureHeader.SourceFormat == SF_CIF ) ? uCIFGOBNumbers : uQCIFGOBNumbers;
uMAXGOBNumber = ( EC->PictureHeader.SourceFormat == SF_CIF ) ? 12 : 3;
u32sizeBitBuffer = CompressGetSize(lpCompInst, lpicComp->lpbiInput,
lpicComp->lpbiOutput);
/* Check to see if we told VfW to create a buffer smaller than the maximum allowable.
*/
ASSERT( u32sizeBitBuffer <= sizeof_bitstreambuf );
if (EC->PictureHeader.PicCodType == INTRAPIC)
{
u32AverageSize =
(EC->PictureHeader.SourceFormat == SF_CIF ) ?
(7 * u32sizeBitBuffer/FCIF_NUM_OF_GOBS) >> 3:
(7 * u32sizeBitBuffer/QCIF_NUM_OF_GOBS) >> 3;
}
else
{
u32AverageSize =
(EC->PictureHeader.SourceFormat == SF_CIF ) ?
(8 * u32sizeBitBuffer/FCIF_NUM_OF_GOBS) >> 4:
(8 * u32sizeBitBuffer/QCIF_NUM_OF_GOBS) >> 4;
}
// Select motion estimation parameters.
if (EC->bUseCustomMotionEstimation)
MEC_index = ME_CUSTOM_CTRLS;
else
MEC_index = ME_DEFAULT_CTRLS;
// Make a local copy of the controls, so that we can alter the
// controls dynamically, without destroying the original values.
MEC = MECatalog[MEC_index];
if (pConfiguration->bRTPHeader) {
MEC.empty_threshold /= EMPTYTHRESHOLD_ADJUST[pConfiguration->unPacketLoss/25];
if (pConfiguration->unPacketLoss > 25) {
MEC.zero_vector_threshold = 99999;
MEC.nonzero_MV_differential = 99999;
if ((MEC.slf_differential <<= 2) > 99999)
MEC.slf_differential = 99999;
if (pConfiguration->unPacketLoss > 50) {
MEC.slf_threshold = 99999;
}
}
}
for (uGOBNumber = *puGOBNumbers++, unStartingMB = 0, u8GOBcount = 1;
uGOBNumber != 0;
uGOBNumber = *puGOBNumbers++, unStartingMB += 33, u8GOBcount++)
{
#ifdef DEBUG_ENC
wsprintf(string, "GOB #%d", (int) uGOBNumber);
DBOUT(string);
trace(string);
#endif
uGOBsLeft = uMAXGOBNumber - u8GOBcount;
if (EC->bUseMotionEstimation)
{
#ifdef ENCODE_STATS
TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore);
#endif
MOTIONESTIMATION(
&(EC->pU8_MBlockActionStream[unStartingMB]),
EC->pU8_CurrFrm_YPlane,
EC->pU8_PrevFrm_YPlane,
EC->pU8_SLFFrm_YPlane,
1, // Do Radius 15 search.
0, // No Half Pel motion estimation
0, // No Block MVs
(int)EC->bUseSpatialLoopFilter,
MEC.zero_vector_threshold, // Zero Vector Threshold. If the
// SWD for the zero vector is less than this
// threshold, then don't search for NZ MV's.
// Set to 99999 to not search.
MEC.nonzero_MV_differential, // NonZeroMVDifferential.
// Once the best NZ MV is found, it must be better
// than the 0 MV SWD by at least this amount.
// Set to 99999 to never choose NZ MV.
128, // BlockMVDifferential. The sum of the four block
// SWD must be better than the MB SWD by at least
// this amount to choose block MV's.
// H.261 don't care
MEC.empty_threshold, // Empty Threshold. Set to 0 to not force
// empty blocks.
MEC.intercoding_threshold, // Inter Coding Threshold. If the
// inter SWD is less than this amount then don't
// bother calc. the intra SWD.
MEC.intercoding_differential, // Intra Coding Differential.
// Bias against choosing INTRA blocks.
MEC.slf_threshold, // If the SWD of the chosen MV is less than
// this threshold, then don't bother investigating
// the spatial loop filtered case.
MEC.slf_differential, // If you do look at the SLF case, its
// SWD must be better than the non-SLF SWD by at
// least this much in order to select the SLF type.
&uIntraSWDTotal,
&uIntraSWDBlocks,
&uInterSWDTotal,
&uInterSWDBlocks
);
#ifdef ENCODE_STATS
TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uMotionEstimationSum)
#endif
/* If it's an inter frame then, calculate chroma vectors and update the InterCodeCnt.
*/
if (EC->PictureHeader.PicCodType == INTERPIC)
{
// RTP: added pConfiguration to CalcGOBChromaVecs()
CalcGOBChromaVecs(EC, unStartingMB, pConfiguration);
} /* end INTERPIC */
} /* end if UseMotionEstimation */
/* Calculate unGQuant based on bits used in previous GOBs, and bits used
* for current GOB of previous frame.
*/
if (EC->bBitRateControl)
{
unGQuantTmp = unGQuant;
unGQuant = CalcMBQUANT(&(EC->BRCState), EC->uBitUsageProfile[unStartingMB],EC->uBitUsageProfile[EC->NumMBs], uCumFrmSize, EC->PictureHeader.PicCodType);
EC->uBitUsageProfile[unStartingMB] = uCumFrmSize;
DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, ("%s: Bitrate controller enabled for GOB #%ld (uCumFrmSize = %ld bits and unGQuantTmp = %ld), setting unGQuant = %ld (min and max will truncate from %ld to 31)\r\n", _fx_, uGOBNumber, uCumFrmSize << 3, unGQuantTmp, unGQuant, uPQUANTMin));
/* if bOverFlowSevereDanger True Increase Quant */
if ( bOverFlowSevereDanger )
{
DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, ("%s: Danger of overflow for GOB #%ld, changing unGQuant from %ld to %ld\r\n", _fx_, uGOBNumber, unGQuant, (unGQuant < unGQuantTmp) ? (unGQuantTmp + ((EC->PictureHeader.PicCodType == INTRAPIC) ? 12 : 6)) : (unGQuant + ((EC->PictureHeader.PicCodType == INTRAPIC) ? 12 : 6))));
if (unGQuant < unGQuantTmp)
unGQuant = unGQuantTmp;
if (EC->PictureHeader.PicCodType == INTRAPIC)
unGQuant += 12;
else
unGQuant += 6;
DBOUT("Increasing GQuant increase by +6");
}
else if ( bOverFlowSevereWarning )
{
DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, ("%s: Danger of overflow for GOB #%ld, changing unGQuant from %ld to %ld\r\n", _fx_, uGOBNumber, unGQuant, (unGQuant < unGQuantTmp) ? (unGQuantTmp + ((EC->PictureHeader.PicCodType == INTRAPIC) ? 8 : 4)) : (unGQuant + ((EC->PictureHeader.PicCodType == INTRAPIC) ? 8 : 4))));
/* if bOverFlowSevereWarning True Increase Quant */
if (unGQuant < unGQuantTmp)
unGQuant = unGQuantTmp;
if (EC->PictureHeader.PicCodType == INTRAPIC)
unGQuant += 8;
else
unGQuant += 4;
DBOUT("Increasing GQuant increase by +4");
}
else if ( !bOverFlowWarning )
{
DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, ("%s: Warning of overflow for GOB #%ld, changing unGQuant from %ld to %ld\r\n", _fx_, uGOBNumber, unGQuant, ((int)unGQuant > ((int)unLastEncodedGQuant + MaxChangeRowMBTbl[unGQuant])) ? (unLastEncodedGQuant + MaxChangeRowMBTbl[unGQuant]) : (((int)unGQuant < ((int)unLastEncodedGQuant - 2)) ? (unLastEncodedGQuant - 2) : unGQuant)));
/* if bOverFlowWarning False limit Quant changes */
/* Limit the quant changes */
if ((int)unGQuant > ((int)unLastEncodedGQuant + MaxChangeRowMBTbl[unGQuant]))
{
unGQuant = unLastEncodedGQuant + MaxChangeRowMBTbl[unGQuant];
DBOUT("Slowing GQuant increase to +[1-4]");
}
else if ((int)unGQuant < ((int)unLastEncodedGQuant - 2))
{
unGQuant = unLastEncodedGQuant - 2;
DBOUT("Slowing GQuant decrease to -2");
}
}
else
{
DBOUT("bOverFlowWarning don't limit Quant change");
}
if (EC->BRCState.uTargetFrmSize == 0)
{
CLAMP_N_TO(unGQuant,6,31);
}
else
{
CLAMP_N_TO(unGQuant, uPQUANTMin , 31);
}
unLastEncodedGQuant = unGQuant;
#ifdef DEBUG_BRC
wsprintf(string,"At MB %d GQuant=%d", unStartingMB, unGQuant);
DBOUT(string);
#endif
}
else
{
unGQuant = EC->PictureHeader.PQUANT;
}
if ( bOverFlowWarning )
{
/* save state may need to recompress */
pu8CurBitStreamSave = pu8CurBitStream;
u8BitOffsetSave = u8BitOffset;
unStartingMBSave = unStartingMB;
unGQuantSave = unGQuant;
for (u8blocknum = 0; u8blocknum < 33; u8blocknum++)
{
/* go through last GOBs macroblock action stream
* saving coded block type because quantization
* resets the pattern in some blocks quantize to 0.
*/
u8CodedBlockSave[u8blocknum] =
(EC->pU8_MBlockActionStream[unStartingMB+u8blocknum]).CodedBlocks;
}
}
// RTP: GOB update
if ( (uGOBNumber != 1) && (pConfiguration->bRTPHeader) )
{
H261RTP_GOBUpdateBsInfo(EC, uGOBNumber, pu8CurBitStream, (U32)
u8BitOffset);
}
WriteGOBHeaderToStream(uGOBNumber, unGQuant, &pu8CurBitStream, &u8BitOffset);
/* Input is the macroblock action stream with pointers to
* current and previous blocks. Output is a set of 32 DWORDs
* containing pairs of coefficients for each block. There are
* from 0 to 12 blocks depending on if PB frames are used and
* what the CBP field states.
*/
#ifdef ENCODE_STATS
TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore);
#endif
FORWARDDCT( &(EC->pU8_MBlockActionStream[unStartingMB]),
EC->pU8_CurrFrm_YPlane,
EC->pU8_PrevFrm_YPlane,
0,
EC->pU8_DCTCoefBuf,
0, // 0 = not a B-frame
0, // 0 = AP not on
0, // 0 = PB?
pU8_temp, // Scratch
EC->NumMBPerRow
);
#ifdef ENCODE_STATS
TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uFDCTSum)
#endif
/* Input is the string of coefficient pairs output from the
* DCT routine.
*/
#ifdef ENCODE_STATS
TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore);
#endif
GOB_Q_RLE_VLC_WriteBS(
EC,
EC->pU8_DCTCoefBuf,
&pu8CurBitStream,
&u8BitOffset,
unStartingMB,
unGQuant,
bOverFlowSevereWarning,
(BOOL) pConfiguration->bRTPHeader, // RTP: MBUpdate flag
uGOBNumber,
uPQUANTMin);
#ifdef ENCODE_STATS
TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uQRLESum)
#endif
/* Calculate number of bytes used in frame so far.
*/
uCumFrmSize = pu8CurBitStream - EC->pU8_BitStream;
/* Try to make sure we won't overrun the bit stream buffer. Take the stuffing bytes
* at the end into account when you do this. Also make sure that if this GOB is not
* recompressed then there is enough space left to send SKIP GOBs for the remaining
* GOBs. I am using sizeof (SKIP GOB) = 2 bytes.
*/
if ( (unsigned)uCumFrmSize >=
(u32sizeBitBuffer - (uGOBsLeft*2) - 10) )
{
/* Already about to exceed need to recompress */
DBOUT("Need to RECOMPRESS");
if ( bOverFlowWarning )
{
#ifdef DEBUG_RECOMPRESS
wsprintf(string,"Bits Used before recompress= %d ", uCumFrmSize*8);
DBOUT(string);
//trace(string);
#endif
u32TooBigSize = uCumFrmSize;
bOverFlowSevereDanger = TRUE;
/* zero out last GOBs worth of bitstream */
U8 u8temp;
u8temp = *pu8CurBitStreamSave;
u8temp = (u8temp>>(8-u8BitOffsetSave))<<(8-u8BitOffsetSave);
*pu8CurBitStreamSave = u8temp;
memset(pu8CurBitStreamSave+1, 0, pu8CurBitStream - pu8CurBitStreamSave);
/* restore state */
pu8CurBitStream = pu8CurBitStreamSave;
u8BitOffset = u8BitOffsetSave;
unStartingMB = unStartingMBSave;
if (EC->PictureHeader.PicCodType == INTRAPIC)
unGQuant = unGQuantSave + 16;
else
unGQuant = unGQuantSave + 8;
CLAMP_N_TO(unGQuant,6,31);
// RTP: rewind operation
if (pConfiguration->bRTPHeader)
H261RTP_RewindBsInfoStream(EC, (U32) uGOBNumber);
for (u8blocknum = 0; u8blocknum < 33; u8blocknum++)
{
/* go through GOBs macroblock action stream
* restoring coded block type because quantization
* resets the patter in some blocks quantize to 0.
*/
(EC->pU8_MBlockActionStream[unStartingMB+u8blocknum]).CodedBlocks = u8CodedBlockSave[u8blocknum];
}
/* rewrite GOB header */
WriteGOBHeaderToStream(uGOBNumber, unGQuant, &pu8CurBitStream, &u8BitOffset);
GOB_Q_RLE_VLC_WriteBS(
EC,
EC->pU8_DCTCoefBuf,
&pu8CurBitStream,
&u8BitOffset,
unStartingMB,
unGQuant,
bOverFlowSevereDanger,
pConfiguration->bRTPHeader, // RTP: switch
uGOBNumber, // RTP: info
uPQUANTMin);
/* test if still too big if so just send skip GOB */
/* For intended KEY frames, this is a problem */
uCumFrmSize = pu8CurBitStream - EC->pU8_BitStream;
if ( (unsigned) uCumFrmSize >=
(u32sizeBitBuffer - (uGOBsLeft*2) - 10) )
{
bOverFlowed = TRUE;
/* zero out last GOBs worth of bitstream */
u8temp = *pu8CurBitStreamSave;
u8temp = (u8temp>>(8-u8BitOffsetSave))<<(8-u8BitOffsetSave);
*pu8CurBitStreamSave = u8temp;
memset(pu8CurBitStreamSave+1, 0, pu8CurBitStream - pu8CurBitStreamSave);
/* restore state */
pu8CurBitStream = pu8CurBitStreamSave;
u8BitOffset = u8BitOffsetSave;
unStartingMB = unStartingMBSave;
// unGQuant = unGQuantSave + 8;
// RTP: rewind operation
if (pConfiguration->bRTPHeader)
H261RTP_RewindBsInfoStream(EC, (U32) uGOBNumber);
WriteGOBHeaderToStream(uGOBNumber, unGQuant, &pu8CurBitStream, &u8BitOffset);
/* write out a stuffing code */
PutBits(FIELDVAL_MBA_STUFFING, FIELDLEN_MBA_STUFFING, &pu8CurBitStream, &u8BitOffset);
DBOUT("Just Sent SKIP GOB");
#ifdef DEBUG_RECOMPRESS
wsprintf(string,"Just Sent SKIP GOB");
//trace(string);
#endif
}
}
else
{
DBOUT("Did not save state to recompress");
}
}
/* Calculate number of bytes used in frame so far.
*/
uCumFrmSize = pu8CurBitStream - EC->pU8_BitStream;
#ifdef DEBUG_RECOMPRESS
wsprintf(string,"Bits Used = %d ", uCumFrmSize*8);
DBOUT(string);
//trace(string);
#endif
/* Check to make sure we haven't overrun the bit stream buffer.
*/
ASSERT( (unsigned) uCumFrmSize < u32sizeBitBuffer );
/* Setup method to determine if might have a problem with buffer size */
bOverFlowWarning =
bOverFlowSevereWarning =
bOverFlowSevereDanger = FALSE;
if (uCumFrmSize > u8GOBcount*u32AverageSize)
{
/* allow for more Quant level changes */
bOverFlowWarning = TRUE;
if (uCumFrmSize > u8GOBcount*u32AverageSize + (u32AverageSize>>1))
{
/* force Quant level increase */
bOverFlowSevereWarning = TRUE;
DBOUT("bOverFlowSevereWarning");
if (uCumFrmSize > u8GOBcount*u32AverageSize+u32AverageSize)
{
/* force Quant level increase by even more */
bOverFlowSevereDanger = TRUE;
DBOUT("bOverFlowSevereDanger");
}
}
}
} /* for uGOBNumber */
/* if recompress failed shove big number in here */
if (bOverFlowed)
{
/* used max size so quant will go up for next frame */
EC->uBitUsageProfile[unStartingMB] = u32TooBigSize;
}
else
EC->uBitUsageProfile[unStartingMB] = uCumFrmSize;
/* Make sure we end this frame on a byte boundary - some decoders require this.
*/
while (u8BitOffset != 0)
{
PutBits(FIELDVAL_MBA_STUFFING, FIELDLEN_MBA_STUFFING, &pu8CurBitStream, &u8BitOffset);
}
/* Add extra DWORD of zero's to try and get rid of green blocks akk */
/* 16 bits of zeros seems to work */
/* 8 bits of zeros does not seems to work */
#ifdef DWORD_HACK
PutBits(0x0000, 16, &pu8CurBitStream, &u8BitOffset);
#endif
if (EC->bBitRateControl)
{
CalculateQP_mean(EC);
}
/********************************************************************
* Calculate the size of the compressed image.
********************************************************************/
unSizeBitStream = pu8CurBitStream - EC->pU8_BitStream;
lpCompInst->CompressedSize = unSizeBitStream;
// IP + UDP + RTP + payload mode C header - worst case
#define TRANSPORT_HEADER_SIZE (20 + 8 + 12 + 12)
DWORD dwTransportOverhead;
// Estimate the transport overhead
if (pConfiguration->bRTPHeader)
dwTransportOverhead = (lpCompInst->CompressedSize / pConfiguration->unPacketSize + 1) * TRANSPORT_HEADER_SIZE;
else
dwTransportOverhead = 0UL;
if (EC->PictureHeader.PicCodType == INTRAPIC)
{
#ifdef _DEBUG
wsprintf(string, "Intra Frame %d size: %d", pBSInfo->uKeyFrameCount + pBSInfo->uDeltaFrameCount, unSizeBitStream);
#endif
pBSInfo->uKeyFrameCount ++;
pBSInfo->uTotalKeyBytes += unSizeBitStream;
if (EC->bBitRateControl)
{
EC->BRCState.uLastINTRAFrmSz = dwTransportOverhead + unSizeBitStream;
}
}
else
{
#ifdef _DEBUG
wsprintf(string, "Inter Frame %d size: %d", pBSInfo->uKeyFrameCount + pBSInfo->uDeltaFrameCount, unSizeBitStream);
#endif
pBSInfo->uDeltaFrameCount ++;
pBSInfo->uTotalDeltaBytes += unSizeBitStream;
if (EC->bBitRateControl)
{
EC->BRCState.uLastINTERFrmSz = dwTransportOverhead + unSizeBitStream;
}
}
#ifdef _DEBUG
DBOUT(string)
#endif
DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: Total cumulated frame size = %ld bits (data: %ld, transport overhead: %ld)\r\n", _fx_, (unSizeBitStream + dwTransportOverhead) << 3, unSizeBitStream << 3, dwTransportOverhead << 3));
/********************************************************************
* Run the decoder on this frame, to get next basis for prediction.
********************************************************************/
ICDecExSt = DefaultICDecExSt;
ICDecExSt.lpSrc = EC->pU8_BitStream;
ICDecExSt.lpbiSrc = lpicComp->lpbiOutput;
ICDecExSt.lpbiSrc->biSizeImage = unSizeBitStream;
ICDecExSt.lpDst = P32Inst->u8PreviousPlane;
ICDecExSt.lpbiDst = NULL;
if (EC->PictureHeader.PicCodType == INTERPIC)
{
ICDecExSt.dwFlags = ICDECOMPRESS_NOTKEYFRAME;
}
#ifdef ENCODE_STATS
TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore);
#endif
lResult = H263Decompress (EC->pDecInstanceInfo, (ICDECOMPRESSEX FAR *)&ICDecExSt, FALSE);
if (lResult != ICERR_OK)
{
DBOUT("Encoder's call to decompress failed.");
EC->bMakeNextFrameKey = TRUE;
goto done;
}
#ifdef ENCODE_STATS
TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uDecodeFrameSum)
#endif
#ifdef CHECKSUM_PICTURE
lResult = H261PictureCheckSumEntry(EC->pDecInstanceInfo, &YVUCheckSumStruct);
if (lResult != ICERR_OK)
{
DBOUT("Encoder's call to compute the picture checksum failed.");
goto done;
}
/* restore the pointers
*/
pu8CurBitStream = pu8SaveCurBitStream;
u8BitOffset = u8SaveBitOffset;
/* update the checksum
*/
WritePictureChecksum(&YVUCheckSumStruct, &pu8CurBitStream, &u8BitOffset, 1);
#endif
if (unSizeBitStream > uMaxSizeBitStream)
{
// Exceeded allowed - 8K for QCIF and 32K for CIF - size
DBOUT("BS exceeds allowed size");
EC->bMakeNextFrameKey = TRUE;
goto done;
}
// RTP: bstream extension attachment
if (pConfiguration->bRTPHeader)
{
// Changed this if statement to check for overflow of bitstream buffer
// 4/14/97 AG.
U32 uEBSSize = H261RTP_GetMaxBsInfoStreamSize(EC);
if (uEBSSize + unSizeBitStream <= u32sizeBitBuffer)
{
unSizeBitStream +=
(WORD) H261RTP_AttachBsInfoStream(EC, (U8 *)EC->pU8_BitStream,
unSizeBitStream);
lpCompInst->CompressedSize = unSizeBitStream;
}
else
{
DBOUT("BS+EBS exceeds allocated buffer size");
EC->bMakeNextFrameKey = TRUE;
goto done;
}
}
#ifndef RING0
#ifdef DEBUG
{
char buf[60];
wsprintf(buf, "Compressed frame is %d bytes\n", unSizeBitStream);
DBOUT(buf);
}
#endif
#endif
/********************************************************************
* Copy the compressed image to the output area. This is done after
* possibly updating the picture checksum.
********************************************************************/
memcpy( lpicComp->lpOutput, EC->pU8_BitStream, unSizeBitStream);
/* zero only the dirty part of the bitstream buffer */
#ifdef ENCODE_STATS
TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore);
#endif
// unSize = CompressGetSize(lpCompInst, lpicComp->lpbiInput, lpicComp->lpbiOutput);
memset(EC->pU8_BitStream, 0, unSizeBitStream);
#ifdef ENCODE_STATS
TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uZeroingBufferSum)
#endif
#ifdef ENCODE_STATS
TIMER_STOP(bTimingThisFrame,uStartLow,uStartHigh,uEncodeFrameSum);
if (bTimingThisFrame)
{
pEncTimingInfo = EC->pEncTimingInfo + uFrameCount;
pEncTimingInfo->uEncodeFrame = uEncodeFrameSum;
pEncTimingInfo->uInputCC = uInputCCSum;
pEncTimingInfo->uMotionEstimation = uMotionEstimationSum;
pEncTimingInfo->uFDCT = uFDCTSum;
pEncTimingInfo->uQRLE = uQRLESum;
pEncTimingInfo->uDecodeFrame = uDecodeFrameSum;
pEncTimingInfo->uZeroingBuffer = uZeroingBufferSum;
// pEncTimingInfo->uSLF_UV = uSLF_UVSum;
/* Verify that we have time for all the required steps
*/
ASSERT(pEncTimingInfo->uEncodeFrame);
ASSERT(pEncTimingInfo->uInputCC);
ASSERT(pEncTimingInfo->uMotionEstimation);
ASSERT(pEncTimingInfo->uFDCT);
ASSERT(pEncTimingInfo->uQRLE);
ASSERT(pEncTimingInfo->uDecodeFrame);
ASSERT(pEncTimingInfo->uZeroingBuffer);
// ASSERT(pEncTimingInfo->uSLF_UV);
}
#endif
lResult = ICERR_OK;
done:
if (pEncoderInst)
{
GlobalUnlock(lpCompInst->hEncoderInst);
}
return lResult;
} /* end H263Compress() */
/*****************************************************************************
*
* H263TermEncoderInstance
*
* This function frees the space allocated for an instance of the H263 encoder.
*/
LRESULT H263TermEncoderInstance(LPCODINST lpCompInst)
{
LRESULT lResult;
LRESULT lLockingResult;
LRESULT lDecoderResult;
LRESULT lColorOutResult;
U8 BIGG * P32Inst;
T_H263EncoderCatalog FAR * EC;
// Check instance pointer
if (!lpCompInst)
return ICERR_ERROR;
if(lpCompInst->Initialized == FALSE)
{
DBOUT("Warning: H263TermEncoderInstance(): Uninitialized instance")
lResult = ICERR_OK;
goto done;
}
lpCompInst->Initialized = FALSE;
lpCompInst->EncoderInst = (LPVOID)GlobalLock(lpCompInst->hEncoderInst);
if (lpCompInst->EncoderInst == NULL)
{
DBOUT("ERROR :: H263TermEncoderInstance :: ICERR_MEMORY");
lLockingResult = ICERR_MEMORY;
lColorOutResult = ICERR_OK;
lDecoderResult = ICERR_OK;
}
else
{
lLockingResult = ICERR_OK;
P32Inst = (U8 *)
((((U32) lpCompInst->EncoderInst) +
(sizeof(T_MBlockActionStream) - 1)) &
~(sizeof(T_MBlockActionStream) - 1));
// P32Inst = (U8 *) ((((U32) lpCompInst->EncoderInst) + 31) & ~0x1F);
EC = ((T_H263EncoderCatalog *) P32Inst);
// Check encoder catalog pointer
if (!EC)
return ICERR_ERROR;
#ifdef ENCODE_STATS
// OutputEncodeBitStreamStatistics(ENCODE_STATS_FILENAME, &EC->BSInfo, EC->PictureHeader.SourceFormat == SF_CIF);
OutputEncodeTimingStatistics(ENCODE_STATS_FILENAME, EC->pEncTimingInfo);
#endif
/* Terminate the color converter
*/
lColorOutResult = H263TermColorConvertor(EC->pDecInstanceInfo);
if (lColorOutResult != ICERR_OK)
{
DBOUT("Terminating the color converter failed.");
}
/* Terminate the decoder
*/
lDecoderResult = H263TermDecoderInstance(EC->pDecInstanceInfo);
if (lDecoderResult != ICERR_OK)
{
DBOUT("Terminating the decoder failed.");
}
GlobalUnlock(lpCompInst->hEncoderInst);
GlobalFree(lpCompInst->hEncoderInst);
}
/* set the result
*/
if (lLockingResult != ICERR_OK)
{
lResult = lLockingResult;
}
else if (lColorOutResult != ICERR_OK)
{
lResult = lColorOutResult;
}
else if (lDecoderResult != ICERR_OK)
{
lResult = lDecoderResult;
}
else
{
lResult = ICERR_OK;
}
done:
return lResult;
}
/*****************************************************************************
*
* WriteBeginPictureHeaderToStream
*
* Write the beginning of the picture header to the stream updating the
* stream pointer and the bit offset. The beginning of the picture header
* is everything but the zero PEI bit
*/
static void WriteBeginPictureHeaderToStream(
T_H263EncoderCatalog *EC,
U8 ** ppu8CurBitStream,
U8 * pu8BitOffset)
{
/* Picture Start Code */
PutBits(FIELDVAL_PSC, FIELDLEN_PSC, ppu8CurBitStream, pu8BitOffset);
/* Temporal Reference */
PutBits( EC->PictureHeader.TR, FIELDLEN_TR, ppu8CurBitStream, pu8BitOffset);
/* PTYPE: Split screen indicator */
PutBits( EC->PictureHeader.Split, FIELDLEN_PTYPE_SPLIT, ppu8CurBitStream, pu8BitOffset);
/* PTYPE: Document camera indicator. */
PutBits( EC->PictureHeader.DocCamera, FIELDLEN_PTYPE_DOC, ppu8CurBitStream, pu8BitOffset);
/* PTYPE: Freeze picture release. */
PutBits( EC->PictureHeader.PicFreeze, FIELDLEN_PTYPE_RELEASE, ppu8CurBitStream, pu8BitOffset);
/* PTYPE: Source image format. */
PutBits( EC->PictureHeader.SourceFormat, FIELDLEN_PTYPE_SRCFORMAT, ppu8CurBitStream, pu8BitOffset);
/* PTYPE: Still image indicator. */
PutBits( EC->PictureHeader.StillImage, FIELDLEN_PTYPE_STILL, ppu8CurBitStream, pu8BitOffset);
/* PTYPE: Still image indicator. */
PutBits( EC->PictureHeader.Spare, FIELDLEN_PTYPE_SPARE, ppu8CurBitStream, pu8BitOffset);
} /* end WriteBeginPictureHeaderToStream() */
/*****************************************************************************
*
* WriteEndPictureHeaderToStream
*
* Write the end of the picture header to the stream updating the
* stream pointer and the bit offset. The end of the picture header is the
* zero PEI bit.
*/
static void WriteEndPictureHeaderToStream(
T_H263EncoderCatalog *EC,
U8 ** ppu8CurBitStream,
U8 * pu8BitOffset)
{
/* PEI - Extra insertion information */
PutBits( EC->PictureHeader.PEI, FIELDLEN_PEI, ppu8CurBitStream, pu8BitOffset);
} /* end WriteEndPictureHeaderToStream() */
#ifdef CHECKSUM_PICTURE
/*****************************************************************************
*
* WritePictureChecksum
*
* Write the picture checksum to the file.
*
* This function should be able to be called twice. The first time it should
* be called with 0 values after saving the values of the bitstream poointer
* and bitoffset. After completing the picture call it with the actual
* checksum values to update.
*/
static void WritePictureChecksum(
YVUCheckSum * pYVUCheckSum,
U8 ** ppu8CurBitStream,
U8 * pu8BitOffset,
U8 u8ValidData)
{
U32 uBytes;
UN unData;
/* Tag data
*/
unData = (UN) u8ValidData;
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
/* Y date - high to low bytes.
*/
uBytes = pYVUCheckSum->uYCheckSum;
unData = (UN) ((uBytes >> 24) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) ((uBytes >> 16) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) ((uBytes >> 8) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) (uBytes & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
/* V date - high to low bytes.
*/
uBytes = pYVUCheckSum->uVCheckSum;
unData = (UN) ((uBytes >> 24) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) ((uBytes >> 16) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) ((uBytes >> 8) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) (uBytes & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
/* U date - high to low bytes.
*/
uBytes = pYVUCheckSum->uUCheckSum;
unData = (UN) ((uBytes >> 24) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) ((uBytes >> 16) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) ((uBytes >> 8) & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
unData = (UN) (uBytes & 0xFF);
PutBits(1, 1, ppu8CurBitStream, pu8BitOffset);
PutBits(unData, 8, ppu8CurBitStream, pu8BitOffset);
} /* WritePictureChecksum() */
#endif
/*****************************************************************************
*
* WriteGOBHeaderToStream
*
* Write the GOB header to the stream updating the stream pointer and the
* bit offset.
*/
static void WriteGOBHeaderToStream(
U32 uGOBNumber,
UN unGQuant,
U8 ** ppu8CurBitStream,
U8 * pu8BitOffset)
{
/* GOB Start Code */
PutBits(FIELDVAL_GBSC, FIELDLEN_GBSC, ppu8CurBitStream, pu8BitOffset);
/* GOB Number */
PutBits((int)uGOBNumber, FIELDLEN_GN, ppu8CurBitStream, pu8BitOffset);
/* GOB Quant */
PutBits((int)unGQuant, FIELDLEN_GQUANT, ppu8CurBitStream, pu8BitOffset);
/* GEI */
PutBits(0, FIELDLEN_GEI, ppu8CurBitStream, pu8BitOffset);
} /* end WriteGOBHeaderToStream() */
/************************************************************************
*
* CalcGOBChromaVecs
*/
static void CalcGOBChromaVecs(
T_H263EncoderCatalog * EC,
UN unStartingMB,
T_CONFIGURATION *pConfiguration)
{
#ifdef ENCODE_STATS
// #include "ctiming.h"
U32 uStartLow = EC->uStartLow;
U32 uStartHigh = EC->uStartHigh;
U32 uElapsed;
U32 uBefore;
U32 uSLF_UVSum = 0;
int bTimingThisFrame = EC->bTimingThisFrame;
ENC_TIMING_INFO * pEncTimingInfo = NULL;
#endif
register T_MBlockActionStream *pCurrMB;
T_MBlockActionStream *pLastMBPlus1;
char HMV;
char VMV;
int c;
pCurrMB = &(EC->pU8_MBlockActionStream[unStartingMB]);
pLastMBPlus1 = &(EC->pU8_MBlockActionStream[unStartingMB + 33]);
for( c = unStartingMB; pCurrMB < pLastMBPlus1 ; pCurrMB++, c++)
{
if (IsIntraBlock(pCurrMB->BlockType))
continue;
/* Now that are using +/- 15 pel motion search, need to change
valid range of returned MVs. Remember these are in 1/2 pel
increments. Valid range is [-32,31] now.
*/
/*
ASSERT( (pCurrMB->BlkY1.PHMV >= -15) &&
(pCurrMB->BlkY1.PHMV <= 15) )
ASSERT( (pCurrMB->BlkY1.PVMV >= -15) &&
(pCurrMB->BlkY1.PVMV <= 15) )
*/
ASSERT( (pCurrMB->BlkY1.PHMV >= -32) &&
(pCurrMB->BlkY1.PHMV <= 31) )
ASSERT( (pCurrMB->BlkY1.PVMV >= -32) &&
(pCurrMB->BlkY1.PVMV <= 31) )
// RTP: resiliency considerations check
if (pConfiguration->bEncoderResiliency && pConfiguration->unPacketLoss)
{
if (pConfiguration->bDisallowAllVerMVs)
{
ASSERT(pCurrMB->BlkY1.PVMV == 0);
}
else if (pConfiguration->bDisallowPosVerMVs)
{
ASSERT(pCurrMB->BlkY1.PVMV <= 0);
}
}
HMV = QtrPelToHalfPel[pCurrMB->BlkY1.PHMV+32];
VMV = QtrPelToHalfPel[pCurrMB->BlkY1.PVMV+32];
/* Make sure we don't do half pel interpolation in the fdct
*/
HMV = (HMV / 2) * 2;
VMV = (VMV / 2) * 2;
/* Assign the motion vectors for use in the dct
*/
pCurrMB->BlkU.PHMV = HMV;
pCurrMB->BlkU.PVMV = VMV;
pCurrMB->BlkV.PHMV = HMV;
pCurrMB->BlkV.PVMV = VMV;
// TBD: get Brian to put this in ex5me.asm
if (IsSLFBlock(pCurrMB->BlockType))
{
/*
if (pCurrMB->CodedBlocks & 0x2)
ASSERT(pCurrMB->BlkY2.B4_7.PastRef == pCurrMB->BlkY1.B4_7.PastRef + 8);
if (pCurrMB->CodedBlocks & 0x4)
ASSERT(pCurrMB->BlkY3.B4_7.PastRef == pCurrMB->BlkY1.B4_7.PastRef + 8*PITCH);
if (pCurrMB->CodedBlocks & 0x8)
ASSERT(pCurrMB->BlkY4.B4_7.PastRef == pCurrMB->BlkY1.B4_7.PastRef + 8*PITCH+8);
*/
if (pCurrMB->CodedBlocks & 0x2)
pCurrMB->BlkY2.B4_7.PastRef = pCurrMB->BlkY1.B4_7.PastRef + 8;
if (pCurrMB->CodedBlocks & 0x4)
pCurrMB->BlkY3.B4_7.PastRef = pCurrMB->BlkY1.B4_7.PastRef + 8*PITCH;
if (pCurrMB->CodedBlocks & 0x8)
pCurrMB->BlkY4.B4_7.PastRef = pCurrMB->BlkY1.B4_7.PastRef + 8*PITCH+8;
}
/* Motion vectors are in half pels. So we need to divide by 2 to get
* to integer pels.
*/
ASSERT((VMV / 2) == (VMV >> 1)); /* since we divided by 2 and mult above */
ASSERT((HMV / 2) == (HMV >> 1));
VMV >>= 1;
HMV >>= 1;
#ifdef SLF_WORK_AROUND
pCurrMB->BlkU.B4_7.PastRef =
EC->pU8_PrevFrm_YPlane
+ pCurrMB->BlkU.BlkOffset
+ VMV*PITCH + HMV;
pCurrMB->BlkV.B4_7.PastRef =
EC->pU8_PrevFrm_YPlane
+ pCurrMB->BlkV.BlkOffset
+ VMV*PITCH + HMV;
/* Currently U & V are not SLF.
TBD: assemble version of SLF for U & V, ask Brian
*/
if (IsSLFBlock(pCurrMB->BlockType))
{
if (pCurrMB->CodedBlocks & 0x10)
{
#ifdef ENCODE_STATS
TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore);
#endif
EncUVLoopFilter((U8*)pCurrMB->BlkU.B4_7.PastRef,
(U8*)EC->pU8_SLFFrm_YPlane+pCurrMB->BlkU.BlkOffset,PITCH);
pCurrMB->BlkU.B4_7.PastRef =
EC->pU8_SLFFrm_YPlane+pCurrMB->BlkU.BlkOffset;
}
if (pCurrMB->CodedBlocks & 0x20)
{
EncUVLoopFilter((U8*)pCurrMB->BlkV.B4_7.PastRef,
(U8*)EC->pU8_SLFFrm_YPlane+pCurrMB->BlkV.BlkOffset,PITCH);
pCurrMB->BlkV.B4_7.PastRef =
EC->pU8_SLFFrm_YPlane+pCurrMB->BlkV.BlkOffset;
#ifdef ENCODE_STATS
TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uSLF_UVSum)
#endif
}
}
#else
pCurrMB->BlkU.B4_7.PastRef =
EC->pU8_PrevFrm_YPlane
+ pCurrMB->BlkU.BlkOffset
+ VMV*PITCH + HMV;
pCurrMB->BlkV.B4_7.PastRef =
EC->pU8_PrevFrm_YPlane
+ pCurrMB->BlkV.BlkOffset
+ VMV*PITCH + HMV;
#endif
} /* for(pCurrMB ... */
#ifdef ENCODE_STATS
if (bTimingThisFrame)
{
pEncTimingInfo = EC->pEncTimingInfo + EC->uStatFrameCount;
pEncTimingInfo->uSLF_UV += uSLF_UVSum;
}
#endif
} /* end CalcGOBChromaVecs() */
/************************************************************************
*
* GetEncoderOptions
*
* Get the options, saving them in the catalog
*/
static void GetEncoderOptions(
T_H263EncoderCatalog * EC)
{
int bSetOptions = 1;
/* Default Options
*/
const int bDefaultUseMotionEstimation = 1;
const int bDefaultUseSpatialLoopFilter = 1;
const char * szDefaultBRCType = "Normal";
const U32 uDefaultForcedQuant = 8;
const U32 uDefaultForcedDataRate = 1024;
const float fDefaultForcedFrameRate = (float) 8.0; /* should be the same as szDefaultForcedFrameRate */
#ifndef RING0
const char * szDefaultForcedFrameRate = "8.0"; /* should be the same as fDefaultForcedFrameRate */
#endif
/* INI file variables
*/
#ifndef RING0
UN unResult;
DWORD dwResult;
char buf120[120];
float fResult;
#define SECTION_NAME "Encode"
#define INI_FILE_NAME "h261test.ini"
#endif
/* Read the options from the INI file
*/
#ifndef RING0
{
DBOUT("Getting options from the ini file h261test.ini");
/* Motion Estimation
*/
unResult = GetPrivateProfileInt(SECTION_NAME, "MotionEstimation", bDefaultUseMotionEstimation, INI_FILE_NAME);
if (unResult != 0 && unResult != 1)
{
#ifdef _DEBUG
wsprintf(string,"MotionEstimation ini value error (should be 0 or 1) - using default=%d",
(int) bDefaultUseMotionEstimation);
DBOUT(string);
#endif
unResult = bDefaultUseMotionEstimation;
}
EC->bUseMotionEstimation = unResult;
/* Set the custom parameters for motion estimation.
*/
unResult = GetPrivateProfileInt(SECTION_NAME, "MEzerothresh", MECatalog[ME_CUSTOM_CTRLS].zero_vector_threshold, INI_FILE_NAME);
MECatalog[ME_CUSTOM_CTRLS].zero_vector_threshold = unResult;
unResult = GetPrivateProfileInt(SECTION_NAME, "MEnonzerodiff", MECatalog[ME_CUSTOM_CTRLS].nonzero_MV_differential, INI_FILE_NAME);
MECatalog[ME_CUSTOM_CTRLS].nonzero_MV_differential = unResult;
unResult = GetPrivateProfileInt(SECTION_NAME, "MEemptythresh", MECatalog[ME_CUSTOM_CTRLS].empty_threshold, INI_FILE_NAME);
MECatalog[ME_CUSTOM_CTRLS].empty_threshold = unResult;
unResult = GetPrivateProfileInt(SECTION_NAME, "MEinterthresh", MECatalog[ME_CUSTOM_CTRLS].intercoding_threshold, INI_FILE_NAME);
MECatalog[ME_CUSTOM_CTRLS].intercoding_threshold = unResult;
unResult = GetPrivateProfileInt(SECTION_NAME, "MEinterdiff", MECatalog[ME_CUSTOM_CTRLS].intercoding_differential, INI_FILE_NAME);
MECatalog[ME_CUSTOM_CTRLS].intercoding_differential = unResult;
unResult = GetPrivateProfileInt(SECTION_NAME, "MEslfthresh", MECatalog[ME_CUSTOM_CTRLS].slf_threshold, INI_FILE_NAME);
MECatalog[ME_CUSTOM_CTRLS].slf_threshold = unResult;
unResult = GetPrivateProfileInt(SECTION_NAME, "MEslfdiff", MECatalog[ME_CUSTOM_CTRLS].slf_differential, INI_FILE_NAME);
MECatalog[ME_CUSTOM_CTRLS].slf_differential = unResult;
/* Enable or disable the custom parameters for motion estimation.
*/
unResult = GetPrivateProfileInt(SECTION_NAME, "CustomME", 0, INI_FILE_NAME);
EC->bUseCustomMotionEstimation = unResult ? 1 : 0;
/* Spatial Loop Filter
*/
unResult = GetPrivateProfileInt(SECTION_NAME, "SpatialLoopFilter", bDefaultUseSpatialLoopFilter, INI_FILE_NAME);
if (unResult != 0 && unResult != 1)
{
#ifdef _DEBUG
wsprintf(string,"SpatialLoopFilter ini value error (should be 0 or 1) - using default=%d",
(int) bDefaultUseSpatialLoopFilter);
DBOUT(string);
#endif
unResult = bDefaultUseSpatialLoopFilter;
}
EC->bUseSpatialLoopFilter = unResult;
/* Bit Rate Controller Type
*/
strcpy(buf120,"Error");
dwResult = GetPrivateProfileString(SECTION_NAME, "BRCType", szDefaultBRCType, buf120, 120, INI_FILE_NAME);
if ((dwResult == 0) ||
((strcmp(buf120,"Normal") != 0) &&
(strcmp(buf120,"ForcedQuant") != 0) &&
(strcmp(buf120,"ForcedDataRate") != 0)))
{
#ifdef _DEBUG
wsprintf(string,"BRCType ini value error (should be Normal, ForcedQuant, or ForcedDataRate) - using default=%s",
szDefaultBRCType);
DBOUT(string);
#endif
strcpy(buf120,szDefaultBRCType);
}
if (strcmp(buf120,"Normal") == 0)
{
EC->u8BRCType = BRC_Normal;
}
else if (strcmp(buf120,"ForcedQuant") == 0)
{
EC->u8BRCType = BRC_ForcedQuant;
}
else if (strcmp(buf120,"ForcedDataRate") == 0)
{
EC->u8BRCType = BRC_ForcedDataRate;
}
else
{
ASSERT(0);
}
/* ForcedQuant
*/
if (EC->u8BRCType == BRC_ForcedQuant)
{
unResult = GetPrivateProfileInt(SECTION_NAME, "ForcedQuant", uDefaultForcedQuant, INI_FILE_NAME);
if (unResult < 6 || unResult > 31)
{
#ifdef _DEBUG
wsprintf(string, "ForcedQuant ini value error (should be 6 to 31) - using default=%d",
uDefaultForcedQuant);
DBOUT(string);
#endif
unResult = uDefaultForcedQuant;
}
EC->uForcedQuant = unResult;
}
/* ForcedDataRate
*/
if (EC->u8BRCType == BRC_ForcedDataRate)
{
unResult = GetPrivateProfileInt(SECTION_NAME, "ForcedDataRate", uDefaultForcedDataRate, INI_FILE_NAME);
if (unResult < 1)
{
#ifdef _DEBUG
wsprintf(string,"ForcedDataRate ini value error (should be > 0) - using default=%d",
uDefaultForcedDataRate);
DBOUT(string);
#endif
unResult = uDefaultForcedDataRate;
}
EC->uForcedDataRate = unResult;
strcpy(buf120,"0.0");
dwResult = GetPrivateProfileString(SECTION_NAME, "ForcedFrameRate", szDefaultForcedFrameRate, buf120, 120, INI_FILE_NAME);
if (dwResult > 0)
{
fResult = (float) atof(buf120);
}
else
{
fResult = (float) 0.0;
}
if ( fResult <= 0.0 || fResult > 30.0)
{
#ifdef _DEBUG
wsprintf(string, "ForcedFrameRate ini value error (should be > 0.0 and <= 30.0) - using default=%s",
szDefaultForcedFrameRate);
DBOUT(string);
#endif
fResult = fDefaultForcedFrameRate;
}
EC->fForcedFrameRate = fResult;
}
bSetOptions = 0;
}
#endif
if (bSetOptions)
{
EC->bUseMotionEstimation = bDefaultUseMotionEstimation;
EC->bUseSpatialLoopFilter = bDefaultUseSpatialLoopFilter;
EC->u8BRCType = BRC_Normal;
EC->uForcedQuant = uDefaultForcedQuant; /* Used with BRC_ForcedQuant */
EC->uForcedDataRate = uDefaultForcedDataRate; /* Used with BRC_ForcedDataRate */
EC->fForcedFrameRate = fDefaultForcedFrameRate; /* Used with BRC_ForcedDataRate */
}
/* Can only use the SLF if ME is on
*/
if (EC->bUseSpatialLoopFilter && !EC->bUseMotionEstimation)
{
DBOUT("The Spatial Loop Filter can not be on if Motion Estimation is OFF");
EC->bUseSpatialLoopFilter = 0;
}
/* Display the options
*/
if (EC->bUseMotionEstimation)
{
DBOUT("Encoder option (Motion Estimation) is ON");
}
else
{
DBOUT("Encoder option (Motion Estimation) is OFF");
}
if (EC->bUseSpatialLoopFilter)
{
DBOUT("Encoder option (Spatial Loop Filter) is ON");
}
else
{
DBOUT("Encoder option (Spatial Loop Filter) is OFF");
}
#ifdef _DEBUG
if (EC->bUseCustomMotionEstimation)
{
wsprintf(string, "Encoder option (Custom Motion Estimation) %5d %5d %5d %5d %5d %5d %5d",
MECatalog[ME_CUSTOM_CTRLS].zero_vector_threshold,
MECatalog[ME_CUSTOM_CTRLS].nonzero_MV_differential,
MECatalog[ME_CUSTOM_CTRLS].empty_threshold,
MECatalog[ME_CUSTOM_CTRLS].intercoding_threshold,
MECatalog[ME_CUSTOM_CTRLS].intercoding_differential,
MECatalog[ME_CUSTOM_CTRLS].slf_threshold,
MECatalog[ME_CUSTOM_CTRLS].slf_differential
);
DBOUT(string);
}
#endif
#ifdef _DEBUG
switch (EC->u8BRCType)
{
case BRC_Normal:
DBOUT("Encoder option (BRC Type) is Normal");
break;
case BRC_ForcedQuant:
wsprintf(string, "Encoder option (BRC Type) is ForcedQuant with value=%d", EC->uForcedQuant);
DBOUT(string);
break;
case BRC_ForcedDataRate:
wsprintf(string, "Encoder option (BRC Type) is ForcedDataRate with value=%d", EC->uForcedDataRate);
DBOUT(string);
break;
default:
ASSERT(0); /* shouldn't happen */
break;
}
#endif
DBOUT("Encoder option (UsePerfmonNFMO) is OFF");
DBOUT("Encoder option (MMX) is OFF");
} /* end GetEncoderOptions() */
/************************************************************************
*
* StartupBRC
*
* Start up the Bit Rate Controller for this frame
* - set EC->bBitRateControl
* - set BRCState.TargetFrameRate
* - set BRCState.uTargetFrmSize
* - set EC->PictureHeader.PQuant
*/
static void StartupBRC(
T_H263EncoderCatalog * EC,
U32 uVfWDataRate, /* VfW data rate - byte per frame */
U32 uVfWQuality, /* VfW Quality 1..10000 */
float fVfWFrameRate) /* VfW frame rate */
{
FX_ENTRY("StartupBRC");
/* Values used to constrain Quant based on Quality.
*
* When you change these remember to change GetOptions.
*/
const int iLowFixedQuant = 6; // the lowest value without clipping artifacts
const int iHighFixedQuant = 31; // the highest value
I32 iRange;
I32 iValue;
float fValue;
switch (EC->u8BRCType) {
case BRC_Normal:
if (uVfWDataRate == 0)
{
EC->bBitRateControl = 0;
EC->BRCState.TargetFrameRate = (float) 0.0; /* turn it off */
EC->BRCState.uTargetFrmSize = 0; /* should not be used */
/* Compute the fixed quant from the quality
*/
iRange = iHighFixedQuant - iLowFixedQuant;
ASSERT((iRange >= 0) && (iRange <= 30));
iValue = (10000 - (int)uVfWQuality);
ASSERT((iValue >= 0) && (iValue <= 10000));
fValue = (float)iValue * (float)iRange / (float)10000.0;
iValue = (int) (fValue + (float) 0.5);
iValue += iLowFixedQuant;
ASSERT((iValue >= iLowFixedQuant) && (iValue <= iHighFixedQuant));
EC->PictureHeader.PQUANT = (U8) iValue;
DEBUGMSG(ZONE_BITRATE_CONTROL, ("\r\n%s: Bitrate controller disabled, setting EC->PictureHeader.PQUANT = %ld\r\n", _fx_, EC->PictureHeader.PQUANT));
}
else
{
EC->bBitRateControl = 1;
EC->BRCState.TargetFrameRate = fVfWFrameRate;
EC->BRCState.uTargetFrmSize = uVfWDataRate;
DEBUGMSG(ZONE_BITRATE_CONTROL, ("\r\n%s: Bitrate controller enabled with\r\n", _fx_));
DEBUGMSG(ZONE_BITRATE_CONTROL, (" Target frame rate = %ld.%ld fps\r\n Target quality = %ld\r\n Target frame size = %ld bits\r\n Target bitrate = %ld bps\r\n", (DWORD)EC->BRCState.TargetFrameRate, (DWORD)(EC->BRCState.TargetFrameRate - (float)(DWORD)EC->BRCState.TargetFrameRate) * 10UL, uVfWQuality, (DWORD)EC->BRCState.uTargetFrmSize << 3, (DWORD)(EC->BRCState.TargetFrameRate * EC->BRCState.uTargetFrmSize) * 8UL));
DEBUGMSG(ZONE_BITRATE_CONTROL, (" Minimum quantizer = %ld\r\n Maximum quantizer = 31\r\n", clampQP((10000 - uVfWQuality)*15/10000)));
EC->PictureHeader.PQUANT = CalcPQUANT( &(EC->BRCState), EC->PictureHeader.PicCodType);
}
break;
case BRC_ForcedQuant:
EC->bBitRateControl = 0;
EC->BRCState.TargetFrameRate = (float) 0.0; /* turn it off */
EC->BRCState.uTargetFrmSize = 0; /* should not be used */
EC->PictureHeader.PQUANT = (U8) EC->uForcedQuant;
break;
case BRC_ForcedDataRate:
EC->bBitRateControl = 1;
EC->BRCState.TargetFrameRate = EC->fForcedFrameRate;
EC->BRCState.uTargetFrmSize = EC->uForcedDataRate;
DEBUGMSG(ZONE_BITRATE_CONTROL, ("\r\n%s: Bitrate controller enabled with\r\n", _fx_));
DEBUGMSG(ZONE_BITRATE_CONTROL, (" Target frame rate = %ld.%ld fps\r\n Target quality = %ld\r\n Target frame size = %ld bits\r\n Target bitrate = %ld bps\r\n", (DWORD)EC->BRCState.TargetFrameRate, (DWORD)(EC->BRCState.TargetFrameRate - (float)(DWORD)EC->BRCState.TargetFrameRate) * 10UL, uVfWQuality, (DWORD)EC->BRCState.uTargetFrmSize << 3, (DWORD)(EC->BRCState.TargetFrameRate * EC->BRCState.uTargetFrmSize) * 8UL));
DEBUGMSG(ZONE_BITRATE_CONTROL, (" Minimum quantizer = %ld\r\n Maximum quantizer = 31\r\n", clampQP((10000 - uVfWQuality)*15/10000)));
EC->PictureHeader.PQUANT = CalcPQUANT( &(EC->BRCState), EC->PictureHeader.PicCodType);
break;
default:
ASSERT(0); /* should never happen */
break;
}
#ifdef DEBUG_BRC
wsprintf(string,"PQuant=%d", EC->PictureHeader.PQUANT);
DBOUT(string);
#endif
} /* end StartupBRC() */
/************************************************************************
*
* CalculateQP_mean
*
* Calculate the new QP_mean value.
*
* TBD: Consider making this more sophisticated - ie: look at more than
* the last frame or look at the second order affect.
*/
static void CalculateQP_mean(
T_H263EncoderCatalog * EC)
{
/* Calculate average quantizer to be used for next frame.
* The current approach changes QP at the beginning of each row.
*/
/* uQP_count no longer on a row of macroblocks bases, Arlene 6/20/96
if ( EC->PictureHeader.SourceFormat == SF_CIF )
{
ASSERT(EC->uQP_count == 2*EC->NumMBRows);
}
else
{
ASSERT(EC->uQP_count == EC->NumMBRows);
}
*/
/* If this is an an INTRA picture use the inter default as QP_mean
* Otherwise compute QP_mean.
*/
if (EC->PictureHeader.PicCodType == INTRAPIC)
{
EC->BRCState.QP_mean = EC->u8DefINTER_QP;
}
else
{
EC->BRCState.QP_mean = EC->uQP_cumulative / EC->uQP_count;
/* New method, Arlene 6/20/96
EC->BRCState.QP_mean =
(EC->uQP_cumulative + (EC->uQP_count >> 1)) / EC->uQP_count;
*/
}
} /* end CalculateQP_mean() */