/* ************************************************************************* ** 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() */