#define P6Version 0 /* ************************************************************************* ** 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. ** ** ************************************************************************* */ /***************************************************************************** * e3enc.cpp * * DESCRIPTION: * Specific encoder compression functions. * * Routines: Prototypes in: * H263InitEncoderInstance * H263Compress * H263TermEncoderInstance * */ // // $Author: JMCVEIGH $ // $Date: 22 Apr 1997 10:44:58 $ // $Archive: S:\h26x\src\enc\e3enc.cpv $ // $Header: S:\h26x\src\enc\e3enc.cpv 1.185 22 Apr 1997 10:44:58 gmlim $ // $Log: S:\h26x\src\enc\e3enc.cpv $ // // Rev 1.185 22 Apr 1997 10:44:58 gmlim // Change to not return an ICERR_ERROR in H263Compress() when a PB frame // is dropped due to 8k/32k buffer size overflow. ICERR_OK will be // returned and the encoded P frame will be output. // // Rev 1.184 18 Apr 1997 10:45:18 JMCVEIGH // Clean-up of InitMEState when resiliency is turned on. Before, we // would duplicate the number of GOBs forced to be intra if packet // loss was requested. // // Rev 1.183 18 Apr 1997 08:43:22 gmlim // Fixed a bug where uAdjCumFrmSize was not being updated when RTP was // disabled. // // Rev 1.182 17 Apr 1997 17:12:20 gmlim // Added u32sizeBSnEBS to indicate the total buffer size. Changed // u32sizeBitBuffer to indicate the 8k/32k frame size without the // RTP extension and trailer. Added check for buffer overflow before // attaching the EBS and trailer to a PB frame. Also, added // uAdjCumFrmSize to be used with rate control in the IA case. // // Rev 1.181 17 Mar 1997 20:22:06 MDUDA // Adjusted calls to motion estimation to use pseudo stack space. // Moved local storage to encoder catalog from H263Compress. // This fixes were needed to support 16-bit apps that had insufficient // stack space. // // Rev 1.180 12 Mar 1997 16:51:02 CLORD // now check for NULL in H263TermEncoder // // Rev 1.179 11 Mar 1997 13:47:36 JMCVEIGH // Catch AVIIF_KEYFRAME flag for coding as an INTRA frame. Some // apps. use ICCOMPRESS_KEYFRAME, others AVIIF_KEYFRAME. // // Rev 1.178 10 Feb 1997 11:43:26 JMCVEIGH // // Support for new interpretation of blocking filter - // allow for motion vectors outside of the reference picture. // // Rev 1.177 05 Feb 1997 13:07:44 JMCVEIGH // // Further clean-up of improved PB. // // Rev 1.176 05 Feb 1997 12:18:16 JMCVEIGH // Pass GOBHeaderPresent parameter to MMxEDTQ() for EMV bug fix // support latest H.263+ draft bitstream spec, and support for // separate improved PB-frame flag. // // Rev 1.175 20 Jan 1997 17:02:16 JMCVEIGH // // Allow UMV without AP (MMX only). // // Rev 1.174 14 Jan 1997 17:55:04 JMCVEIGH // Allow in-the-loop deblocking filter on IA encoder. // // Rev 1.173 09 Jan 1997 13:49:46 MDUDA // Put emms instruction at end of H263Compress for MMX. // // Rev 1.172 08 Jan 1997 11:37:22 BECHOLS // Changed ini file name to H263Test.ini // // Rev 1.171 30 Dec 1996 19:54:08 MDUDA // Passing input format to encoder initializer so input color convertors // can be initialized. // // Rev 1.170 19 Dec 1996 16:32:52 MDUDA // Modified call to colorCnvtFrame to support H263 backward compatibility. // // Rev 1.169 19 Dec 1996 16:01:38 JMCVEIGH // Fixed turning off of deblocking filter if not MMX. // // Rev 1.168 16 Dec 1996 17:50:00 JMCVEIGH // Support for improved PB-frame mode and 8x8 motion vectors if // deblocking filter selected (no OBMC unless advanced prediction // also selected). // // Rev 1.167 16 Dec 1996 13:34:46 MDUDA // Added support for H263' codec plus some _CODEC_STATS changes. // // Rev 1.166 11 Dec 1996 15:02:06 JMCVEIGH // // Turning on of deblocking filter and true B-frames. Currently // only deblocking filter is implemented. Also, we do not automatically // turn on 8x8 motion vectors when the deblocking filter is selected. // Will use 8x8 vectors when the OBMC part of AP can be selectively // turned off. // // Rev 1.165 09 Dec 1996 17:57:24 JMCVEIGH // Added support for arbitrary frame size support. // 4 <= width <= 352, 4 <= height <= 288, both multiples of 4. // Normally, application will pass identical (arbitrary) frame // sizes in lParam1 and lParam2 of CompressBegin(). If // cropping/stretching desired to convert to standard frame sizes, // application should pass the desired output size in lParam2 and // the input size in lParam1. // // Rev 1.164 09 Dec 1996 09:49:56 MDUDA // // Modified for H263P. // // Rev 1.163 05 Dec 1996 16:49:46 GMLIM // Changed the way RTP packetization was done to guarantee proper packet // size. Modifications made to RTP related function calls in H263Compress(). // // Rev 1.162 03 Dec 1996 08:53:22 GMLIM // Move the check for TR==TRPrev a few lines forward so that it is done // before any write to the bitstream buffer. // // Rev 1.161 03 Dec 1996 08:47:36 KLILLEVO // improved overflow resiliency for PB-frames. Still not perfect, since // that would require re-encoding of parts of the P-frames as well as the // corresponding parts of the B-frames. // // Rev 1.160 27 Nov 1996 16:15:50 gmlim // Modified RTP bitstream bufferring to improve efficiency and also to // avoid internal bitstream buffer overflow. // // Rev 1.159 26 Nov 1996 16:28:30 GMLIM // Added error checking for TR == TRPrev. Merged two sections of identical // code into one block common to both MMX and non-MMX cases. // // Rev 1.157 11 Nov 1996 09:14:26 JMCVEIGH // Fixed bug that caused all blocks in interframes to be intra coded // after the second I frame in a sequence. Now the ME states are // re-initialized when the previous frame was an I frame and the current // frame is a non-intra frame (also reinitialized when the AP state // changes). // // Rev 1.156 06 Nov 1996 16:29:20 gmlim // Removed H263ModeC. // // Rev 1.155 05 Nov 1996 13:33:22 GMLIM // Added mode c support for mmx case. // // Rev 1.154 03 Nov 1996 18:56:46 gmlim // Added mode c support for rtp bs ext. // // Rev 1.153 24 Oct 1996 15:25:54 KLILLEVO // // removed two string allocations no longer needed // // Rev 1.152 24 Oct 1996 15:19:40 KLILLEVO // // changed loglevel for instance events to 2 (from 4) // // Rev 1.151 23 Oct 1996 17:13:36 KLILLEVO // // typo in one DbgLog statement fixed // // Rev 1.150 23 Oct 1996 17:11:36 KLILLEVO // changed to DbgLog() // // Rev 1.149 22 Oct 1996 14:51:10 KLILLEVO // Blocktype initialization in InitMEState() is now only called if // the AP mode has changed from the previous picture. // // Rev 1.148 18 Oct 1996 16:57:00 BNICKERS // Fixes for EMV // // Rev 1.147 10 Oct 1996 16:43:00 BNICKERS // Initial debugging of Extended Motion Vectors. // // Rev 1.146 04 Oct 1996 17:05:22 BECHOLS // When we set the output flags lpdwFlags to AVIIF_KEYFRAME, we also set // dwFlags to ICCOMPRESS_KEYFRAME, to support changes Sylvia Day made to // CXQ_MAIN.CPP // // Rev 1.145 04 Oct 1996 08:47:40 BNICKERS // Add EMV. // // Rev 1.144 16 Sep 1996 16:49:52 CZHU // Changed interface for RTP BS initialization for smaller packet size // // Rev 1.143 13 Sep 1996 12:48:30 KLILLEVO // cleaned up intra update code to make it more understandable // // Rev 1.142 12 Sep 1996 14:46:14 KLILLEVO // finished baseline+PB // // Rev 1.141 12 Sep 1996 14:09:58 KLILLEVO // started baseline+PB changes (not finished) // added PVCS log ;//////////////////////////////////////////////////////////////////////////// #include "precomp.h" #ifdef TRACK_ALLOCATIONS char gsz1[32]; char gsz2[32]; char gsz3[32]; #endif #define DUMPFILE 0 /* QP level for which the AP mode is turned off for IA */ /* on MMX AP is always used if the caller asks for it */ const int AP_MODE_QP_LEVEL = 11; /* Pick a resiliency strategy. */ #define REQUESTED_KEY_FRAME 0 #define PERIODIC_KEY_FRAME 1 #define FAST_RECOVERY 2 #define SLOW_RECOVERY 3 #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. #define REUSE_DECODE 1 // Set to one if second decode (as under Videdit) // can reuse the encoder's decode. /* * Need this hack to allow temporarily turning off PB frames * when they are turned on usin the INI file. */ #define TEMPORARILY_FALSE 88 #ifdef STAT #define STATADDRESS 0x250 #define ELAPSED_ENCODER_TIME 1 // Must be set for other timers to work right. #define SAMPLE_RGBCONV_TIME 0 // Time conversion of RGB24 to YUV9 step. #define SAMPLE_MOTION_TIME 0 // Time motion estimation step. #define SAMPLE_ENCBLK_TIME 0 // Time encode block layer step. #define SAMPLE_ENCMBLK_TIME 0 // Time encode macroblock layer step. #define SAMPLE_ENCVLC_TIME 0 // Time encode VLC step. #define SAMPLE_COMPAND_TIME 1 // Time decode of encoded block step. #else #define STATADDRESS 0x250 #define ELAPSED_ENCODER_TIME 0 // Must be set for other timers to work right. #define SAMPLE_RGBCONV_TIME 0 // Time conversion of RGB24 to YUV9 step. #define SAMPLE_MOTION_TIME 0 // Time motion estimation step. #define SAMPLE_ENCBLK_TIME 0 // Time encode block layer step. #define SAMPLE_ENCMBLK_TIME 0 // Time encode macroblock layer step. #define SAMPLE_ENCVLC_TIME 0 // Time encode VLC step. #define SAMPLE_COMPAND_TIME 0 // Time decode of encoded block step. #endif //#pragma warning(disable:4101) //#pragma warning(disable:4102) #if ELAPSED_ENCODER_TIME // #include "statx.h" --- commented out to allow updating dependencies DWORD Elapsed, Sample; DWORD TotalElapsed, TotalSample, TimedIterations; #endif //#define PITCH 384 #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 #ifdef USE_MMX // { USE_MMX extern BOOL MMxVersion; // from ccpuvsn.cpp BOOL MMX_Enabled = MMxVersion; #endif // } USE_MMX BOOL ToggleAP = TRUE; BOOL TogglePB = TRUE; U8 u8QPMax; #ifdef REUSE_DECODE extern struct { // Communicate Encoder's decode to display decode. U8 FAR * Address; // Addr at which encoded frame is placed. DECINSTINFO BIGG * PDecoderInstInfo; // Encoder's decoder instance. unsigned int FrameNumber; // Frame number last encoded, mod 128. } CompandedFrame; #endif #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) // { #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) #pragma message ("Current log encode timing computations handle 105 frames max") void OutputEncodeTimingStatistics(char * szFileName, ENC_TIMING_INFO * pEncTimingInfo); void OutputEncTimingDetail(FILE * pFile, ENC_TIMING_INFO * pEncTimingInfo); #endif // } #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) /* * Look up table for quarter pel to half pel conversion of chroma MV's. * The motion vectors value is half the index value. The input to the * array must be biased by +64. */ const char QtrPelToHalfPel[] = { -32, -31, -31, -31, -30, -29, -29, -29, -28, -27, -27, -27, -26, -25, -25, -25, -24, -23, -23, -23, -22, -21, -21, -21, -20, -19, -19, -19, -18, -17, -17, -17, -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, 16, 17, 17, 17, 18, 19, 19, 19, 20, 21, 21, 21, 22, 23, 23, 23, 24, 25, 25, 25, 26, 27, 27, 27, 28, 29, 29, 29, 30, 31, 31, 31}; /* * Look-up table for converting the sum of four motion vectors to a chroma * motion vector. Since motion vectors are in the range [-32,31.5], their * indices are in the range [-64,63]. Hence the sum are in the range [-256,248]. * The input to the array must be biased by +256. */ const char SixteenthPelToHalfPel[] = { -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -28, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -26, -26, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -24, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -22, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -20, -20, -20, -20, -20, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -18, -18, -18, -18, -18, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32}; void InitMEState(T_H263EncoderCatalog *EC, ICCOMPRESS *lpicComp, T_CONFIGURATION * pConfiguration); UN FindNewQuant( T_H263EncoderCatalog *EC, UN gquant_prev, UN uComFrmSize, UN GOB, U8 u8QPMax, U8 u8QPMin, BOOL bBitRateControl, BOOL bGOBoverflowWarning ); static void encodeFrameHeader( T_H263EncoderCatalog * EC, U8 ** ppCurBitStream, U8 * u8BitOffset, BOOL bPBframe ); /* static void copyEdgePels(T_H263EncoderCatalog * EC); */ extern "C" { void ExpandPlane(U32, U32, U32, U32); } #ifdef USE_MMX // { USE_MMX static void Check_InterCodeCnt_MMX(T_H263EncoderCatalog *, U32); #endif // } USE_MMX static void Check_InterCodeCnt (T_H263EncoderCatalog *, U32); static void calcGOBChromaVectors( T_H263EncoderCatalog *EC, U32 StartingMB, T_CONFIGURATION *pConfiguration ); static void calcBGOBChromaVectors( T_H263EncoderCatalog *EC, const U32 StartingMB ); static void GetEncoderOptions(T_H263EncoderCatalog * EC); /*static U8 StillImageQnt[] = { 31, 29, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3}; */ static U8 StillImageQnt[] = { 31, 18, 12, 10, 8, 6, 5, 4, 4, 3}; //ia #ifdef USE_MMX // { USE_MMX static U8 StillImageQnt_MMX[] = { 31, 12, 10, 8, 6, 4, 3, 3, 3, 2}; //mmx #endif // } USE_MMX const int numStillImageQnts = 10; #ifdef COUNT_BITS static void InitBits(T_H263EncoderCatalog * EC); void InitCountBitFile(); void WriteCountBitFile(T_BitCounts *Bits); #endif #ifdef USE_MMX // { USE_MMX /* * Exception Filter for access violations in MMxEDTQ B-frame motion estimation * No memory is allocation before run-time, it is only reserved. * Then, when an access violation occurs, more memory is allocated, * provided the access violation is withing the reserved memory area. */ int ExceptionFilterForMMxEDTQ( LPEXCEPTION_POINTERS exc, LPVOID lpMBRVS, BOOL fLuma) { DWORD dwCode; LPVOID lpAddress; FX_ENTRY("ExceptionFilterForMMxEDTQ") dwCode = exc->ExceptionRecord->ExceptionCode; // check that this is an access violation if (dwCode != EXCEPTION_ACCESS_VIOLATION) return EXCEPTION_CONTINUE_SEARCH; lpAddress = (LPVOID)exc->ExceptionRecord->ExceptionInformation[1]; // check for access violation outside address range if (lpAddress < lpMBRVS) return EXCEPTION_CONTINUE_SEARCH; // this exception is not handled here if (fLuma) { if ((DWORD)lpAddress > ((DWORD)lpMBRVS + 18*65*22*3*4)) return EXCEPTION_CONTINUE_SEARCH; // this exception is not handled here } else { if ((DWORD)lpAddress > ((DWORD)lpMBRVS + 18*65*22*3*2)) return EXCEPTION_CONTINUE_SEARCH; // this exception is not handled here } DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Access Violation. Don't worry - be happy - committing another page\r\n", _fx_)); // commit another page if (VirtualAlloc(lpAddress,4095,MEM_COMMIT,PAGE_READWRITE) == NULL) { return EXCEPTION_CONTINUE_SEARCH; // could not commit // this should never happen, since RESERVE was successfull } // return and try instruction causing the access violation again return EXCEPTION_CONTINUE_EXECUTION; } #endif // } USE_MMX /******************************************************************************* H263InitEncoderGlobal -- This function initializes the global tables used by the H263 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. *******************************************************************************/ LRESULT H263InitEncoderGlobal(void) { // Initialize fixed length tables for INTRADC InitVLC(); return ICERR_OK; } /******************************************************************************* H263InitEncoderInstance -- This function allocates and initializes the per-instance tables used by the H263 encoder. *******************************************************************************/ #if defined(H263P) || defined(USE_BILINEAR_MSH26X) LRESULT H263InitEncoderInstance(LPBITMAPINFOHEADER lpbiInput, LPCODINST lpCompInst) #else LRESULT H263InitEncoderInstance(LPCODINST lpCompInst) #endif { LRESULT ret; UN i; U32 Sz; T_H263EncoderInstanceMemory * P32Inst; T_H263EncoderCatalog * EC; #if ELAPSED_ENCODER_TIME TotalElapsed = 0; TotalSample = 0; TimedIterations = 0; #endif T_CONFIGURATION * pConfiguration; UN uIntraQP; UN uInterQP; FX_ENTRY("H263InitEncoderInstance") /* * Allocate memory if instance is not initialized. * TO ADD: If instance IS intialized, we have to check to see * if important parameters have changed, such as frame size, and * then reallocate memory if necessary. */ if(lpCompInst->Initialized == FALSE) { /* * Calculate size of encoder instance memory needed. We add the size * of a MacroBlock Action Descriptor to it since we want the MacroBlock * Action Stream (which is the first element of the memory structure) * to be aligned to a boundary equal to the size of a descriptor. */ Sz = sizeof(T_H263EncoderInstanceMemory) + sizeof(T_MBlockActionStream); /* * Allocate the memory. */ // lpCompInst->hEncoderInst = GlobalAlloc(GHND, Sz); // VirtualAlloc automatically zeros memory. The bitstream // needs to be zeroed when I change this to HeapAlloc. lpCompInst->hEncoderInst = VirtualAlloc( NULL, // can be allocated anywhere Sz, // number of bytes to allocate MEM_RESERVE | MEM_COMMIT, // reserve & commit memory PAGE_READWRITE); // protection #ifdef TRACK_ALLOCATIONS // Track memory allocation wsprintf(gsz1, "E3ENC: (VM) %7ld Ln %5ld\0", Sz, __LINE__); AddName((unsigned int)lpCompInst->hEncoderInst, gsz1); #endif /* Indicate that we have allocated memory for the compressor instance. */ lpCompInst->Initialized = TRUE; } /* else { // check if parameters have changed, thay may make us have // to reallocate memory. } */ // lpCompInst->EncoderInst = (LPVOID)GlobalLock(lpCompInst->hEncoderInst); lpCompInst->EncoderInst = lpCompInst->hEncoderInst; if (lpCompInst->hEncoderInst == NULL) { ret = ICERR_MEMORY; goto done; } /* * Calculate the 32 bit instance pointer starting at required 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); #ifdef COUNT_BITS InitCountBitFile(); #endif #ifdef ENCODE_STATS InitQuantStats(); InitFrameSizeStats(); InitPSNRStats(); #endif /* ENCODE_STATS */ /* Initialize the Configuration information */ pConfiguration = &(lpCompInst->Configuration); #if 0 if (LoadConfiguration(pConfiguration) == FALSE) GetConfigurationDefaults(pConfiguration); #endif pConfiguration->bInitialized = TRUE; pConfiguration->bCompressBegin = TRUE; EC->hBsInfoStream= NULL; #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) // { #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) // We really want those timings to match the actual use we // will make of the codec. So initialize it with the same values pConfiguration->bRTPHeader = TRUE; pConfiguration->unPacketSize = 512; #endif // } #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) DEBUGMSG(ZONE_INIT, ("%s: Encoder Configuration Options: bRTPHeader=%d, unPacketSize=%d, bEncoderResiliency=%d, bDisallowPosVerMVs=%d\r\n", _fx_, (int)pConfiguration->bRTPHeader, (int)pConfiguration->unPacketSize, (int)pConfiguration->bEncoderResiliency, (int)pConfiguration->bDisallowPosVerMVs)); DEBUGMSG(ZONE_INIT, ("%s: Encoder Configuration Options: bDisallowAllVerMVs=%d, unPercentForcedUpdate=%d, unDefaultIntraQuant=%d, unDefaultInterQuant=%d\r\n", _fx_, (int)pConfiguration->bDisallowAllVerMVs, (int)pConfiguration->unPercentForcedUpdate, (int)pConfiguration->unDefaultIntraQuant, (int)pConfiguration->unDefaultInterQuant)); /* * Initialize encoder catalog. */ #if defined(H263P) || defined(USE_BILINEAR_MSH26X) // In H.263+, we encode and decode the padded frames (padded to the right // and bottom to multiples of 16). The actual frame dimensions are used // for display purposes only. EC->FrameHeight = (lpCompInst->yres + 0xf) & ~0xf; EC->FrameWidth = (lpCompInst->xres + 0xf) & ~0xf; EC->uActualFrameHeight = lpCompInst->yres; EC->uActualFrameWidth = lpCompInst->xres; ASSERT(sizeof(T_H263EncoderCatalog) == sizeof_T_H263EncoderCatalog); { int found_cc = TRUE; if (BI_RGB == lpCompInst->InputCompression) { #ifdef USE_BILINEAR_MSH26X if (24 == lpCompInst->InputBitWidth) { EC->ColorConvertor = RGB24toYUV12; #else if (32 == lpCompInst->InputBitWidth) { EC->ColorConvertor = RGB32toYUV12; } else if (24 == lpCompInst->InputBitWidth) { EC->ColorConvertor = RGB24toYUV12; #endif } else if (16 == lpCompInst->InputBitWidth) { EC->ColorConvertor = RGB16555toYUV12; } else if (8 == lpCompInst->InputBitWidth) { EC->ColorConvertor = CLUT8toYUV12; } else if (4 == lpCompInst->InputBitWidth) { EC->ColorConvertor = CLUT4toYUV12; } else { found_cc = FALSE; ERRORMESSAGE(("%s: Unexpected input format detected\r\n", _fx_)); } } else if (FOURCC_YVU9 == lpCompInst->InputCompression) { EC->ColorConvertor = YVU9toYUV12; } else if (FOURCC_YUY2 == lpCompInst->InputCompression) { EC->ColorConvertor = YUY2toYUV12; } else if (FOURCC_UYVY == lpCompInst->InputCompression) { EC->ColorConvertor = UYVYtoYUV12; } else if ((FOURCC_YUV12 == lpCompInst->InputCompression) || (FOURCC_IYUV == lpCompInst->InputCompression)) { EC->ColorConvertor = YUV12toEncYUV12; } else { found_cc = FALSE; ERRORMESSAGE(("%s: Unexpected input format detected\r\n", _fx_)); } if (found_cc) { colorCnvtInitialize(lpbiInput, EC->ColorConvertor); } } #else EC->FrameHeight = lpCompInst->yres; EC->FrameWidth = lpCompInst->xres; #endif EC->FrameSz = lpCompInst->FrameSz; EC->NumMBRows = EC->FrameHeight >> 4; EC->NumMBPerRow = EC->FrameWidth >> 4; EC->NumMBs = EC->NumMBRows * EC->NumMBPerRow; // This should default to zero. If RTP is used, it will be changed later EC->uNumberForcedIntraMBs = 0; #ifdef H263P EC->uNextIntraMB = 0; #else if(pConfiguration->bEncoderResiliency && pConfiguration->unPercentForcedUpdate && pConfiguration->unPacketLoss) {//Chad Intra GOB // EC->uNumberForcedIntraMBs = ((EC->NumMBs * pConfiguration->unPercentForcedUpdate) + 50) / 100; EC->uNextIntraMB = 0; } #endif // 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 future frame in the catalog. EC->pU8_FutrFrm = P32Inst->u8FuturePlane; EC->pU8_FutrFrm_YPlane = EC->pU8_FutrFrm + 16*PITCH + 16; EC->pU8_FutrFrm_UPlane = EC->pU8_FutrFrm_YPlane + YU_OFFSET; EC->pU8_FutrFrm_VPlane = EC->pU8_FutrFrm_UPlane + UV_OFFSET; // Store pointers to the B frame in the catalog. EC->pU8_BidiFrm = P32Inst->u8BPlane; EC->pU8_BFrm_YPlane = EC->pU8_BidiFrm + 16; EC->pU8_BFrm_UPlane = EC->pU8_BFrm_YPlane + YU_OFFSET; EC->pU8_BFrm_VPlane = EC->pU8_BFrm_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 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 area in which to pre-compute OBMC predictions. EC->pU8_PredictionScratchArea = P32Inst->u8PredictionScratchArea; // Store pointer to the bit stream buffer in the catalog. EC->pU8_BitStream = P32Inst->u8BitStream; EC->pU8_BitStrCopy = P32Inst->u8BitStrCopy; // Store pointer to the RunValSign triplets for Luma and Chroma EC->pI8_MBRVS_Luma = P32Inst->i8MBRVS_Luma; EC->pI8_MBRVS_Chroma = P32Inst->i8MBRVS_Chroma; // Reserve virtual memory EC->pI8_MBRVS_BLuma = (I8 *) VirtualAlloc( NULL, // anywhere 18*(65*3*22*4), // number of bytes MEM_RESERVE, // reserve PAGE_READWRITE); // access #ifdef TRACK_ALLOCATIONS // Track memory allocation wsprintf(gsz2, "E3ENC: (VM) %7ld Ln %5ld\0", 18*(65*3*22*4), __LINE__); AddName((unsigned int)EC->pI8_MBRVS_BLuma, gsz2); #endif EC->pI8_MBRVS_BChroma = (I8 *) VirtualAlloc( NULL, // anywhere 18*(65*3*22*2), // number of bytes MEM_RESERVE, // reserve PAGE_READWRITE); // access #ifdef TRACK_ALLOCATIONS // Track memory allocation wsprintf(gsz3, "E3ENC: (VM) %7ld Ln %5ld\0", 18*(65*3*22*2), __LINE__); AddName((unsigned int)EC->pI8_MBRVS_BChroma, gsz3); #endif if (EC->pI8_MBRVS_BLuma == NULL || EC->pI8_MBRVS_BChroma == NULL) { ret = ICERR_MEMORY; goto done; } // Store pointer to private copy of decoder instance info. EC->pDecInstanceInfo = &(P32Inst->DecInstanceInfo); /* * Check to see if there is an H263test.ini file. If the UseINI key * is not 1, or the INI file is not found, then we allow option * signalling in the ICCOMPRESS structure. If set, the INI * options override the ICCOMPRESS options. */ GetEncoderOptions(EC); EC->u8SavedBFrame = FALSE; // Fill the picture header structure. EC->PictureHeader.TR = 0; EC->PictureHeader.Split = OFF; EC->PictureHeader.DocCamera = OFF; EC->PictureHeader.PicFreeze = OFF; EC->PictureHeader.PB = OFF; // Leave this off here. It is turned on after the P frame // has been encoded, when the PB frame is written. EC->prevAP = 255; EC->prevUMV = 255; #ifdef H263P EC->prevDF = 255; #endif EC->PictureHeader.CPM = 0; EC->PictureHeader.TRB = 0; EC->PictureHeader.DBQUANT = 1; EC->PictureHeader.PLCI = 0; EC->PictureHeader.PEI = 0; #ifdef LOG_ENCODE_TIMINGS_ON // { LOG_ENCODE_TIMINGS_ON EC->pEncTimingInfo = P32Inst->EncTimingInfo; #endif // } LOG_ENCODE_TIMINGS_ON /* * This flag is used by the encoder to signal that the * next frame should be encoded as an INTRA regardless of what * the client asks for. This may be either because an error was * detected in compressing the current delta, or to ensure that * the first frame is encoded INTRA. */ EC->bMakeNextFrameKey = TRUE; // Ensure that we always start with a key frame. /* * Initialize table with Bit Usage Profile */ for (i = 0; i <= EC->NumMBRows ; i++) EC->uBitUsageProfile[i] = i; // 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 ) ASSERT( ((sizeof_T_MBlockActionStream-1) & sizeof_T_MBlockActionStream) == 0); // Size is power of two ASSERT( sizeof(T_H263EncoderCatalog) == sizeof_T_H263EncoderCatalog ) // Encoder instance memory should start on a 32 byte boundary. ASSERT( ( (unsigned int)P32Inst & 0x1f) == 0) // MB Action Stream should be on boundary equal to size of a descriptor. ASSERT((((int)EC->pU8_MBlockActionStream) & (sizeof_T_MBlockActionStream-1)) == 0); // Allocated at right boundary. // Block structure array should be on a 16 byte boundary. ASSERT( ( (unsigned int) &(EC->pU8_MBlockActionStream->BlkY1) & 0xf) == 0) // DCT coefficient array should be on a 32 byte boundary. ASSERT( ( (unsigned int)EC->pU8_DCTCoefBuf & 0x1f) == 0) // Current Frame Buffers should be on 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) ASSERT( ( (unsigned int)EC->pU8_BFrm_YPlane & 0x1f) == 0x10) ASSERT( ( (unsigned int)EC->pU8_BFrm_UPlane & 0x1f) == 0x10) ASSERT( ( (unsigned int)EC->pU8_BFrm_VPlane & 0x1f) == 0x10) // Previous Frame Buffers should be on 32 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) ASSERT( ( (unsigned int)EC->pU8_FutrFrm_YPlane & 0x1f) == 0x10) ASSERT( ( (unsigned int)EC->pU8_FutrFrm_UPlane & 0x1f) == 0x10) ASSERT( ( (unsigned int)EC->pU8_FutrFrm_VPlane & 0x1f) == 0x10) // Decoder instance structure should be on a DWORD boundary. ASSERT( ( (unsigned int)EC->pDecInstanceInfo & 0x3 ) == 0 ) /* * Initialize MBActionStream */ int YBlockOffset, UBlockOffset; YBlockOffset = 0; UBlockOffset = EC->pU8_CurrFrm_UPlane - EC->pU8_CurrFrm_YPlane; for(i = 0; i < EC->NumMBs; i++) { // Clear the counter of the number of consecutive times a // macroblock has been inter coded. (EC->pU8_MBlockActionStream[i]).InterCodeCnt = (i & 0xf); // Store offsets to each block in the MB from the beginning of // the Y plane. (EC->pU8_MBlockActionStream[i]).BlkY1.BlkOffset = YBlockOffset; (EC->pU8_MBlockActionStream[i]).BlkY2.BlkOffset = YBlockOffset+8; (EC->pU8_MBlockActionStream[i]).BlkY3.BlkOffset = YBlockOffset+PITCH*8; (EC->pU8_MBlockActionStream[i]).BlkY4.BlkOffset = YBlockOffset+PITCH*8+8; (EC->pU8_MBlockActionStream[i]).BlkU.BlkOffset = UBlockOffset; (EC->pU8_MBlockActionStream[i]).BlkV.BlkOffset = UBlockOffset+UV_OFFSET; YBlockOffset += 16; UBlockOffset += 8; (EC->pU8_MBlockActionStream[i]).MBEdgeType = 0xF; if ((i % EC->NumMBPerRow) == 0) { (EC->pU8_MBlockActionStream[i]).MBEdgeType &= MBEdgeTypeIsLeftEdge; } if (((i+1) % EC->NumMBPerRow) == 0) { (EC->pU8_MBlockActionStream[i]).MBEdgeType &= MBEdgeTypeIsRightEdge; // Set bit six of CodedBlocks to indicate this is the last // MB of the row. (EC->pU8_MBlockActionStream[i]).CodedBlocks |= 0x40; YBlockOffset += PITCH*16 - EC->NumMBPerRow*16; UBlockOffset += PITCH*8 - EC->NumMBPerRow*8; } if (i < EC->NumMBPerRow) { (EC->pU8_MBlockActionStream[i]).MBEdgeType &= MBEdgeTypeIsTopEdge; } if ((i + EC->NumMBPerRow) >= EC->NumMBs) { (EC->pU8_MBlockActionStream[i]).MBEdgeType &= MBEdgeTypeIsBottomEdge; } } // end of for loop. /* * Initialize previous frame pointers. For now we can do this from here. */ /* YBlockAddress = EC->pU8_PrevFrm_YPlane; UBlockAddress = EC->pU8_PrevFrm_UPlane; for(i = 0; i < EC->NumMBs; i++) { (EC->pU8_MBlockActionStream[i]).Blk[0].PastRef = YBlockAddress; (EC->pU8_MBlockActionStream[i]).Blk[1].PastRef = YBlockAddress+8; (EC->pU8_MBlockActionStream[i]).Blk[2].PastRef = YBlockAddress+PITCH*8; (EC->pU8_MBlockActionStream[i]).Blk[3].PastRef = YBlockAddress+PITCH*8+8; (EC->pU8_MBlockActionStream[i]).Blk[4].PastRef = UBlockAddress; (EC->pU8_MBlockActionStream[i]).Blk[5].PastRef = UBlockAddress+UV_OFFSET; // Zero all motion vectors. (EC->pU8_MBlockActionStream[i]).Blk[0].PastHMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[0].PastVMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[1].PastHMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[1].PastVMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[2].PastHMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[2].PastVMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[3].PastHMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[3].PastVMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[4].PastHMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[4].PastVMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[5].PastHMV = 0; (EC->pU8_MBlockActionStream[i]).Blk[5].PastVMV = 0; YBlockAddress += 16; UBlockAddress += 8; if( (i != 0) && (( (i+1) % EC->NumMBPerRow ) == 0) ) { YBlockAddress += PITCH*16 - EC->NumMBPerRow*16; UBlockAddress += PITCH*8 - EC->NumMBPerRow*8; } } // end of for loop. */ /* * Initialize bit rate controller. */ if(pConfiguration->bEncoderResiliency && pConfiguration->unPacketLoss) { uIntraQP = pConfiguration->unDefaultIntraQuant; uInterQP = pConfiguration->unDefaultInterQuant; } else { uIntraQP = def263INTRA_QP; uInterQP = def263INTER_QP; } InitBRC(&(EC->BRCState), uIntraQP, uInterQP, EC->NumMBs); if (pConfiguration->bRTPHeader) H263RTP_InitBsInfoStream(lpCompInst,EC); /* * Create a decoder instance and initialize it. DecoderInstInfo must be in first 64K. */ EC->pDecInstanceInfo->xres = lpCompInst->xres; EC->pDecInstanceInfo->yres = lpCompInst->yres; ret = H263InitDecoderInstance(EC->pDecInstanceInfo, H263_CODEC); if (ret != ICERR_OK) goto done1; ret = H263InitColorConvertor(EC->pDecInstanceInfo, YUV12ForEnc); if (ret != ICERR_OK) goto done1; /* * Clear initialized memory. */ // to be added. lpCompInst->Initialized = TRUE; ret = ICERR_OK; #if defined(H263P) // Set the pseudo stack space pointer (to be used for motion estimation and // whatever else needs extra stack space). EC->pPseudoStackSpace = ((T_H263EncoderInstanceMemory *)(lpCompInst->EncoderInst))->u8PseudoStackSpace + (SIZEOF_PSEUDOSTACKSPACE - sizeof(DWORD)); #endif done1: //GlobalUnlock(lpCompInst->hEncoderInst); done: return ret; } /******************************************************************************* * * H263Compress * This function drives the compression of one frame * Note: * The timing statistics code produces incorrect no. after PB-frame changes * were made. *******************************************************************************/ LRESULT H263Compress( #ifdef USE_BILINEAR_MSH26X LPINST pi, #else LPCODINST lpCompInst, // ptr to compressor instance info. #endif ICCOMPRESS *lpicComp // ptr to ICCOMPRESS structure. ) { FX_ENTRY("H263Compress"); #ifdef USE_BILINEAR_MSH26X LPCODINST lpCompInst = (LPCODINST)pi->CompPtr; // ptr to compressor instance info. #endif // Start PB-frame data #if !defined(H263P) T_FutrPMBData FutrPMBData[GOBs_IN_CIF*MBs_PER_GOB_CIF + 1]; I8 WeightForwMotion[128]; // values based on TRb and TRd I8 WeightBackMotion[128]; // values based on TRb and TRd #endif U8 FutrFrmGQUANT[GOBs_IN_CIF]; // End PB-frame LRESULT ret; UN GOB, SizeBitStream; UN SizeBSnEBS; #ifdef DEBUG UN i; #endif U8 *pCurBitStream; // pointer to the current location in the bitstream. U8 u8bitoffset; // bit offset in the current byte of the bitstream. U32 uCumFrmSize = 0, GOBHeaderMask; U32 uAdjCumFrmSize = 0; T_H263EncoderInstanceMemory *P32Inst; T_H263EncoderCatalog *EC; T_MBlockActionStream *MBlockActionPtr; BOOL bGOBoverflowWarning = FALSE; //RH U32 u32tempBuf; //RH U32 u32sizeBitBuffer; //RH U32 u32sizeBSnEBS; LPVOID EncoderInst; ICDECOMPRESSEX ICDecExSt; ICDECOMPRESSEX DefaultICDecExSt = { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned int gquant, gquant_prev; U32 QP_cumulative; U32 IntraSWDTotal, IntraSWDBlocks, InterSWDTotal, InterSWDBlocks; int StartingMB; EnumOnOff bBitRateControl; T_CONFIGURATION * pConfiguration = &(lpCompInst->Configuration); #ifdef ENCODE_STATS U32 uBitStreamBytes; #endif /* ENCODE_STATS */ U32 iSumSWD = 0, iSumBSWD = 0; U32 iSWD = 0, iBSWD = 0; U8 u8QPMin; // PB-frame variables I32 TRb; I32 TRd; I32 j; U8 *pP_BitStreamStart; U8 *pPB_BitStream; U8 u8PB_BitOffset; U8 *temp; BOOL bEncodePBFrame; BOOL bPBFailed; U32 u32BFrmZeroThreshold; //Chad, intra gob int uUsedByIntra=0; DWORD dwRTPSize=0; #if ELAPSED_ENCODER_TIME SetStatAdd (STATADDRESS); InitStat (); ConfigElapsed (); ConfigSample (); StartElapsed (); #endif #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) // { #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) U32 uStartLow; U32 uStartHigh; U32 uElapsed; U32 uBefore; U32 uEncodeTime = 0; int bTimingThisFrame = 0; #endif // } #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON U32 uInputCC = 0; U32 uMotionEstimation = 0; U32 uFDCT = 0; U32 uQRLE = 0; U32 uDecodeFrame = 0; U32 uZeroingBuffer = 0; #endif // } DETAILED_ENCODE_TIMINGS_ON #ifdef LOG_ENCODE_TIMINGS_ON // { LOG_ENCODE_TIMINGS_ON ENC_TIMING_INFO * pEncTimingInfo = NULL; #endif // } LOG_ENCODE_TIMINGS_ON #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) // { #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) TIMER_START(bTimingThisFrame,uStartLow,uStartHigh); #endif // } #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) #ifdef REUSE_DECODE CompandedFrame.Address = NULL; CompandedFrame.PDecoderInstInfo = NULL; CompandedFrame.FrameNumber = 0xFFFF; #endif ret = ICERR_OK; // check instance pointer if (!lpCompInst) return ICERR_ERROR; /* * Lock the instance data private to the encoder. */ // EncoderInst = (LPVOID)GlobalLock(lpCompInst->hEncoderInst); EncoderInst = lpCompInst->hEncoderInst; if (EncoderInst == NULL) { ERRORMESSAGE(("%s: ICERR_MEMORY\r\n", _fx_)); ret = ICERR_MEMORY; goto done; } /* * Generate the pointer to the encoder instance memory aligned to the * required boundary. */ P32Inst = (T_H263EncoderInstanceMemory *) ((((U32) 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; #ifdef LOG_ENCODE_TIMINGS_ON // { LOG_ENCODE_TIMINGS_ON if (EC->uStatFrameCount < ENC_TIMING_INFO_FRAME_COUNT) { EC->uStartLow = uStartLow; EC->uStartHigh = uStartHigh; } EC->bTimingThisFrame = bTimingThisFrame; #endif // } LOG_ENCODE_TIMINGS_ON #ifdef FORCE_ADVANCED_OPTIONS_ON // { FORCE_ADVANCED_OPTIONS_ON // Force PB-Frame for testing lpicComp->dwFlags |= CODEC_CUSTOM_PB; // Force UMV for testing lpicComp->dwFlags |= CODEC_CUSTOM_UMV; // Force AP for testing lpicComp->dwFlags |= CODEC_CUSTOM_AP; // Force SAC for testing EC->PictureHeader.SAC = ON; if (!(lpicComp->dwFlags & ICCOMPRESS_KEYFRAME)) { lpicComp->lFrameNum *= 5; } #endif // } FORCE_ADVANCED_OPTIONS_ON /*************************************************************************** * Do per-frame initialization. **************************************************************************/ if ((lpicComp->dwFlags & ICCOMPRESS_KEYFRAME) || (*(lpicComp->lpdwFlags) & AVIIF_KEYFRAME) || (EC->bMakeNextFrameKey == TRUE)) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Coding an Intra Frame\r\n", _fx_)); EC->PictureHeader.PicCodType = INTRAPIC; EC->bMakeNextFrameKey = FALSE; EC->u8SavedBFrame = FALSE; } else EC->PictureHeader.PicCodType = INTERPIC; /* * Check for H.263 options. This is the location that * you can manually enable the options if you want. */ if (!EC->bUseINISettings) { // Check to see if PB frames is requested. // For our particular implementation, unrestricted motion vectors // are used when PB is on. // if (lpicComp->dwFlags & CODEC_CUSTOM_PB) EC->u8EncodePBFrame = TRUE; else { EC->u8EncodePBFrame = FALSE; } // Check to see if advanced prediction is requested. if (lpicComp->dwFlags & CODEC_CUSTOM_AP) EC->PictureHeader.AP = ON; else EC->PictureHeader.AP = OFF; // Check to see if advanced prediction is requested. if (lpicComp->dwFlags & CODEC_CUSTOM_UMV) EC->PictureHeader.UMV = ON; else EC->PictureHeader.UMV = OFF; #ifdef H263P if (pConfiguration->bH263PlusState) { // Check to see if in-the-loop deblocking filter is requested. if (pConfiguration->bDeblockingFilterState) EC->PictureHeader.DeblockingFilter = ON; else EC->PictureHeader.DeblockingFilter = OFF; // Check to see if improved PB-frame mode requested. if (pConfiguration->bImprovedPBState) { EC->PictureHeader.ImprovedPB = ON; EC->u8EncodePBFrame = TRUE; } else EC->PictureHeader.ImprovedPB = OFF; } #endif // Turn off AP mode if the QP_mean is lower than a certain level. This should increase // sharpness for low motion (low QP => no AP), and reduce blockiness at high motion // (higher QP => with AP) #ifdef USE_MMX // { USE_MMX if (ToggleAP == ON && MMX_Enabled == FALSE) #else // }{ USE_MMX if (ToggleAP == ON) #endif // } USE_MMX { if (EC->PictureHeader.AP == ON && EC->BRCState.QP_mean < AP_MODE_QP_LEVEL && EC->u8EncodePBFrame == FALSE) EC->PictureHeader.AP = OFF; } } // If we are not going to encode as a PB-frame, reset the saved flag if (EC->u8EncodePBFrame == FALSE) EC->u8SavedBFrame = FALSE; // verify that flags are set correctly if (EC->PictureHeader.UMV == ON) { #ifdef USE_MMX // { USE_MMX if (MMX_Enabled == FALSE) #endif // } USE_MMX { // can't do this #ifdef USE_MMX // { USE_MMX DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Warning: turning UMV off MMX_Enabled is FALSE\r\n", _fx_)); #else // }{ USE_MMX DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Warning: turning UMV off MMX_Enabled is FALSE\r\n", _fx_)); #endif // } USE_MMX EC->PictureHeader.UMV = OFF; } } #ifdef H263P if (EC->PictureHeader.ImprovedPB == ON) { #ifdef USE_MMX // { USE_MMX if (MMX_Enabled == FALSE) #endif // } USE_MMX { // can't do this DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Warning: turning improved PB off MMX_Enabled is FALSE\r\n", _fx_)); EC->PictureHeader.ImprovedPB = OFF; } } #endif // H263P #ifdef COUNT_BITS // Clear bit counters. InitBits(EC); #endif #ifdef USE_MMX // { USE_MMX DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: AP: %d, PB: %d, UMV: %d, MMX: %d, Target fr.size: %d\r\n", _fx_, EC->PictureHeader.AP, EC->u8EncodePBFrame, EC->PictureHeader.UMV, MMX_Enabled, lpicComp->dwFrameSize)); #else // }{ USE_MMX DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: AP: %d, PB: %d, UMV: %d, Target fr.size: %d\r\n", _fx_, EC->PictureHeader.AP, EC->u8EncodePBFrame, EC->PictureHeader.UMV, lpicComp->dwFrameSize)); #endif // } USE_MMX #if H263P DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: H.263+ options: IPB: %d, DF: %d\r\n", _fx_, EC->PictureHeader.ImprovedPB, EC->PictureHeader.DeblockingFilter)); #endif /* * Check to see if this is an inter-frame and if a B frame has not been * saved yet. If so, we do nothing but save the frame to the B frame * buffer and exit. * */ U32 TRB; /* * Turn PB frame option back on if it was just * temporariy turned off for last frame. */ if( EC->u8EncodePBFrame == TEMPORARILY_FALSE ) EC->u8EncodePBFrame = TRUE; // If this is to be saved as a B frame. if (EC->u8EncodePBFrame == TRUE && EC->PictureHeader.PicCodType == INTERPIC && EC->u8SavedBFrame == FALSE) { /* * Set temporal reference for B frame. * It is the number of non-transmitted pictures (at 29.97 Hz) * since the last P or I frame plus 1. TRB has a maximum value * of 7, and can never be zero. * TODO: At the beginning of a sequence, the key frame is compressed, * and then the first frame is copied over to the B frame store, so that * temporal reference for B is zero, which is not allowed. This may cause * problems in some decoders. */ TRB = (lpicComp->lFrameNum % 256); // Take the modulo in order to compare it with TR. if ( TRB < EC->PictureHeader.TR ) TRB += 256; // It should always be greater than TR. TRB = TRB - EC->PictureHeader.TR; // Calculate the TRB value for the bitstream. if (TRB > 7) { /* * We don't want to encode this as a PB-frame because TRB > 7, or * the adaptive switch has turned PB-frames off for a while. */ EC->PictureHeader.TR = (lpicComp->lFrameNum % 256); EC->u8EncodePBFrame = TEMPORARILY_FALSE; // Turn off PBframe for this frame. DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: TRB too big (%d), making P frame, TR = %d\r\n", _fx_, TRB, EC->PictureHeader.TR)); } else { EC->PictureHeader.TRB = (U8) TRB; DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Saving B Frame, TRB = %d\r\n", _fx_, EC->PictureHeader.TRB)); // Copy with color conversion and return #if defined(H263P) || defined(USE_BILINEAR_MSH26X) colorCnvtFrame(EC->ColorConvertor, lpCompInst, lpicComp, EC->pU8_BFrm_YPlane, EC->pU8_BFrm_UPlane, EC->pU8_BFrm_VPlane); #else colorCnvtFrame(EC, lpCompInst, lpicComp, EC->pU8_BFrm_YPlane, EC->pU8_BFrm_UPlane, EC->pU8_BFrm_VPlane); #endif EC->u8SavedBFrame = TRUE; // indicate that we saved a B frame. lpCompInst->CompressedSize = 8; // Internal Encoder/decoder agreement #ifdef ENCODE_STATS StatsFrameSize(lpCompInst->CompressedSize, lpCompInst->CompressedSize); #endif /* ENCODE_STATS */ goto done; // <<<<<<<<<<<<<<<<<<<< } } else // This is a P or I frame. { // Save temporal reference modulo 256. EC->PictureHeader.TR = (lpicComp->lFrameNum % 256); #ifdef _DEBUG if (EC->u8EncodePBFrame == TRUE) DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: PB Frame, TR = %d\r\n", _fx_, EC->PictureHeader.TR)); else if (EC->PictureHeader.PicCodType == INTRAPIC) DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: I Frame, TR = %d\r\n", _fx_, EC->PictureHeader.TR)); else DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: P Frame, TR = %d\r\n", _fx_, EC->PictureHeader.TR)); #endif } // Initialize Motion Estimation state InitMEState(EC, lpicComp, pConfiguration); // Get pointer to macrobock action stream. MBlockActionPtr = EC->pU8_MBlockActionStream; /****************************************************************** * RGB to YVU 12 Conversion ******************************************************************/ #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON #if defined(H263P) || defined(USE_BILINEAR_MSH26X) colorCnvtFrame(EC->ColorConvertor, lpCompInst, lpicComp, EC->pU8_CurrFrm_YPlane, EC->pU8_CurrFrm_UPlane, EC->pU8_CurrFrm_VPlane); #else colorCnvtFrame(EC, lpCompInst, lpicComp, EC->pU8_CurrFrm_YPlane, EC->pU8_CurrFrm_UPlane, EC->pU8_CurrFrm_VPlane); #endif #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uInputCC) #endif // } DETAILED_ENCODE_TIMINGS_ON #if SAMPLE_RGBCONV_TIME && ELAPSED_ENCODER_TIME StopSample (); #endif /****************************************************** * Set picture level quantizer. ******************************************************/ // clear the still quantizer counter if this is not a still frame or // it is the key frame for a still frame sequence. R.H. if ( ((lpicComp->dwFlags & CODEC_CUSTOM_STILL) == 0 ) || ((lpicComp->dwFlags & CODEC_CUSTOM_STILL) && (EC->PictureHeader.PicCodType == INTRAPIC)) ) EC->BRCState.u8StillQnt = 0; // 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); } // Use a different quantizer selection scheme if this is a // progressive still transmission. if (lpicComp->dwFlags & CODEC_CUSTOM_STILL) { bBitRateControl = OFF; #ifdef USE_MMX // { USE_MMX if (MMX_Enabled == TRUE) EC->PictureHeader.PQUANT = StillImageQnt_MMX[ EC->BRCState.u8StillQnt ]; else EC->PictureHeader.PQUANT = StillImageQnt[ EC->BRCState.u8StillQnt ]; #else // }{ USE_MMX EC->PictureHeader.PQUANT = StillImageQnt[ EC->BRCState.u8StillQnt ]; #endif // } USE_MMX DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Setting still frames QP : %d\r\n", _fx_, EC->PictureHeader.PQUANT)); } // If requested frame size is 0, then we simply set the quantizer // according to the value in dwQuality. else if (lpicComp->dwFrameSize == 0) { bBitRateControl = OFF; EC->PictureHeader.PQUANT = clampQP((10000 - lpicComp->dwQuality)*32/10000); // In case a fixed quality setting is chosen (for example from VidEdit), // we have to limit the lower QP value, in order not to blow the quite // small bitstream buffer size. This size is set to be compliant with // the H.263 spec. If the "chance of buffer overflow" code had not been // added (search for "bGOBoverflowWarning", these limits would have had // to be even higher. if (EC->PictureHeader.PicCodType == INTERPIC) { if (EC->PictureHeader.PQUANT < 3) EC->PictureHeader.PQUANT = 3; } else { if (EC->PictureHeader.PQUANT < 8) EC->PictureHeader.PQUANT = 8; } DEBUGMSG(ZONE_BITRATE_CONTROL, ("\r\n%s: Bitrate controller disabled (no target frame size), setting EC->PictureHeader.PQUANT = %ld\r\n", _fx_, EC->PictureHeader.PQUANT)); // Limit the picture header QP to 2. Because of the calculation of u8QPMin // below, this will effectively limit the QP at 2 for all macroblocks. // The reason we need this is that the encoder generates an illegal // bitstream when encoding a synthetic image for QP=1 if (EC->PictureHeader.PQUANT == 1) EC->PictureHeader.PQUANT = 2; // Calculate the lower level for GQuant in this picture u8QPMin = EC->PictureHeader.PQUANT - EC->PictureHeader.PQUANT/3; } else { // Calculate PQUANT based on bits used in last picture // Get Target Frame Rate that was passed from CompressFrames structure. if (lpCompInst->FrameRate != 0) EC->BRCState.TargetFrameRate = lpCompInst->FrameRate; bBitRateControl = ON; // If this is to be compressed as a PB frame, then we modify // the target framesize for the P frame to be a percentage // of twice the target frame size. if ((EC->u8EncodePBFrame == TRUE) && (EC->PictureHeader.PicCodType == INTERPIC) && (EC->u8SavedBFrame == TRUE)) EC->BRCState.uTargetFrmSize = (80 * 2 * lpicComp->dwFrameSize)/100; else EC->BRCState.uTargetFrmSize = lpicComp->dwFrameSize; 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, (DWORD)lpicComp->dwQuality, (DWORD)lpicComp->dwFrameSize << 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 - lpicComp->dwQuality)*15/10000))); // Get the new quantizer value EC->PictureHeader.PQUANT = CalcPQUANT( &(EC->BRCState), EC->PictureHeader.PicCodType); // Calculate the min and max value for GQuant in this picture u8QPMax = 31; u8QPMin = clampQP((10000 - lpicComp->dwQuality)*15/10000); } gquant_prev = EC->PictureHeader.PQUANT; QP_cumulative = 0; // Check for AP, UMV or deblocking-filter modes. Each of these allows // motion vectors to point outside of the reference picture. // Need to verify this in final H.263+ spec for the deblocking filter. if (EC->PictureHeader.AP == ON || EC->PictureHeader.UMV #ifdef H263P || EC->PictureHeader.DeblockingFilter == ON #endif ) { ExpandPlane((U32)EC->pU8_PrevFrm_YPlane, (U32)EC->FrameWidth, (U32)EC->FrameHeight, 16); ExpandPlane((U32)EC->pU8_PrevFrm_UPlane, (U32)EC->FrameWidth>>1, (U32)EC->FrameHeight>>1, 8); ExpandPlane((U32)EC->pU8_PrevFrm_VPlane, (U32)EC->FrameWidth>>1, (U32)EC->FrameHeight>>1, 8); } // If PB-frames are used and AP or UMV is not used at the same time, we can't search // for a PB-delta vector (this is a limitation in the motion estimation routine, // not the standard) // If we allowed searching for B-frame vectors without AP, UMV or DF, we would need // to worry about searching outside of the frame if (EC->u8EncodePBFrame == TRUE && EC->PictureHeader.AP == OFF && EC->PictureHeader.UMV == OFF #ifdef H263P && EC->PictureHeader.DeblockingFilter == OFF #endif ) u32BFrmZeroThreshold = 999999; // do not search for other vectors than zero vector else #ifdef USE_MMX // { USE_MMX u32BFrmZeroThreshold = (MMX_Enabled == FALSE ? 384 : 500); #else // }{ USE_MMX u32BFrmZeroThreshold = 384; #endif // } USE_MMX // Variables which will not change during the frame // Gim 4/16/97 - added u32sizeBSnEBS // u32sizeBitBuffer : max. allowable frame size w/o RTP stuff // u32sizeBSnEBS : max. allowable size w/ RTP stuff (EBS & trailer) #if defined(H263P) u32sizeBSnEBS = CompressGetSize(lpCompInst, lpicComp->lpbiInput, lpicComp->lpbiOutput); #elif defined(USE_BILINEAR_MSH26X) u32sizeBSnEBS = CompressGetSize(pi, lpicComp->lpbiInput, lpicComp->lpbiOutput); #else u32sizeBSnEBS = CompressGetSize(lpCompInst, lpicComp->lpbiInput, 0); #endif if (pConfiguration->bRTPHeader) u32sizeBitBuffer = u32sizeBSnEBS - getRTPBsInfoSize(lpCompInst); else u32sizeBitBuffer = u32sizeBSnEBS; u32tempBuf = (3 * u32sizeBitBuffer / EC->NumMBRows) >> 2; /* * Check to see if we told VfW to create a buffer smaller * than the maximum allowable. */ ASSERT(u32sizeBitBuffer <= sizeof_bitstreambuf) // Check to see if we are to encode a PB frame bEncodePBFrame = (EC->u8EncodePBFrame && EC->u8SavedBFrame); bPBFailed = FALSE; #if defined(H263P) EC->pFutrPMBData = ((T_H263EncoderInstanceMemory *)(lpCompInst->EncoderInst))->FutrPMBData; EC->pWeightForwMotion = ((T_H263EncoderInstanceMemory *)(lpCompInst->EncoderInst))->WeightForwMotion; // values based on TRb and TRd EC->pWeightBackMotion = ((T_H263EncoderInstanceMemory *)(lpCompInst->EncoderInst))->WeightBackMotion; // values based on TRb and TRd #endif if (bEncodePBFrame) { TRb = EC->PictureHeader.TRB; TRd = (I32) EC->PictureHeader.TR - (I32) EC->PictureHeader.TRPrev; if (TRd == 0) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Warning: TR == TRPrev. Setting TRd = 256\r\n", _fx_)); } else if (TRd < 0) TRd += 256; for (j = 0; j < 128; j ++) { #if defined(H263P) EC->pWeightForwMotion[j] = (I8) ((TRb * (j-64)) / TRd); EC->pWeightBackMotion[j] = (I8) (((TRb-TRd) * (j-64)) / TRd); #else WeightForwMotion[j] = (I8) ((TRb * (j-64)) / TRd); WeightBackMotion[j] = (I8) (((TRb-TRd) * (j-64)) / TRd); #endif } } /*************************************************************** * Initialization before encoding all GOBs. * Store frame header code into bitstream buffer. ***************************************************************/ if (pConfiguration->bRTPHeader) H263RTP_ResetBsInfoStream(EC); // zero bit stream buffer pCurBitStream = EC->pU8_BitStream; u8bitoffset = 0; GOBHeaderMask = 1; EC->GOBHeaderPresent = 0; // Clear GOB Header Present flag. encodeFrameHeader(EC, &pCurBitStream, &u8bitoffset, FALSE); #ifdef USE_MMX // { USE_MMX if (MMX_Enabled == FALSE) { for (GOB = 0; GOB < EC->NumMBRows; GOB ++, GOBHeaderMask <<= 1) { StartingMB = GOB * EC->NumMBPerRow; gquant = FindNewQuant(EC,gquant_prev,uAdjCumFrmSize,GOB,u8QPMax,u8QPMin, bBitRateControl,bGOBoverflowWarning); // Save gquant for PB-frames FutrFrmGQUANT[GOB] = gquant; QP_cumulative += gquant; #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON MOTIONESTIMATION( &(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_CurrFrm_YPlane, EC->pU8_PrevFrm_YPlane, 0, // Not used for H.263. 1, // Do Radius 15 search. 1, // Half Pel Motion Estimation flag (0-off, 1-on) #ifdef H263P (EC->PictureHeader.AP == ON || EC->PictureHeader.DeblockingFilter) ? 1 : 0, // Block MVs flag EC->pPseudoStackSpace, #else (EC->PictureHeader.AP == ON) ? 1 : 0, // Block MVs flag #endif 0, // No Spatial Filtering 150,//384, // Zero Vector Threshold. If less than this threshold // don't search for NZ MV's. Set to 99999 to not search. 128, // 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. 512, // 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. 20,//96, // Empty Threshold. Set to 0 to not force empty blocks. 550,///1152, // Inter Coding Threshold. If the inter SWD is less than // this amount then don't bother calc. the intra SWD. 500, // Intra Coding Differential. Bias against choosing INTRA // blocks. 0, // Spatial Filtering Threshold. 0, // Spatial Filtering Differential. &IntraSWDTotal, &IntraSWDBlocks, &InterSWDTotal, &InterSWDBlocks ); #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uMotionEstimation) #endif // } DETAILED_ENCODE_TIMINGS_ON // Sum up SWD iSumSWD += IntraSWDTotal + InterSWDTotal; /* * If it's an inter frame then calculate chroma vectors. * Also check the inter coded count for each macro block * and force to intra if it exceeds 132. */ if (EC->PictureHeader.PicCodType == INTERPIC) { calcGOBChromaVectors(EC, StartingMB, pConfiguration); // for IA this is called after motion estimation Check_InterCodeCnt(EC, StartingMB); } // Save the starting offset of the GOB as the start // bit offset of the first MB. if (bEncodePBFrame) { #if defined(H263P) EC->pFutrPMBData[StartingMB].MBStartBitOff = (U32) (((pCurBitStream - EC->pU8_BitStream) << 3) + u8bitoffset); #else FutrPMBData[StartingMB].MBStartBitOff = (U32) (((pCurBitStream - EC->pU8_BitStream) << 3) + u8bitoffset); #endif } if (GOB && (pConfiguration->bRTPHeader || gquant != gquant_prev)) { unsigned int GFID; // Set a bit if header is present. (bit0=GOB0, bit1=GOB1, ...) EC->GOBHeaderPresent |= GOBHeaderMask; // Write GOB start code. PutBits(FIELDVAL_GBSC, FIELDLEN_GBSC, &pCurBitStream, &u8bitoffset); // Write GOB number. PutBits(GOB, FIELDLEN_GN, &pCurBitStream, &u8bitoffset); // Write GOB frame ID. // According to section 5.2.5 of the H.263 specification: // "GFID shall have the same value in every GOB header of a given // picture. Moreover, if PTYPE as indicated in a picture header is // the same as for the previous transmitted picture, GFID shall have // the same value as in that previous picture. However, if PTYPE in // a certain picture header differs from the PTYPE in the previous // transmitted picture header, the value for GFID in that picture // shall differ from the value in the previous picture." // In our usage of H.263, we usually send either I of P frames with // all options turned of, or always the same options turned on. This // simplifies the fix in allowing us to compute a GFID based only on // the picture type and the presence of at least on option. GFID = (EC->PictureHeader.PB || EC->PictureHeader.AP || EC->PictureHeader.SAC || EC->PictureHeader.UMV) ? 2 : 0; if (EC->PictureHeader.PicCodType == INTRAPIC) GFID++; PutBits(GFID, FIELDLEN_GFID, &pCurBitStream, &u8bitoffset); // Write GQUANT. PutBits(gquant, FIELDLEN_GQUANT, &pCurBitStream, &u8bitoffset); gquant_prev = gquant; #ifdef COUNT_BITS EC->Bits.GOBHeader += FIELDLEN_GBSC + FIELDLEN_GN + FIELDLEN_GFID + FIELDLEN_GQUANT; #endif } /* * 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 DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON FORWARDDCT(&(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_CurrFrm_YPlane, EC->pU8_PrevFrm_YPlane, 0, EC->pU8_DCTCoefBuf, 0, // 0 = not a B-frame EC->PictureHeader.AP == ON, // Advanced prediction (OBMC) bEncodePBFrame, // Is P of PB pair? EC->pU8_PredictionScratchArea, EC->NumMBPerRow ); #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uFDCT) #endif // } DETAILED_ENCODE_TIMINGS_ON /* * Input is the string of coefficient pairs output from the * DCT routine. */ #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON GOB_Q_RLE_VLC_WriteBS( EC, EC->pU8_DCTCoefBuf, &pCurBitStream, &u8bitoffset, #if defined(H263P) EC->pFutrPMBData, #else FutrPMBData, #endif GOB, gquant, pConfiguration->bRTPHeader, StartingMB); #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uQRLE) #endif // } DETAILED_ENCODE_TIMINGS_ON //Chad INTRA GOB if (pConfiguration->bRTPHeader && IsIntraCoded(EC, GOB)) uUsedByIntra += pCurBitStream - EC->pU8_BitStream + 1 - uCumFrmSize; // Accumulate number of bytes used in frame so far. uCumFrmSize = pCurBitStream - EC->pU8_BitStream + 1; // Here we will check to see if we have blown the buffer. If we have, // then we will set the next frame up to be a key frame and return an // ICERR_ERROR. We hope that with an INTRA quantizer of 16, we will not // overflow the buffer for the next frame. if (uCumFrmSize > u32sizeBitBuffer) { ERRORMESSAGE(("%s: Buffer overflow, uCumFrmSize %d > %d\r\n", _fx_, uCumFrmSize, u32sizeBitBuffer)); // Now clear the buffer for the next frame and set up for a key frame memset(EC->pU8_BitStream, 0, uCumFrmSize); EC->bMakeNextFrameKey = TRUE; // Could be a problem in still mode if ret = ICERR_ERROR; // we blow the buffer on the first key frame: RH goto done; } else { if ((bEncodePBFrame?3*uCumFrmSize>>1:uCumFrmSize) > ((GOB + 1) * u32tempBuf)) { // set the next GOB quantizer to be higher to minimize overflowing the // buffer at the end of GOB processing. bGOBoverflowWarning = TRUE; DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, ("%s: Anticipating overflow: uCumFrmSize = %ld bits > (GOB + 1) * u32tempBuf = (#%ld + 1) * %ld\r\n", _fx_, uCumFrmSize << 3, GOB, u32tempBuf << 3)); } else bGOBoverflowWarning = FALSE; } // Gim 4/16/97 - moved this adjustment from before to after the // buffer check above // if the current GOB is intra coded, adjust the cumulated sum if (pConfiguration->bRTPHeader) { if (!GOB) uAdjCumFrmSize = uCumFrmSize - uUsedByIntra / 4; else uAdjCumFrmSize = uCumFrmSize - uUsedByIntra; } else uAdjCumFrmSize = uCumFrmSize; } // for GOB //Chad INTRA GOB restore after use uUsedByIntra = 0; // Store the number of bits spent so far EC->uBitUsageProfile[GOB] = uAdjCumFrmSize; } else // MMX_Enabled == TRUE { MMxMESignaturePrep(EC->pU8_PrevFrm_YPlane, EC->pU8_Signature_YPlane, EC->FrameWidth, EC->FrameHeight); for (GOB = 0; GOB < EC->NumMBRows; GOB ++, GOBHeaderMask <<= 1) { StartingMB = GOB * EC->NumMBPerRow; // Check inter code count for all macroblocks on this row // Need special version for MMX since it is called before motion estiamtion // When the intra coding flag is set, Brian still does motion estimation // for this MB in MMXEDTQ if the PB coding flag is set Check_InterCodeCnt_MMX(EC, StartingMB); gquant = FindNewQuant(EC,gquant_prev,uCumFrmSize,GOB,u8QPMax,u8QPMin, bBitRateControl,bGOBoverflowWarning); // Save gquant for PB-frames FutrFrmGQUANT[GOB] = gquant; QP_cumulative += gquant; #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON // This does the pass over the Luma blocks... __try { MMxEDTQ ( &(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_CurrFrm_YPlane, EC->pU8_PrevFrm_YPlane, EC->pU8_BFrm_YPlane, EC->pU8_Signature_YPlane, #if defined(H263P) EC->pWeightForwMotion, EC->pWeightBackMotion, #else WeightForwMotion, WeightBackMotion, #endif EC->FrameWidth, 1, // Half Pel Motion Estimation flag (0-off, 1-on) #ifdef H263P // H.263+, deblocking filter automatically turns on // block level MVs, but not OBMC (EC->PictureHeader.AP == ON) || (EC->PictureHeader.DeblockingFilter == ON), // Block MVs flag EC->pPseudoStackSpace, #else EC->PictureHeader.AP == ON, // Block MVs flag #endif 0, // No Spatial Filtering EC->PictureHeader.AP == ON, // Advanced Prediction (OBMC) and MVs outside of picture flag bEncodePBFrame, // Is PB pair? #ifdef H263P EC->PictureHeader.DeblockingFilter == ON, // Use deblocking filter (8x8 and unrestricted MV's) EC->PictureHeader.ImprovedPB == ON, // Use improved PB-frame method #endif 1, // Do Luma blocks this Pass EC->PictureHeader.UMV, // MVs outside of picture and within [-31.5, 31.5] #ifdef H263P (GOB && (pConfiguration->bRTPHeader || gquant != gquant_prev)), // GOB header present. Used to generate MV predictor and search range in UMV #endif gquant, min((6*gquant)>>2, 31), // TODO: to match DBQUANT in picture header u32BFrmZeroThreshold, // BFrmZeroVectorThreshold 0, // SpatialFiltThreshold 0, // SpatialFiltDifferential &iSWD, &iBSWD, EC->pI8_MBRVS_Luma, EC->pI8_MBRVS_BLuma+GOB*(65*3*22*4) ); } __except(ExceptionFilterForMMxEDTQ(GetExceptionInformation(),EC->pI8_MBRVS_BLuma,1)) { // no exception handler } #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uMotionEstimation) #endif // } DETAILED_ENCODE_TIMINGS_ON // Sum up SWDs iSumSWD += iSWD; iSumBSWD += iBSWD; /* * If it's an inter frame then calculate chroma vectors. * Also check the inter coded count for each macro block * and force to intra if it exceeds 132. */ if (EC->PictureHeader.PicCodType == INTERPIC) { calcGOBChromaVectors(EC, StartingMB, pConfiguration); if (bEncodePBFrame) // Calculate chroma vectors. calcBGOBChromaVectors(EC, StartingMB); } // Save the starting offset of the GOB as the start // bit offset of the first MB. if (bEncodePBFrame) { #if defined(H263P) EC->pFutrPMBData[StartingMB].MBStartBitOff = (U32) (((pCurBitStream - EC->pU8_BitStream) << 3) + u8bitoffset); #else FutrPMBData[StartingMB].MBStartBitOff = (U32) (((pCurBitStream - EC->pU8_BitStream) << 3) + u8bitoffset); #endif } if (GOB && (pConfiguration->bRTPHeader || gquant != gquant_prev)) { unsigned int GFID; // Set a bit if header is present. (bit0=GOB0, bit1=GOB1, ...) EC->GOBHeaderPresent |= GOBHeaderMask; // Write GOB start code. PutBits(FIELDVAL_GBSC, FIELDLEN_GBSC, &pCurBitStream, &u8bitoffset); // Write GOB number. PutBits(GOB, FIELDLEN_GN, &pCurBitStream, &u8bitoffset); // Write GOB frame ID. // According to section 5.2.5 of the H.263 specification: // "GFID shall have the same value in every GOB header of a given // picture. Moreover, if PTYPE as indicated in a picture header is // the same as for the previous transmitted picture, GFID shall have // the same value as in that previous picture. However, if PTYPE in // a certain picture header differs from the PTYPE in the previous // transmitted picture header, the value for GFID in that picture // shall differ from the value in the previous picture." // In our usage of H.263, we usually send either I of P frames with // all options turned of, or always the same options turned on. This // simplifies the fix in allowing us to compute a GFID based only on // the picture type and the presence of at least on option. GFID = (EC->PictureHeader.PB || EC->PictureHeader.AP || EC->PictureHeader.SAC || EC->PictureHeader.UMV) ? 2 : 0; if (EC->PictureHeader.PicCodType == INTRAPIC) GFID++; PutBits(GFID, FIELDLEN_GFID, &pCurBitStream, &u8bitoffset); // Write GQUANT. PutBits(gquant, FIELDLEN_GQUANT, &pCurBitStream, &u8bitoffset); gquant_prev = gquant; #ifdef COUNT_BITS EC->Bits.GOBHeader += FIELDLEN_GBSC + FIELDLEN_GN + FIELDLEN_GFID + FIELDLEN_GQUANT; #endif } /* * 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. */ // This does the pass over the Chroma blocks.... // #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON __try { MMxEDTQ ( &(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_CurrFrm_YPlane, EC->pU8_PrevFrm_YPlane, EC->pU8_BFrm_YPlane, EC->pU8_Signature_YPlane, #if defined(H263P) EC->pWeightForwMotion, EC->pWeightBackMotion, #else WeightForwMotion, WeightBackMotion, #endif EC->FrameWidth, 1, // Half Pel Motion Estimation flag (0-off, 1-on) #ifdef H263P // H.263+, deblocking filter automatically turns on // block level MVs, but not OBMC (EC->PictureHeader.AP == ON) || (EC->PictureHeader.DeblockingFilter == ON), // Block MVs flag EC->pPseudoStackSpace, #else EC->PictureHeader.AP == ON, // Block MVs flag #endif 0, // No Spatial Filtering EC->PictureHeader.AP == ON, // Advanced Prediction (OBMC) bEncodePBFrame, // Is PB pair? #ifdef H263P EC->PictureHeader.DeblockingFilter == ON, // Use deblocking filter (8x8 and unrestricted MV's) EC->PictureHeader.ImprovedPB == ON, // Use improved PB-frame method 0, // If not H.263+, must be 0 0, // If not H.263+, must be 0 #endif 0, // Do Chroma blocks this Pass 0, // 1 for extended motion vectors #ifdef H263P 0, // GOB header present. Used in UMV to generate MV predictor. #endif gquant, min((6*gquant) >> 2, 31), 500, // BFrmZeroVectorThreshold 0, // SpatialFiltThreshold 0, // SpatialFiltDifferential &iSWD, &iBSWD, EC->pI8_MBRVS_Chroma, EC->pI8_MBRVS_BChroma+GOB*(65*3*22*2) ); } __except(ExceptionFilterForMMxEDTQ(GetExceptionInformation(),EC->pI8_MBRVS_BChroma,0)) { // no exception handler } #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uFDCT) #endif // } DETAILED_ENCODE_TIMINGS_ON /* * Input is the string of coefficient pairs output from the * DCT routine. */ #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON GOB_VLC_WriteBS( EC, EC->pI8_MBRVS_Luma, EC->pI8_MBRVS_Chroma, &pCurBitStream, &u8bitoffset, #if defined(H263P) EC->pFutrPMBData, #else FutrPMBData, #endif GOB, gquant, pConfiguration->bRTPHeader, StartingMB); #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uQRLE) #endif // } DETAILED_ENCODE_TIMINGS_ON // Accumulate number of bytes used in frame so far. uCumFrmSize = pCurBitStream - EC->pU8_BitStream + 1; // Here we will check to see if we have blown the buffer. If we have, // then we will set the next frame up to be a key frame and return an // ICERR_ERROR. We hope that with an INTRA quantizer of 16, we will not // overflow the buffer for the next frame. if (uCumFrmSize > u32sizeBitBuffer) { ERRORMESSAGE(("%s: Buffer overflow, uCumFrmSize %d > %d\r\n", _fx_, uCumFrmSize, u32sizeBitBuffer)); memset(EC->pU8_BitStream, 0, uCumFrmSize); EC->bMakeNextFrameKey = TRUE; // Could be a problem in still mode if ret = ICERR_ERROR; // we blow the buffer on the first key frame: RH goto done; } else { if ((bEncodePBFrame?3*uCumFrmSize>>1:uCumFrmSize) > ((GOB + 1) * u32tempBuf)) // set the next GOB quantizer to be higher to minimize overflowing the // buffer at the end of GOB processing. bGOBoverflowWarning = TRUE; else bGOBoverflowWarning = FALSE; } } // for GOB // Store the number of bits spent so far EC->uBitUsageProfile[GOB] = uCumFrmSize; // This is the new MMX PB-frames switch // Simple check to see if B-frame will look bad // This could be possibly be improved by looking at the // actual number of coefficients, or the number of bits // in the bitstream. #ifdef H263P // Always use the B frame if improved PB-frame mode and AP or UMV mode requested if (TogglePB == TRUE && iSumBSWD >= iSumSWD && !(EC->PictureHeader.ImprovedPB == ON && (EC->PictureHeader.AP == ON || EC->PictureHeader.UMV == ON || EC->PictureHeader.DeblockingFilter == ON))) #else if (TogglePB == TRUE && iSumBSWD >= iSumSWD) #endif { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Giving up PB, SumBSWD = %d, SumSWD = %d\r\n", _fx_, iSumBSWD, iSumSWD)); bEncodePBFrame = FALSE; EC->u8SavedBFrame = FALSE; } } #else // }{ USE_MMX for (GOB = 0; GOB < EC->NumMBRows; GOB ++, GOBHeaderMask <<= 1) { StartingMB = GOB * EC->NumMBPerRow; gquant = FindNewQuant(EC,gquant_prev,uAdjCumFrmSize,GOB,u8QPMax,u8QPMin, bBitRateControl,bGOBoverflowWarning); // Save gquant for PB-frames FutrFrmGQUANT[GOB] = gquant; QP_cumulative += gquant; #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON MOTIONESTIMATION( &(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_CurrFrm_YPlane, EC->pU8_PrevFrm_YPlane, 0, // Not used for H.263. 1, // Do Radius 15 search. 1, // Half Pel Motion Estimation flag (0-off, 1-on) #ifdef H263P (EC->PictureHeader.AP == ON || EC->PictureHeader.DeblockingFilter) ? 1 : 0, // Block MVs flag EC->pPseudoStackSpace, #else (EC->PictureHeader.AP == ON) ? 1 : 0, // Block MVs flag #endif 0, // No Spatial Filtering 150,//384, // Zero Vector Threshold. If less than this threshold // don't search for NZ MV's. Set to 99999 to not search. 128, // 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. 512, // 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. 20,//96, // Empty Threshold. Set to 0 to not force empty blocks. 550,///1152, // Inter Coding Threshold. If the inter SWD is less than // this amount then don't bother calc. the intra SWD. 500, // Intra Coding Differential. Bias against choosing INTRA // blocks. 0, // Spatial Filtering Threshold. 0, // Spatial Filtering Differential. &IntraSWDTotal, &IntraSWDBlocks, &InterSWDTotal, &InterSWDBlocks ); #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uMotionEstimation) #endif // } DETAILED_ENCODE_TIMINGS_ON // Sum up SWD iSumSWD += IntraSWDTotal + InterSWDTotal; /* * If it's an inter frame then calculate chroma vectors. * Also check the inter coded count for each macro block * and force to intra if it exceeds 132. */ if (EC->PictureHeader.PicCodType == INTERPIC) { calcGOBChromaVectors(EC, StartingMB, pConfiguration); // for IA this is called after motion estimation Check_InterCodeCnt(EC, StartingMB); } // Save the starting offset of the GOB as the start // bit offset of the first MB. if (bEncodePBFrame) { #if defined(H263P) EC->pFutrPMBData[StartingMB].MBStartBitOff = (U32) (((pCurBitStream - EC->pU8_BitStream) << 3) + u8bitoffset); #else FutrPMBData[StartingMB].MBStartBitOff = (U32) (((pCurBitStream - EC->pU8_BitStream) << 3) + u8bitoffset); #endif } if (GOB && (pConfiguration->bRTPHeader || gquant != gquant_prev)) { unsigned int GFID; // Set a bit if header is present. (bit0=GOB0, bit1=GOB1, ...) EC->GOBHeaderPresent |= GOBHeaderMask; // Write GOB start code. PutBits(FIELDVAL_GBSC, FIELDLEN_GBSC, &pCurBitStream, &u8bitoffset); // Write GOB number. PutBits(GOB, FIELDLEN_GN, &pCurBitStream, &u8bitoffset); // Write GOB frame ID. // According to section 5.2.5 of the H.263 specification: // "GFID shall have the same value in every GOB header of a given // picture. Moreover, if PTYPE as indicated in a picture header is // the same as for the previous transmitted picture, GFID shall have // the same value as in that previous picture. However, if PTYPE in // a certain picture header differs from the PTYPE in the previous // transmitted picture header, the value for GFID in that picture // shall differ from the value in the previous picture." // In our usage of H.263, we usually send either I of P frames with // all options turned of, or always the same options turned on. This // simplifies the fix in allowing us to compute a GFID based only on // the picture type and the presence of at least on option. GFID = (EC->PictureHeader.PB || EC->PictureHeader.AP || EC->PictureHeader.SAC || EC->PictureHeader.UMV) ? 2 : 0; if (EC->PictureHeader.PicCodType == INTRAPIC) GFID++; PutBits(GFID, FIELDLEN_GFID, &pCurBitStream, &u8bitoffset); // Write GQUANT. PutBits(gquant, FIELDLEN_GQUANT, &pCurBitStream, &u8bitoffset); gquant_prev = gquant; #ifdef COUNT_BITS EC->Bits.GOBHeader += FIELDLEN_GBSC + FIELDLEN_GN + FIELDLEN_GFID + FIELDLEN_GQUANT; #endif } /* * 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 DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON FORWARDDCT(&(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_CurrFrm_YPlane, EC->pU8_PrevFrm_YPlane, 0, EC->pU8_DCTCoefBuf, 0, // 0 = not a B-frame EC->PictureHeader.AP == ON, // Advanced prediction (OBMC) bEncodePBFrame, // Is P of PB pair? EC->pU8_PredictionScratchArea, EC->NumMBPerRow ); #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uFDCT) #endif // } DETAILED_ENCODE_TIMINGS_ON /* * Input is the string of coefficient pairs output from the * DCT routine. */ #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON GOB_Q_RLE_VLC_WriteBS( EC, EC->pU8_DCTCoefBuf, &pCurBitStream, &u8bitoffset, #if defined(H263P) EC->pFutrPMBData, #else FutrPMBData, #endif GOB, gquant, pConfiguration->bRTPHeader, StartingMB); #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uQRLE) #endif // } DETAILED_ENCODE_TIMINGS_ON //Chad INTRA GOB if (pConfiguration->bRTPHeader && IsIntraCoded(EC, GOB)) uUsedByIntra += pCurBitStream - EC->pU8_BitStream + 1 - uCumFrmSize; // Accumulate number of bytes used in frame so far. uCumFrmSize = pCurBitStream - EC->pU8_BitStream + 1; // Here we will check to see if we have blown the buffer. If we have, // then we will set the next frame up to be a key frame and return an // ICERR_ERROR. We hope that with an INTRA quantizer of 16, we will not // overflow the buffer for the next frame. if (uCumFrmSize > u32sizeBitBuffer) { ERRORMESSAGE(("%s: Buffer overflow, uCumFrmSize %d > %d\r\n", _fx_, uCumFrmSize, u32sizeBitBuffer)); // Now clear the buffer for the next frame and set up for a key frame memset(EC->pU8_BitStream, 0, uCumFrmSize); EC->bMakeNextFrameKey = TRUE; // Could be a problem in still mode if ret = ICERR_ERROR; // we blow the buffer on the first key frame: RH goto done; } else { if ((bEncodePBFrame?3*uCumFrmSize>>1:uCumFrmSize) > ((GOB + 1) * u32tempBuf)) // set the next GOB quantizer to be higher to minimize overflowing the // buffer at the end of GOB processing. bGOBoverflowWarning = TRUE; else bGOBoverflowWarning = FALSE; } // Gim 4/16/97 - moved this adjustment from before to after the // buffer check above // if the current GOB is intra coded, adjust the cumulated sum if (pConfiguration->bRTPHeader) { if (!GOB) uAdjCumFrmSize = uCumFrmSize - uUsedByIntra / 4; else uAdjCumFrmSize = uCumFrmSize - uUsedByIntra; } else uAdjCumFrmSize = uCumFrmSize; } // for GOB //Chad INTRA GOB restore after use uUsedByIntra = 0; // Store the number of bits spent so far EC->uBitUsageProfile[GOB] = uAdjCumFrmSize; #endif // } USE_MMX #ifdef COUNT_BITS WriteCountBitFile( &(EC->Bits) ); #endif // ------------------------------------------------------------------------ // Write the MBStartBitOff in the sentinel macroblock // ------------------------------------------------------------------------ if (bEncodePBFrame) { // Encoding future P frame #if defined(H263P) EC->pFutrPMBData[EC->NumMBs].MBStartBitOff = (U32) (((pCurBitStream - EC->pU8_BitStream) << 3) + u8bitoffset); #else FutrPMBData[EC->NumMBs].MBStartBitOff = (U32) (((pCurBitStream - EC->pU8_BitStream) << 3) + u8bitoffset); #endif #ifdef DEBUG for (i = 0; i < EC->NumMBs; i++) { #if defined(H263P) ASSERT(EC->pFutrPMBData[i].MBStartBitOff < EC->pFutrPMBData[i + 1].MBStartBitOff) ASSERT(EC->pFutrPMBData[i].CBPYBitOff <= EC->pFutrPMBData[i].MVDBitOff) ASSERT(EC->pFutrPMBData[i].MVDBitOff <= EC->pFutrPMBData[i].BlkDataBitOff) ASSERT(EC->pFutrPMBData[i].BlkDataBitOff <= (EC->pFutrPMBData[i + 1].MBStartBitOff - EC->pFutrPMBData[i].MBStartBitOff)) #else ASSERT(FutrPMBData[i].MBStartBitOff < FutrPMBData[i + 1].MBStartBitOff) ASSERT(FutrPMBData[i].CBPYBitOff <= FutrPMBData[i].MVDBitOff) ASSERT(FutrPMBData[i].MVDBitOff <= FutrPMBData[i].BlkDataBitOff) ASSERT(FutrPMBData[i].BlkDataBitOff <= (FutrPMBData[i + 1].MBStartBitOff - FutrPMBData[i].MBStartBitOff)) #endif } #endif } // ------------------------------------------------------------------------ // Copy the compressed image to the output area. // ------------------------------------------------------------------------ SizeBitStream = pCurBitStream - EC->pU8_BitStream + 1; /* make sure we don't write 8 empty bits */ if (!u8bitoffset) SizeBitStream --; // Gim 4/21/97 - added check for overall buffer overflow before attaching // RTP info and trailer to the end of a P or I frame bitstream if (pConfiguration->bRTPHeader) { SizeBSnEBS = SizeBitStream + H263RTP_GetMaxBsInfoStreamSize(EC); if (SizeBSnEBS > u32sizeBSnEBS) { ERRORMESSAGE(("%s: BS+EBS buffer overflow, SizeBSnEBS %d > %d\r\n", _fx_, SizeBSnEBS, u32sizeBSnEBS)); memset(EC->pU8_BitStream, 0, SizeBitStream); EC->bMakeNextFrameKey = TRUE; ret = ICERR_ERROR; goto done; } } #ifdef ENCODE_STATS uBitStreamBytes = SizeBitStream; #endif memcpy(lpicComp->lpOutput, EC->pU8_BitStream, SizeBitStream); memset(EC->pU8_BitStream, 0, SizeBitStream); if (pConfiguration->bRTPHeader) SizeBitStream += (WORD) H263RTP_AttachBsInfoStream(EC, (U8 *) lpicComp->lpOutput, SizeBitStream); lpCompInst->CompressedSize = SizeBitStream; // ------------------------------------------------------------------------ // Run the decoder on this frame, to get next basis for prediction. // ------------------------------------------------------------------------ ICDecExSt = DefaultICDecExSt; ICDecExSt.lpSrc = lpicComp->lpOutput; ICDecExSt.lpbiSrc = lpicComp->lpbiOutput; ICDecExSt.lpbiSrc->biSizeImage = SizeBitStream; // Decode it in future frame if doing PB-frame ICDecExSt.lpDst = bEncodePBFrame ? EC->pU8_FutrFrm : EC->pU8_PrevFrm; ICDecExSt.lpbiDst = NULL; if (EC->PictureHeader.PicCodType == INTERPIC) ICDecExSt.dwFlags = ICDECOMPRESS_NOTKEYFRAME; // Call the decompressor // Call the decompressor #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_BEFORE(bTimingThisFrame,uStartLow,uStartHigh,uBefore); #endif // } DETAILED_ENCODE_TIMINGS_ON #if defined(DECODE_TIMINGS_ON) || defined(DETAILED_DECODE_TIMINGS_ON) // { #if defined(DECODE_TIMINGS_ON) || defined(DETAILED_DECODE_TIMINGS_ON) ret = H263Decompress (EC->pDecInstanceInfo, (ICDECOMPRESSEX FAR *)&ICDecExSt, FALSE, FALSE); #else // }{ #if defined(DECODE_TIMINGS_ON) || defined(DETAILED_DECODE_TIMINGS_ON) ret = H263Decompress (EC->pDecInstanceInfo, (ICDECOMPRESSEX FAR *)&ICDecExSt, FALSE); #endif // } #if defined(DECODE_TIMINGS_ON) || defined(DETAILED_DECODE_TIMINGS_ON) #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON TIMER_AFTER_P5(bTimingThisFrame,uStartLow,uStartHigh,uBefore,uElapsed,uDecodeFrame) #endif // } DETAILED_ENCODE_TIMINGS_ON if (ret != ICERR_OK) { // Check to see if an error occurred in the decoder. If it did // we don't have a valid "previous frame" hence force the next // frame to be a key frame. ERRORMESSAGE(("%s: Decoder failed in encoder\r\n", _fx_)); EC->bMakeNextFrameKey = TRUE; ret = ICERR_ERROR; goto done; } // ------------------------------------------------------------------------ // Start processing the saved B frame. // ------------------------------------------------------------------------ if (bEncodePBFrame) { #ifdef COUNT_BITS InitBits(EC); #endif // zero PB-frame bit stream buffer. pPB_BitStream = EC->pU8_BitStrCopy; pP_BitStreamStart = (U8 *) lpicComp->lpOutput; u8PB_BitOffset = 0; // Encode the frame header EC->PictureHeader.PB = ON; // Clear GOB Header Present flag. EC->GOBHeaderPresent = 0; GOBHeaderMask = 1; gquant_prev = EC->PictureHeader.PQUANT; if (pConfiguration->bRTPHeader) H263RTP_ResetBsInfoStream(EC); encodeFrameHeader(EC, &pPB_BitStream, &u8PB_BitOffset, TRUE); #ifdef USE_MMX // { USE_MMX if (MMX_Enabled == FALSE) { /***************************************** * . copy edge pels in the previous frame * . initialize arrays used in motion estimation * . foreach(GOB) * . BFRAMEMOTIONESTIMATION * . Compute Chroma motion vectors * . Write GOB header * . FORWARDDCT * . PB_GOB_Q_RLE_VLC_WriteBS *****************************************/ for (GOB = 0; GOB < EC->NumMBRows; GOB ++, GOBHeaderMask <<= 1) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: BFRAME GOB #%d\r\n", _fx_, GOB)); gquant = FutrFrmGQUANT[GOB]; if (GOB && (pConfiguration->bRTPHeader || gquant != gquant_prev)) { // Set a bit if header is present. (bit0=GOB0, bit1=GOB1, ...) EC->GOBHeaderPresent |= GOBHeaderMask; gquant_prev = gquant; } StartingMB = GOB * EC->NumMBPerRow; BFRAMEMOTIONESTIMATION( &(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_BFrm_YPlane, EC->pU8_PrevFrm_YPlane, EC->pU8_FutrFrm_YPlane, #if defined(H263P) EC->pWeightForwMotion+32, EC->pWeightBackMotion+32, #else WeightForwMotion+32, WeightBackMotion+32, #endif u32BFrmZeroThreshold, // Zero Vector Threshold. If less than this threshold don't search for #if defined(H263P) EC->pPseudoStackSpace, #endif // NZ MV's. Set to 99999 to not search. 128, // 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. 96, // Empty Threshold. Set to 0 to not force empty blocks. &InterSWDTotal, &InterSWDBlocks ); iSumBSWD += InterSWDTotal; if (TogglePB && iSumBSWD >= (3 * iSumSWD) >> 1) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Giving up PB, SumBSWD = %d, SumSWD = %d\r\n", _fx_, iSumBSWD, iSumSWD)); memset(EC->pU8_BitStrCopy, 0, pPB_BitStream - EC->pU8_BitStrCopy + 1); bPBFailed = TRUE; break; } // Calculate chroma vectors. calcBGOBChromaVectors(EC, StartingMB); FORWARDDCT( &(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_BFrm_YPlane, EC->pU8_PrevFrm_YPlane, EC->pU8_FutrFrm_YPlane, EC->pU8_DCTCoefBuf, 1, // 1 = BFrame 0, // Advanced prediction irrelevant for B frame. 0, // Is not P of PB pair. 0, // PredictionScratchArea unneeded. EC->NumMBPerRow ); // GOB header is copied to PB stream when the data for the first // macroblock in the GOB is copied PB_GOB_Q_RLE_VLC_WriteBS( EC, EC->pU8_DCTCoefBuf, pP_BitStreamStart, &pPB_BitStream, &u8PB_BitOffset, #if defined(H263P) EC->pFutrPMBData, #else FutrPMBData, #endif GOB, min((6*FutrFrmGQUANT[GOB])>>2, 31), // TODO: to match DBQUANT in picture header pConfiguration->bRTPHeader ); } } else // MMX_Enabled == TRUE { for (GOB = 0; GOB < EC->NumMBRows; GOB ++, GOBHeaderMask <<= 1) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: BFRAME GOB #%d\r\n", _fx_, GOB)); gquant = FutrFrmGQUANT[GOB]; if (GOB && (pConfiguration->bRTPHeader || gquant != gquant_prev)) { // Set a bit if header is present. (bit0=GOB0, bit1=GOB1, ...) EC->GOBHeaderPresent |= GOBHeaderMask; gquant_prev = gquant; } // GOB header is copied to PB stream when the data for the first // macroblock in the GOB is copied PB_GOB_VLC_WriteBS( EC, EC->pI8_MBRVS_BLuma+GOB*(65*3*22*4), EC->pI8_MBRVS_BChroma+GOB*(65*3*22*2), pP_BitStreamStart, &pPB_BitStream, &u8PB_BitOffset, #if defined(H263P) EC->pFutrPMBData, #else FutrPMBData, #endif GOB, min((6 * gquant) >> 2, 31), pConfiguration->bRTPHeader); } } #else // }{ USE_MMX /***************************************** * . copy edge pels in the previous frame * . initialize arrays used in motion estimation * . foreach(GOB) * . BFRAMEMOTIONESTIMATION * . Compute Chroma motion vectors * . Write GOB header * . FORWARDDCT * . PB_GOB_Q_RLE_VLC_WriteBS *****************************************/ for (GOB = 0; GOB < EC->NumMBRows; GOB ++, GOBHeaderMask <<= 1) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: BFRAME GOB #%d\r\n", _fx_, GOB)); gquant = FutrFrmGQUANT[GOB]; if (GOB && (pConfiguration->bRTPHeader || gquant != gquant_prev)) { // Set a bit if header is present. (bit0=GOB0, bit1=GOB1, ...) EC->GOBHeaderPresent |= GOBHeaderMask; gquant_prev = gquant; } StartingMB = GOB * EC->NumMBPerRow; BFRAMEMOTIONESTIMATION( &(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_BFrm_YPlane, EC->pU8_PrevFrm_YPlane, EC->pU8_FutrFrm_YPlane, #if defined(H263P) EC->pWeightForwMotion+32, EC->pWeightBackMotion+32, #else WeightForwMotion+32, WeightBackMotion+32, #endif u32BFrmZeroThreshold, // Zero Vector Threshold. If less than this threshold don't search for #if defined(H263P) EC->pPseudoStackSpace, #endif // NZ MV's. Set to 99999 to not search. 128, // 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. 96, // Empty Threshold. Set to 0 to not force empty blocks. &InterSWDTotal, &InterSWDBlocks ); iSumBSWD += InterSWDTotal; if (TogglePB && iSumBSWD >= (3 * iSumSWD) >> 1) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Giving up PB, SumBSWD = %d, SumSWD = %d\r\n", _fx_, iSumBSWD, iSumSWD)); memset(EC->pU8_BitStrCopy, 0, pPB_BitStream - EC->pU8_BitStrCopy + 1); bPBFailed = TRUE; break; } // Calculate chroma vectors. calcBGOBChromaVectors(EC, StartingMB); FORWARDDCT( &(EC->pU8_MBlockActionStream[StartingMB]), EC->pU8_BFrm_YPlane, EC->pU8_PrevFrm_YPlane, EC->pU8_FutrFrm_YPlane, EC->pU8_DCTCoefBuf, 1, // 1 = BFrame 0, // Advanced prediction irrelevant for B frame. 0, // Is not P of PB pair. 0, // PredictionScratchArea unneeded. EC->NumMBPerRow ); // GOB header is copied to PB stream when the data for the first // macroblock in the GOB is copied PB_GOB_Q_RLE_VLC_WriteBS( EC, EC->pU8_DCTCoefBuf, pP_BitStreamStart, &pPB_BitStream, &u8PB_BitOffset, #if defined(H263P) EC->pFutrPMBData, #else FutrPMBData, #endif GOB, min((6*FutrFrmGQUANT[GOB])>>2, 31), // TODO: to match DBQUANT in picture header pConfiguration->bRTPHeader ); } #endif // } USE_MMX if (bPBFailed == FALSE) { // Copy the compressed image to the output area. SizeBitStream = pPB_BitStream - EC->pU8_BitStrCopy + 1; // make sure we don't write 8 empty bits if (u8PB_BitOffset == 0) SizeBitStream --; // Gim 4/21/97 - check to see if the PB buffer overflows the spec // size. If it does, zero out the PB buffer and continue. The P // frame encoded will be returned. if (SizeBitStream > u32sizeBitBuffer) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: PB buffer overflow, SizeBitStream %d > %d\r\n", _fx_, SizeBitStream, u32sizeBitBuffer)); bPBFailed = TRUE; } else if (pConfiguration->bRTPHeader) { SizeBSnEBS = SizeBitStream + H263RTP_GetMaxBsInfoStreamSize(EC); if (SizeBSnEBS > u32sizeBSnEBS) { DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: PB BS+EBS buffer overflow, SizeBSnEBS %d > %d\r\n", _fx_, SizeBSnEBS, u32sizeBSnEBS)); bPBFailed = TRUE; } } if (bPBFailed == TRUE) { // if buffer overflow has been detected, we will drop the PB // and return the encoded P memset(EC->pU8_BitStrCopy, 0, SizeBitStream); EC->u8SavedBFrame = FALSE; } else { #ifdef ENCODE_STATS uBitStreamBytes = SizeBitStream; #endif memcpy(lpicComp->lpOutput, EC->pU8_BitStrCopy, SizeBitStream); memset(EC->pU8_BitStrCopy, 0, SizeBitStream); if (pConfiguration->bRTPHeader) SizeBitStream += (WORD) H263RTP_AttachBsInfoStream(EC, (U8 *) lpicComp->lpOutput, SizeBitStream); lpCompInst->CompressedSize = SizeBitStream; } } // For the next PB-frame, frame pointers are swapped; i.e. for the next // frame future ... temp = EC->pU8_PrevFrm; EC->pU8_PrevFrm = EC->pU8_FutrFrm; EC->pU8_FutrFrm = temp; temp = EC->pU8_PrevFrm_YPlane; EC->pU8_PrevFrm_YPlane = EC->pU8_FutrFrm_YPlane; EC->pU8_FutrFrm_YPlane = temp; temp = EC->pU8_PrevFrm_UPlane; EC->pU8_PrevFrm_UPlane = EC->pU8_FutrFrm_UPlane; EC->pU8_FutrFrm_UPlane = temp; temp = EC->pU8_PrevFrm_VPlane; EC->pU8_PrevFrm_VPlane = EC->pU8_FutrFrm_VPlane; EC->pU8_FutrFrm_VPlane = temp; EC->u8SavedBFrame = FALSE; EC->PictureHeader.PB = OFF; // RH: why is this here ? } // if (bEncodePBFrame) DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Frame size: %d\r\n", _fx_, lpCompInst->CompressedSize)); #ifdef ENCODE_STATS StatsFrameSize(uBitStreamBytes, lpCompInst->CompressedSize); #endif // ------------------------------------------------------------------------ // update states for next frame, etc. // ------------------------------------------------------------------------ // This is a still image sequence and there is still more quantizers // in the sequence, then increment the quantizer. if ((lpicComp->dwFlags & CODEC_CUSTOM_STILL) && (EC->BRCState.u8StillQnt < (numStillImageQnts-1))) EC->BRCState.u8StillQnt ++; // Calculate average quantizer to be used for next frame. if (EC->PictureHeader.PicCodType == INTERPIC) EC->BRCState.QP_mean = (QP_cumulative + (EC->NumMBRows >> 1)) / EC->NumMBRows; else // If this is an INTRA frame, then we don't want to // use the QP for the next delta frame, hence we just // reset the QP_mean to the default. EC->BRCState.QP_mean = def263INTER_QP; // Record frame size for bit rate controller on next frame. // 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; #ifdef USE_MMX // { USE_MMX if (EC->PictureHeader.PicCodType == INTRAPIC) EC->BRCState.uLastINTRAFrmSz = dwTransportOverhead + ((MMX_Enabled == FALSE) ? uAdjCumFrmSize : uCumFrmSize); else EC->BRCState.uLastINTERFrmSz = dwTransportOverhead + ((MMX_Enabled == FALSE) ? uAdjCumFrmSize : uCumFrmSize); DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: Total cumulated frame size = %ld bits (data: %ld, transport overhead: %ld)\r\n", _fx_, (((MMX_Enabled == FALSE) ? uAdjCumFrmSize : uCumFrmSize) << 3) + (dwTransportOverhead << 3), ((MMX_Enabled == FALSE) ? uAdjCumFrmSize : uCumFrmSize) << 3, dwTransportOverhead << 3)); #else // }{ USE_MMX if (EC->PictureHeader.PicCodType == INTRAPIC) EC->BRCState.uLastINTRAFrmSz = dwTransportOverhead + uAdjCumFrmSize; else EC->BRCState.uLastINTERFrmSz = dwTransportOverhead + uAdjCumFrmSize; DEBUGMSG(ZONE_BITRATE_CONTROL, ("%s: Total cumulated frame size = %ld bits (data: %ld, transport overhead: %ld)\r\n", _fx_, (uAdjCumFrmSize << 3) + (dwTransportOverhead << 3), uAdjCumFrmSize << 3, dwTransportOverhead << 3)); #endif // } USE_MMX // Save temporal reference for next frame. EC->PictureHeader.TRPrev = EC->PictureHeader.TR; // Save AP, UMV and DF modes in case InitMEState needs to re-initialize some data if (EC->PictureHeader.PicCodType == INTERPIC) { EC->prevAP = EC->PictureHeader.AP; EC->prevUMV = EC->PictureHeader.UMV; #ifdef H263P EC->prevDF = EC->PictureHeader.DeblockingFilter; #endif } // send mean quantizer to real-time app. Not necessary, info. only *(lpicComp->lpdwFlags) |= (EC->BRCState.QP_mean << 16); #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) // { #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) TIMER_STOP(bTimingThisFrame,uStartLow,uStartHigh,uEncodeTime); if (bTimingThisFrame) { // Update the decompression timings counter #pragma message ("Current encode timing computations assume P5/90Mhz") UPDATE_COUNTER(g_pctrCompressionTimePerFrame, (uEncodeTime + 45000UL) / 90000UL); DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Compression time: %ld\r\n", _fx_, (uEncodeTime + 45000UL) / 90000UL)); } #endif // } ENCODE_TIMINGS_ON #ifdef LOG_ENCODE_TIMINGS_ON // { LOG_ENCODE_TIMINGS_ON if (bTimingThisFrame) { pEncTimingInfo = EC->pEncTimingInfo + EC->uStatFrameCount; pEncTimingInfo->uEncodeFrame = uEncodeTime; #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON pEncTimingInfo->uInputCC = uInputCC; pEncTimingInfo->uMotionEstimation = uMotionEstimation; pEncTimingInfo->uFDCT = uFDCT; pEncTimingInfo->uQRLE = uQRLE; pEncTimingInfo->uDecodeFrame = uDecodeFrame; pEncTimingInfo->uZeroingBuffer = uZeroingBuffer; #endif // } DETAILED_ENCODE_TIMINGS_ON EC->uStatFrameCount++; } #endif // } #if defined(ENCODE_TIMINGS_ON) || defined(DETAILED_ENCODE_TIMINGS_ON) /* #ifdef REUSE_DECODE CompandedFrame.Address = (unsigned char*) lpicComp->lpOutput; CompandedFrame.PDecoderInstInfo = PDecoderInstInfo; CompandedFrame.FrameNumber = PFrmHdr->FrameNumber; #endif */ #if ELAPSED_ENCODER_TIME StopElapsed (); Elapsed = ReadElapsed () / 4L; Sample = ReadSample () / 4L; #if 01 DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: "%ld,%ld us\r\n", _fx_, Elapsed, Sample)); #else DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Elapsed time to encode frame: %ld us\r\n", _fx_, Elapsed)); #if SAMPLE_RGBCONV_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Time to convert RGB24 to YUV9: %ld us\r\n", _fx_, Sample)); #endif #if SAMPLE_MOTION_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Time to do motion estimation: %ld us\r\n", _fx_, Sample)); #endif #if SAMPLE_ENCBLK_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Time to encode block layer: %ld us\r\n", _fx_, Sample)); #endif #if SAMPLE_ENCMBLK_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Time to encode macroblock layer: %ld us\r\n", _fx_, Sample)); #endif #if SAMPLE_ENCVLC_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Time to encode VLC: %ld us\r\n", _fx_, Sample)); #endif #if SAMPLE_COMPAND_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Time to decode companded image: %ld us\r\n", _fx_, Sample)); #endif #endif TotalElapsed += Elapsed; TotalSample += Sample; TimedIterations++; #endif done: // GlobalUnlock(lpCompInst->hEncoderInst); #ifdef FORCE_ADVANCED_OPTIONS_ON // { FORCE_ADVANCED_OPTIONS_ON // Force advanced options for testing if (!(lpicComp->dwFlags & ICCOMPRESS_KEYFRAME)) lpicComp->lFrameNum /= 5; #endif // } FORCE_ADVANCED_OPTIONS_ON #ifdef USE_MMX // { USE_MMX if (MMX_Enabled) { __asm { _emit 0x0f _emit 0x77 // emms } } #endif // } USE_MMX return ret; } /**************************************************************************** * @doc INTERNAL H263FUNC * * @func UN | FindNewQuant | This function computes the GQUANT value to * use for a GOB. * * @parm T_H263EncoderCatalog * | EC | Specifies a pointer to the encoder * catalog (global encoder state). * * @parm UN | gquant_prev | Specifies the GQUANT value of the previous GOB. * * @parm UN | uCumFrmSize | Specifies the cumulated size of the previous GOBs. * * @parm UN | GOB | Specifies the number of the GOB to find a new quantizer * for. * * @parm U8 | u8QPMax | Specifies the maximum GQUANT value for the GOB. It * is always set to 31. * * @parm U8 | u8QPMin | Specifies the minimum GQUANT value for the GOB. It * is typically 1 when compressing at high quality, or 15 at low quality. * * @parm BOOL | bBitRateControl | If set to TRUE, the new value for GQUANT * is computed to achieve a target bitrate. * * @parm BOOL | bGOBoverflowWarning | If set to TRUE, the previous GQUANT was * tool low and could potentially generate a buffer overflow. * * @rdesc The GQUANT value. * * @xref ***************************************************************************/ UN FindNewQuant( T_H263EncoderCatalog *EC, UN gquant_prev, UN uCumFrmSize, UN GOB, U8 u8QPMax, U8 u8QPMin, BOOL bBitRateControl, BOOL bGOBoverflowWarning ) { FX_ENTRY("FindNewQuant"); I32 gquant_delta; I32 gquant; if (bBitRateControl == ON) { // Check out if some GOBs have been arbitrary forced to be Intra coded. This always // returns TRUE for an I-frame, and FALSE for all other frame types since this can only // return TRUE for predicted frames when the error resiliency mode is ON, and we never // use this mode. if (IsIntraCoded(EC,GOB) && GOB) gquant = CalcMBQUANT(&(EC->BRCState), EC->uBitUsageProfile[GOB], EC->uBitUsageProfile[EC->NumMBRows], uCumFrmSize,INTRAPIC); else gquant = CalcMBQUANT(&(EC->BRCState), EC->uBitUsageProfile[GOB], EC->uBitUsageProfile[EC->NumMBRows], uCumFrmSize, EC->PictureHeader.PicCodType); EC->uBitUsageProfile[GOB] = uCumFrmSize; // Make sure we don't exceed the maximum quantizer value if (gquant > u8QPMax) gquant = u8QPMax; DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, ("%s: Bitrate controller enabled for GOB #%ld (uCumFrmSize = %ld bits and gquant_prev = %ld), setting gquant = %ld (min and max were %ld and %ld)\r\n", _fx_, GOB, uCumFrmSize << 3, gquant_prev, gquant, u8QPMin, u8QPMax)); } else { // No bitrate control. Use the picture quantizer value for this GOB gquant = EC->PictureHeader.PQUANT; DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, ("%s: Bitrate controller disabled for GOB #%ld (uCumFrmSize = %ld bits and gquant_prev = %ld), setting gquant = %ld (min and max were %ld and %ld)\r\n", _fx_, GOB, uCumFrmSize << 3, gquant_prev, gquant, u8QPMin, u8QPMax)); } // Make sure we're not below the minimum quantizer value if (gquant < u8QPMin) gquant = u8QPMin; // Limit the amount that GQUANT can change from frame to frame gquant_delta = gquant - gquant_prev; // Increase the QP value if there is danger of buffer overflow if (!bGOBoverflowWarning) { // There's no overflow warning, but we don't want the quantizer value to // fluctuate too much from GOB to GOB if (gquant_delta > 4L) { DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, (" %s: Limiting amount of increase for GOB #%ld to 4, changing gquant from %ld to %ld\r\n", _fx_, GOB, gquant, clampQP(gquant_prev + 4L))); gquant = gquant_prev + 4L; } else if (gquant_delta < -2L) { DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, (" %s: Limiting amount of decrease for GOB #%ld to -2, changing gquant from %ld to %ld\r\n", _fx_, GOB, gquant, clampQP(gquant_prev - 2L))); gquant = gquant_prev - 2L; } } else { // There's a risk of overflow - arbitrarily raise the value of the quantizer if necessary if (gquant_delta < 4L) { DEBUGMSG(ZONE_BITRATE_CONTROL_DETAILS, (" %s: Danger of overflow for GOB #%ld, changing gquant from %ld to %ld\r\n", _fx_, GOB, gquant, clampQP(gquant_prev + 4L))); gquant = gquant_prev + 4L; } } return clampQP(gquant); } /******************************************************************************* H263TermEncoderInstance -- This function frees the space allocated for an instance of the H263 encoder. *******************************************************************************/ LRESULT H263TermEncoderInstance(LPCODINST lpInst) { LRESULT ret; U8 BIGG * P32Inst; T_H263EncoderCatalog FAR * EC; FX_ENTRY("H263TermEncoderInstance") #if DUMPFILE _lclose (dmpfil); #endif #if ELAPSED_ENCODER_TIME if (TimedIterations == 0) TimedIterations = 10000000; TotalElapsed /= TimedIterations; TotalSample /= TimedIterations; #if 01 DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: "%ld,%ld us\r\n", _fx_, TotalElapsed, TotalSample)); #else DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Average elapsed time to encode frame: %ld us\r\n", _fx_, TotalElapsed)); #if SAMPLE_RGBCONV_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Average time to convert RGB24 to YUV9: %ld us\r\n", _fx_, TotalSample)); #endif #if SAMPLE_MOTION_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Average time to do motion estimation: %ld us\r\n", _fx_, TotalSample)); #endif #if SAMPLE_ENCBLK_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Average time to encode block layer: %ld us\r\n", _fx_, TotalSample)); #endif #if SAMPLE_ENCMBLK_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Average time to encode macroblock layer: %ld us\r\n", _fx_, TotalSample)); #endif #if SAMPLE_ENCVLC_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Average time to encode VLC: %ld us\r\n", _fx_, TotalSample)); #endif #if SAMPLE_COMPAND_TIME DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Average time to decode companded image: %ld us\r\n", _fx_, TotalSample)); #endif #endif #endif // Check instance pointer if (!lpInst) return ICERR_ERROR; if(lpInst->Initialized == FALSE) { ERRORMESSAGE(("%s: Uninitialized instance\r\n", _fx_)); ret = ICERR_OK; goto done; } lpInst->Initialized = FALSE; // lpInst->EncoderInst = (LPVOID)GlobalLock(lpInst->hEncoderInst); lpInst->EncoderInst = lpInst->hEncoderInst; P32Inst = (U8 *) ((((U32) lpInst->EncoderInst) + (sizeof(T_MBlockActionStream) - 1)) & ~(sizeof(T_MBlockActionStream) - 1)); EC = ((T_H263EncoderCatalog *) P32Inst); // Check encoder catalog pointer if (!EC) return ICERR_ERROR; if (lpInst->Configuration.bRTPHeader) H263RTP_TermBsInfoStream(EC); #ifdef ENCODE_STATS OutputQuantStats("encstats.txt"); OutputPSNRStats("encstats.txt"); OutputFrameSizeStats("encstats.txt"); #endif /* ENCODE_STATS */ #ifdef LOG_ENCODE_TIMINGS_ON // { LOG_ENCODE_TIMINGS_ON if (EC->pEncTimingInfo) OutputEncodeTimingStatistics("c:\\encode.txt", EC->pEncTimingInfo); #endif // } LOG_ENCODE_TIMINGS_ON ret = H263TermColorConvertor(EC->pDecInstanceInfo); if (ret != ICERR_OK) goto done; #if defined(DECODE_TIMINGS_ON) || defined(DETAILED_DECODE_TIMINGS_ON) // { #if defined(DECODE_TIMINGS_ON) || defined(DETAILED_DECODE_TIMINGS_ON) ret = H263TermDecoderInstance(EC->pDecInstanceInfo, FALSE); #else // }{ #if defined(DECODE_TIMINGS_ON) || defined(DETAILED_DECODE_TIMINGS_ON) ret = H263TermDecoderInstance(EC->pDecInstanceInfo); #endif // } #if defined(DECODE_TIMINGS_ON) || defined(DETAILED_DECODE_TIMINGS_ON) if (ret != ICERR_OK) goto done; // Free virtual memory VirtualFree(EC->pI8_MBRVS_BLuma,0,MEM_RELEASE); #ifdef TRACK_ALLOCATIONS // Track memory allocation RemoveName((unsigned int)EC->pI8_MBRVS_BLuma); #endif VirtualFree(EC->pI8_MBRVS_BChroma,0,MEM_RELEASE); #ifdef TRACK_ALLOCATIONS // Track memory allocation RemoveName((unsigned int)EC->pI8_MBRVS_BChroma); #endif // No matter how many sparse pages we committed during encoding, // the whole memory block is released with these calls. // Documentation on VirtualFree() says the individual pages must // first be decommitted, but this is not correct, according // to Jeffrey R. Richter // GlobalUnlock(lpInst->hEncoderInst); // GlobalFree(lpInst->hEncoderInst); VirtualFree(lpInst->hEncoderInst,0,MEM_RELEASE); #ifdef TRACK_ALLOCATIONS // Track memory allocation RemoveName((unsigned int)lpInst->hEncoderInst); #endif ret = ICERR_OK; done: return ret; } /************************************************************************ * * GetEncoderOptions * * Get the options, saving them in the catalog *************************************************************************/ static void GetEncoderOptions(T_H263EncoderCatalog * EC) { /* Default Options */ #ifdef FORCE_ADVANCED_OPTIONS_ON // { FORCE_ADVANCED_OPTIONS_ON // Force PB-Frames for testing EC->u8EncodePBFrame = OFF; // Force UMV for testing EC->PictureHeader.UMV = ON; // Force SAC for testing EC->PictureHeader.SAC = ON; // Force AP for testing EC->PictureHeader.AP = ON; #else // }{ FORCE_ADVANCED_OPTIONS_ON EC->u8EncodePBFrame = FALSE; EC->PictureHeader.UMV = OFF; EC->PictureHeader.SAC = OFF; EC->PictureHeader.AP = OFF; #endif // } FORCE_ADVANCED_OPTIONS_ON #ifdef USE_MMX // { USE_MMX MMX_Enabled = MMxVersion; #endif // } USE_MMX #ifdef H263P EC->bH263Plus = FALSE; EC->PictureHeader.DeblockingFilter = OFF; EC->PictureHeader.ImprovedPB = OFF; #endif EC->bUseINISettings = 0; // Clear option override. return; } /* end GetEncoderOptions() */ /************************************************************* * Name: encodeFrameHeader * Description: Write out the PB-frame header to the bit stream. ************************************************************/ static void encodeFrameHeader( T_H263EncoderCatalog * EC, U8 ** ppCurBitStream, U8 * pBitOffset, BOOL PBframe ) { U8 temp=0; #ifdef H263P BOOL bUseH263PlusOptions = FALSE; #endif // Picture start code PutBits(FIELDVAL_PSC, FIELDLEN_PSC, ppCurBitStream, pBitOffset); // TR : Temporal reference PutBits(EC->PictureHeader.TR, FIELDLEN_TR, ppCurBitStream, pBitOffset); // PTYPE : bits 1-2 PutBits(0x2, FIELDLEN_PTYPE_CONST, ppCurBitStream, pBitOffset); // PTYPE : bit 3 split screen indicator PutBits(EC->PictureHeader.Split, FIELDLEN_PTYPE_SPLIT, ppCurBitStream, pBitOffset); // PTYPE : bit 4 document camera indicator PutBits(EC->PictureHeader.DocCamera, FIELDLEN_PTYPE_DOC, ppCurBitStream, pBitOffset); // PTYPE : bit 5 freeze picture release PutBits(EC->PictureHeader.PicFreeze, FIELDLEN_PTYPE_RELEASE, ppCurBitStream, pBitOffset); #ifdef H263P if ((EC->FrameSz == CUSTOM) || (EC->PictureHeader.DeblockingFilter == ON) || (EC->PictureHeader.PB == ON && EC->PictureHeader.ImprovedPB == ON) // other supported H.263+ options ) { // PTYPE : bits 6-8 extended PTYPE flag enum FrameSize tmpFrameSz = EPTYPE; bUseH263PlusOptions = TRUE; // at least one H.263+ optional mode requested PutBits(tmpFrameSz, FIELDLEN_PTYPE_SRCFORMAT, ppCurBitStream, pBitOffset); } else { // PTYPE : bits 6-8 source format PutBits(EC->FrameSz, FIELDLEN_PTYPE_SRCFORMAT, ppCurBitStream, pBitOffset); } #else // PTYPE : bits 6-8 source format PutBits(EC->FrameSz, FIELDLEN_PTYPE_SRCFORMAT, ppCurBitStream, pBitOffset); #endif // PTYPE : bit 9 picture coding type PutBits(EC->PictureHeader.PicCodType, FIELDLEN_PTYPE_CODINGTYPE, ppCurBitStream, pBitOffset); // PTYPE : bit 10 UMV PutBits(EC->PictureHeader.UMV, FIELDLEN_PTYPE_UMV, ppCurBitStream, pBitOffset); // PTYPE : bit 11 SAC PutBits(EC->PictureHeader.SAC, FIELDLEN_PTYPE_SAC, ppCurBitStream, pBitOffset); // PTYPE : bit 12 advanced prediction mode PutBits(EC->PictureHeader.AP, FIELDLEN_PTYPE_AP, ppCurBitStream, pBitOffset); // PTYPE : bit 13 PB-frames mode PutBits(EC->PictureHeader.PB, FIELDLEN_PTYPE_PB, ppCurBitStream, pBitOffset); #ifdef H263P // EPTYPE : 18 bits if (bUseH263PlusOptions) { // EPTYPE : bits 1-3 source format PutBits(EC->FrameSz, FIELDLEN_EPTYPE_SRCFORMAT, ppCurBitStream, pBitOffset); // EPTYPE : bit 4 custom PCF PutBits(EC->PictureHeader.CustomPCF, FIELDLEN_EPTYPE_CPCF, ppCurBitStream, pBitOffset); // EPTYPE : bit 5 advanced intra coding mode PutBits(EC->PictureHeader.AdvancedIntra, FIELDLEN_EPTYPE_AI, ppCurBitStream, pBitOffset); // EPTYPE : bit 6 deblocking filter mode PutBits(EC->PictureHeader.DeblockingFilter, FIELDLEN_EPTYPE_DF, ppCurBitStream, pBitOffset); // EPTYPE : bit 7 slice structured mode PutBits(EC->PictureHeader.SliceStructured, FIELDLEN_EPTYPE_SS, ppCurBitStream, pBitOffset); // EPTYPE : bit 8 improved PB-frame mode PutBits((EC->PictureHeader.PB == ON && EC->PictureHeader.ImprovedPB), FIELDLEN_EPTYPE_IPB, ppCurBitStream, pBitOffset); // EPTYPE : bit 9 back-channel operation mode PutBits(EC->PictureHeader.BackChannel, FIELDLEN_EPTYPE_BCO, ppCurBitStream, pBitOffset); // EPTYPE : bit 10 SNR and spatial scalability mode PutBits(EC->PictureHeader.Scalability, FIELDLEN_EPTYPE_SCALE, ppCurBitStream, pBitOffset); // EPTYPE : bit 11 true B-frame mode PutBits(EC->PictureHeader.TrueBFrame, FIELDLEN_EPTYPE_TB, ppCurBitStream, pBitOffset); // EPTYPE : bit 12 reference-picture resampling mode PutBits(EC->PictureHeader.RefPicResampling, FIELDLEN_EPTYPE_RPR, ppCurBitStream, pBitOffset); // EPTYPE : bit 13 reduced-resolution update mode PutBits(EC->PictureHeader.RedResUpdate, FIELDLEN_EPTYPE_RRU, ppCurBitStream, pBitOffset); // EPTYPE : bit 14-18 reserved PutBits(0x1, FIELDLEN_EPTYPE_CONST, ppCurBitStream, pBitOffset); } if (EC->FrameSz == CUSTOM) { // CSFMT : bit 1-4 pixel aspect ratio code // TODO. For now, force to CIF PutBits(0x2, FIELDLEN_CSFMT_PARC, ppCurBitStream, pBitOffset); // CSFMT : bits 5-13 frame width indication PutBits((EC->uActualFrameWidth >> 2) - 1, FIELDLEN_CSFMT_FWI, ppCurBitStream, pBitOffset); // CSFMT : bit 14 "1" to avoid start code emulation PutBits(0x1, FIELDLEN_CSFMT_CONST, ppCurBitStream, pBitOffset); // CSFMT : bits 15-23 frame height indication PutBits((EC->uActualFrameHeight >> 2) - 1, FIELDLEN_CSFMT_FHI, ppCurBitStream, pBitOffset); } #endif // PQUANT PutBits(EC->PictureHeader.PQUANT, FIELDLEN_PQUANT, ppCurBitStream, pBitOffset); // CPM PutBits(EC->PictureHeader.CPM, FIELDLEN_CPM, ppCurBitStream, pBitOffset); if (PBframe == TRUE) { // AG:TODO // TRB PutBits(EC->PictureHeader.TRB, FIELDLEN_TRB, ppCurBitStream, pBitOffset); // AG:TODO // DBQUANT PutBits(EC->PictureHeader.DBQUANT, FIELDLEN_DBQUANT, ppCurBitStream, pBitOffset); #ifdef COUNT_BITS EC->Bits.PictureHeader += FIELDLEN_TRB + FIELDLEN_DBQUANT; #endif } // PEI PutBits(EC->PictureHeader.PEI, FIELDLEN_PEI, ppCurBitStream, pBitOffset); #ifdef COUNT_BITS EC->Bits.PictureHeader += FIELDLEN_PSC + FIELDLEN_TR + FIELDLEN_PTYPE_CONST + FIELDLEN_PTYPE_SPLIT + FIELDLEN_PTYPE_DOC + FIELDLEN_PTYPE_RELEASE + FIELDLEN_PTYPE_SRCFORMAT + FIELDLEN_PTYPE_CODINGTYPE + FIELDLEN_PTYPE_UMV + FIELDLEN_PTYPE_SAC + FIELDLEN_PTYPE_AP + FIELDLEN_PTYPE_PB + FIELDLEN_PQUANT + FIELDLEN_CPM + FIELDLEN_PEI; #endif } /************************************************************* * Name: InitMEState * Description: Initialize the MB action stream for the ME * state engine. ************************************************************/ void InitMEState(T_H263EncoderCatalog *EC, ICCOMPRESS *lpicComp, T_CONFIGURATION *pConfiguration) { register unsigned int i; U8 u8FirstMEState; FX_ENTRY("InitMEState") // TODO: The FirstMEState initialization can be avoided // for each compress by either adding a parameter to the // motion estimator signalling key frame, or by not calling // motion estimation on intra frames, and resetting MBType, // CodedBlocks ourselves. if (EC->PictureHeader.PicCodType == INTRAPIC) { for(i=0; i < EC->NumMBs; i++) { // Clear the intercode count. (EC->pU8_MBlockActionStream[i]).InterCodeCnt = (i & 0xf); // For the motion estimator, this field must be set to force // intra blocks for intra frames. (EC->pU8_MBlockActionStream[i]).FirstMEState = ForceIntra; } *(lpicComp->lpdwFlags) |= AVIIF_KEYFRAME; lpicComp->dwFlags |= ICCOMPRESS_KEYFRAME; // Store that this frame was intra coded. Used during the initialization // of the ME state for the next frame. EC->bPrevFrameIntra = TRUE; } else // Picture Coding type is INTERPIC { /* * The FirstMEState element in each MB structure must be set * to indicate its position in the frame. This is used by the * motion estimator. */ /* * Check for AP or UMV modes. When these mode is signalled, motion vectors are * allowed to point outside the picture. */ /* We also need to perform the initialization if the previous frame was an intra frame! (JM) */ if (EC->bPrevFrameIntra || EC->PictureHeader.AP != EC->prevAP || EC->PictureHeader.UMV != EC->prevUMV #ifdef H263P || EC->PictureHeader.DeblockingFilter != EC->prevDF #endif ) { if( (EC->PictureHeader.UMV == ON) || (EC->PictureHeader.AP == ON) #ifdef H263P || (EC->PictureHeader.DeblockingFilter == ON) #endif ) { // Set ME state central blocks. for(i=0; i < EC->NumMBs; i++) (EC->pU8_MBlockActionStream[i]).FirstMEState = CentralBlock; } else // No AP or UMV option. { // Set upper left corner (EC->pU8_MBlockActionStream[0]).FirstMEState = UpperLeft; // Set ME state for top edge. for(i=1; i < EC->NumMBPerRow; i++) (EC->pU8_MBlockActionStream[i]).FirstMEState = UpperEdge; // Set upper right corner. (EC->pU8_MBlockActionStream[ EC->NumMBPerRow - 1 ]).FirstMEState = UpperRight; // Set ME state for central blocks. for(i=EC->NumMBPerRow; i < EC->NumMBs; i++) (EC->pU8_MBlockActionStream[i]).FirstMEState = CentralBlock; // Set ME state for bottom edge. for(i= (EC->NumMBs - EC->NumMBPerRow); i < EC->NumMBs; i++) (EC->pU8_MBlockActionStream[i]).FirstMEState = LowerEdge; // Set ME state for left edge for(i= EC->NumMBPerRow ; i < EC->NumMBs; i += EC->NumMBPerRow) (EC->pU8_MBlockActionStream[i]).FirstMEState = LeftEdge; // Set ME state for right edge. for(i= 2 * EC->NumMBPerRow - 1 ; i < EC->NumMBs; i += EC->NumMBPerRow) (EC->pU8_MBlockActionStream[i]).FirstMEState = RightEdge; // Bottom left corner. (EC->pU8_MBlockActionStream[EC->NumMBs - EC->NumMBPerRow]).FirstMEState = LowerLeft; // Bottom right corner. (EC->pU8_MBlockActionStream[EC->NumMBs - 1]).FirstMEState = LowerRight; } // end of else (not UMV) } // end of if (bPrevFrameIntra || prevAP != AP || prevUMV != UMV || prevDF != DF) // Clear key frame flag. *(lpicComp->lpdwFlags) &= ~AVIIF_KEYFRAME; lpicComp->dwFlags &= ~ICCOMPRESS_KEYFRAME; // Store that this frame was not intra coded. Used during the initialization // of the ME state for the next frame. EC->bPrevFrameIntra = FALSE; } // RTP stuff which needs to be done for every frame (?) if (pConfiguration->bEncoderResiliency && pConfiguration->unPacketLoss) { //Chad intra GOB // Of course unPacketLoss is non-zero. Why are we checking it here if (pConfiguration->unPacketLoss > 0) { //Chad INTRA GOB EC->uNumberForcedIntraMBs = ((EC->NumMBs * pConfiguration->unPacketLoss) + 50) / 100; EC->uNumberForcedIntraMBs = (EC->uNumberForcedIntraMBs+EC->NumMBPerRow-1) / EC->NumMBPerRow * EC->NumMBPerRow; } if (EC->uNumberForcedIntraMBs > 0) { /* Force all the MBs in a GOB to intra. */ for ( i = 0 ; i < EC->uNumberForcedIntraMBs ; i++, EC->uNextIntraMB++) { // Reset it to the first row when we reach the end. if (EC->uNextIntraMB >= EC->NumMBs) { EC->uNextIntraMB = 0; } (EC->pU8_MBlockActionStream[EC->uNextIntraMB]).FirstMEState = ForceIntra; } } if (pConfiguration->bDisallowAllVerMVs) { /* Walk thru all the FirstMEStateME settings turning off Vertical. */ for(i=0; i < EC->NumMBs; i++) { u8FirstMEState = (EC->pU8_MBlockActionStream[i]).FirstMEState; switch (u8FirstMEState) { case ForceIntra: break; case UpperLeft: case LeftEdge: case LowerLeft: u8FirstMEState = NoVertLeftEdge; break; case UpperEdge: case CentralBlock: case LowerEdge: u8FirstMEState = NoVertCentralBlock; break; case UpperRight: case RightEdge: case LowerRight: u8FirstMEState = NoVertRightEdge; break; case NoVertLeftEdge: case NoVertCentralBlock: case NoVertRightEdge: ASSERT(0); /* It should work, but why was this already on */ break; default: DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Warning: Unexpected FirstMEState\r\n", _fx_)); break; } (EC->pU8_MBlockActionStream[i]).FirstMEState = u8FirstMEState; } } else if (pConfiguration->bDisallowPosVerMVs) { /* Walk thru all the FirstMEState settings turning off Positive Vertical */ for(i=0; i < EC->NumMBs; i++) { u8FirstMEState = (EC->pU8_MBlockActionStream[i]).FirstMEState; switch (u8FirstMEState) { case ForceIntra: case LowerLeft: case LowerEdge: case LowerRight: break; case UpperLeft: u8FirstMEState = NoVertLeftEdge; break; case LeftEdge: u8FirstMEState = LowerLeft; break; case UpperEdge: u8FirstMEState = NoVertCentralBlock; break; case CentralBlock: u8FirstMEState = LowerEdge; break; case UpperRight: u8FirstMEState = NoVertRightEdge; break; case RightEdge: u8FirstMEState = LowerRight; break; case NoVertLeftEdge: case NoVertCentralBlock: case NoVertRightEdge: ASSERT(0); /* It should work, but why was this already on */ break; default: DEBUGMSG(ZONE_ENCODE_DETAILS, ("%s: Warning: Unexpected FirstMEState\r\n", _fx_)); break; } (EC->pU8_MBlockActionStream[i]).FirstMEState = u8FirstMEState; } /* for */ } /* else if... */ } /* if (pConfiguration->bEncoderResiliency) */ } // end of InitMEState() #ifdef USE_MMX // { USE_MMX /************************************************************* * Name: Check_InterCodeCnt_MMX * Description: Track inter code count for macro blocks * for forced update. Called before Motion * Estimation. ************************************************************/ static void Check_InterCodeCnt_MMX(T_H263EncoderCatalog *EC, U32 StartingMB) { register T_MBlockActionStream *pCurrMB; T_MBlockActionStream *pLastMBPlus1; pCurrMB = &(EC->pU8_MBlockActionStream[StartingMB]); pLastMBPlus1 = &(EC->pU8_MBlockActionStream[StartingMB + EC->NumMBPerRow]); for(; pCurrMB < pLastMBPlus1; pCurrMB++, StartingMB++) { // Check to see if it's time to refresh this block. if(pCurrMB->InterCodeCnt > 132) { pCurrMB->CodedBlocks |= 0x80; // InterCodeCnt is reset in GOB_VLC_WriteBS() in e3mbenc.cpp */ } } } #endif // } USE_MMX /************************************************************* * Name: Check_InterCodeCnt * Description: Track inter code count for macro blocks * for forced update. Called after Motion * Estimation. ************************************************************/ static void Check_InterCodeCnt(T_H263EncoderCatalog *EC, U32 StartingMB) { register T_MBlockActionStream *pCurrMB; T_MBlockActionStream *pLastMBPlus1; pCurrMB = &(EC->pU8_MBlockActionStream[StartingMB]); pLastMBPlus1 = &(EC->pU8_MBlockActionStream[StartingMB + EC->NumMBPerRow]); for(; pCurrMB < pLastMBPlus1; pCurrMB++, StartingMB++) { // Check to see if it's time to refresh this block. if(pCurrMB->InterCodeCnt > 132) { if (pCurrMB->BlockType == INTER4MV) { pCurrMB->BlkY1.PHMV = pCurrMB->BlkY2.PHMV = pCurrMB->BlkY3.PHMV = pCurrMB->BlkY4.PHMV = (pCurrMB->BlkY1.PHMV+pCurrMB->BlkY2.PHMV+pCurrMB->BlkY3.PHMV+pCurrMB->BlkY4.PHMV+2) >> 2; pCurrMB->BlkY1.PVMV = pCurrMB->BlkY2.PVMV = pCurrMB->BlkY3.PVMV = pCurrMB->BlkY4.PVMV = (pCurrMB->BlkY1.PVMV+pCurrMB->BlkY2.PVMV+pCurrMB->BlkY3.PVMV+pCurrMB->BlkY4.PVMV+2) >> 2; } pCurrMB->BlockType = INTRABLOCK; pCurrMB->CodedBlocks |= 0x3f; // InterCodeCnt is reset in GOB_Q_VLC_WriteBS() in e3mbenc.cpp */ } } } /************************************************************* * Name: calcGOBChromaVectors * Description: Compute chroma motion vectors ************************************************************/ static void calcGOBChromaVectors( T_H263EncoderCatalog *EC, U32 StartingMB, T_CONFIGURATION *pConfiguration ) { register T_MBlockActionStream *pCurrMB; T_MBlockActionStream *pLastMBPlus1; char HMV, VMV; pCurrMB = &(EC->pU8_MBlockActionStream[StartingMB]); pLastMBPlus1 = &(EC->pU8_MBlockActionStream[StartingMB + EC->NumMBPerRow]); for( ; pCurrMB < pLastMBPlus1; pCurrMB++, StartingMB++) { // The ME should generate MV indices in the range // of [-32,31]. // ASSERT( (pCurrMB->BlkY1.PHMV >= -32) && // (pCurrMB->BlkY1.PHMV <= 31) ) // ASSERT( (pCurrMB->BlkY1.PVMV >= -32) && // (pCurrMB->BlkY1.PVMV <= 31) ) // ASSERT( (pCurrMB->BlkY2.PHMV >= -32) && // (pCurrMB->BlkY2.PHMV <= 31) ) // ASSERT( (pCurrMB->BlkY2.PVMV >= -32) && // (pCurrMB->BlkY2.PVMV <= 31) ) // ASSERT( (pCurrMB->BlkY3.PHMV >= -32) && // (pCurrMB->BlkY3.PHMV <= 31) ) // ASSERT( (pCurrMB->BlkY3.PVMV >= -32) && // (pCurrMB->BlkY3.PVMV <= 31) ) // ASSERT( (pCurrMB->BlkY4.PHMV >= -32) && // (pCurrMB->BlkY4.PHMV <= 31) ) // ASSERT( (pCurrMB->BlkY4.PVMV >= -32) && // (pCurrMB->BlkY4.PVMV <= 31) ) #ifdef _DEBUG if (pConfiguration->bEncoderResiliency && pConfiguration->unPacketLoss) { if (pConfiguration->bDisallowAllVerMVs) { ASSERT(pCurrMB->BlkY1.PVMV == 0); ASSERT(pCurrMB->BlkY2.PVMV == 0); ASSERT(pCurrMB->BlkY3.PVMV == 0); ASSERT(pCurrMB->BlkY4.PVMV == 0); } else if (pConfiguration->bDisallowPosVerMVs) { ASSERT(pCurrMB->BlkY1.PVMV <= 0); ASSERT(pCurrMB->BlkY2.PVMV <= 0); ASSERT(pCurrMB->BlkY3.PVMV <= 0); ASSERT(pCurrMB->BlkY4.PVMV <= 0); } } #endif /* _DEBUG */ // TODO: Don't calculate chroma vectors if this is not a P-frame // inside a PB frame and it's an INTRA MB or inter code count // exceeded 132. if(pCurrMB->BlockType != INTER4MV) { HMV = QtrPelToHalfPel[pCurrMB->BlkY1.PHMV+64]; VMV = QtrPelToHalfPel[pCurrMB->BlkY1.PVMV+64]; } else // 4 MV's per block. { HMV = SixteenthPelToHalfPel[ pCurrMB->BlkY1.PHMV + pCurrMB->BlkY2.PHMV + pCurrMB->BlkY3.PHMV + pCurrMB->BlkY4.PHMV + 256 ]; VMV = SixteenthPelToHalfPel[ pCurrMB->BlkY1.PVMV + pCurrMB->BlkY2.PVMV + pCurrMB->BlkY3.PVMV + pCurrMB->BlkY4.PVMV + 256 ]; } pCurrMB->BlkU.PHMV = HMV; pCurrMB->BlkU.PVMV = VMV; pCurrMB->BlkV.PHMV = HMV; pCurrMB->BlkV.PVMV = VMV; pCurrMB->BlkU.B4_7.PastRef = EC->pU8_PrevFrm_YPlane + pCurrMB->BlkU.BlkOffset + (VMV>>1)*PITCH + (HMV>>1); pCurrMB->BlkV.B4_7.PastRef = EC->pU8_PrevFrm_YPlane + pCurrMB->BlkV.BlkOffset + (VMV>>1)*PITCH + (HMV>>1); // The increment of pCurrMB->InterCodeCnt is now done // in void GOB_VLC_WriteBS and void GOB_Q_RLE_VLC_WriteBS // When it was incremented here, it was always incremented, // no matter whether coefficients were coded or not. } // end of for loop } // end of /************************************************************* * Name: calcBGOBChromaVectors * Description: Compute forward and backward chroma motion vectors for the * B-frame GOB starting at MB number "StartingMB". Luma motion * vectors are biased by 0x60. Chroma motion vectors are also * biased by 0x60. ************************************************************/ static void calcBGOBChromaVectors( T_H263EncoderCatalog *EC, const U32 StartingMB ) { register T_MBlockActionStream *pCurrMB; register I8 HMVf, HMVb, VMVf, VMVb; for(pCurrMB = &(EC->pU8_MBlockActionStream[StartingMB]); pCurrMB < &(EC->pU8_MBlockActionStream[StartingMB + EC->NumMBPerRow]); pCurrMB++) { // Luma block motion vectors HMVf = QtrPelToHalfPel[pCurrMB->BlkY1.BestMV.HMVf-0x60+64]+0x60; HMVb = QtrPelToHalfPel[pCurrMB->BlkY1.BestMV.HMVb-0x60+64]+0x60; VMVf = QtrPelToHalfPel[pCurrMB->BlkY1.BestMV.VMVf-0x60+64]+0x60; VMVb = QtrPelToHalfPel[pCurrMB->BlkY1.BestMV.VMVb-0x60+64]+0x60; pCurrMB->BlkU.BestMV.HMVf = HMVf; pCurrMB->BlkU.BestMV.HMVb = HMVb; pCurrMB->BlkU.BestMV.VMVf = VMVf; pCurrMB->BlkU.BestMV.VMVb = VMVb; pCurrMB->BlkV.BestMV.HMVf = HMVf; pCurrMB->BlkV.BestMV.HMVb = HMVb; pCurrMB->BlkV.BestMV.VMVf = VMVf; pCurrMB->BlkV.BestMV.VMVb = VMVb; } } /************************************************************* * Name: InitBits ************************************************************/ #ifdef COUNT_BITS static void InitBits(T_H263EncoderCatalog * EC) { EC->Bits.PictureHeader = 0; EC->Bits.GOBHeader = 0; EC->Bits.MBHeader = 0; EC->Bits.DQUANT = 0; EC->Bits.MV = 0; EC->Bits.Coefs = 0; EC->Bits.Coefs_Y = 0; EC->Bits.IntraDC_Y = 0; EC->Bits.Coefs_C = 0; EC->Bits.IntraDC_C = 0; EC->Bits.CBPY = 0; EC->Bits.MCBPC = 0; EC->Bits.Coded = 0; EC->Bits.num_intra = 0; EC->Bits.num_inter = 0; EC->Bits.num_inter4v = 0; } #endif #ifdef COUNT_BITS void InitCountBitFile() { FILE *fp; fp = fopen("bits.txt", "w"); ASSERT(fp != NULL); fclose(fp); } void WriteCountBitFile(T_BitCounts *Bits) { FILE *fp; fp = fopen("bits.txt", "a"); ASSERT(fp != NULL); fprintf(fp, "%8d %8d %8d %8d %8d %8d %8d\n", Bits->PictureHeader, Bits->GOBHeader, Bits->MBHeader, Bits->MV, Bits->Coefs, Bits->CBPY, Bits->MCBPC ); fclose(fp); } #endif #ifdef DEBUG_ENC void trace(char *str) { FILE *fp; fp = fopen("trace.txt", "a"); fprintf(fp, "%s\n", str); fclose(fp); } #endif #ifdef DEBUG_DCT void cnvt_fdct_output(unsigned short *DCTcoeff, int DCTarray[64], int BlockType) { register int i; static int coefforder[64] = { // 0 1 2 3 4 5 6 7 6,38, 4,36,70,100,68,102, // 0 10,46, 8,44,74,104,72,106, // 1 18,50,16,48,82,112,80,114, // 2 14,42,12,40,78,108,76,110, // 3 22,54,20,52,86,116,84,118, // 4 2,34, 0,32,66, 96,64, 98, // 5 26,58,24,56,90,120,88,122, // 6 30,62,28,60,94,124,92,126 // 7 }; static int zigzag[64] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; unsigned int index; for (i = 0; i < 64; i++) { index = (coefforder[i])>>1; if( (i ==0) && ((BlockType & 1) == 1) ) DCTarray[zigzag[i]] = ((int)(DCTcoeff[index])) >> 4 ; else DCTarray[zigzag[i]] = ((int)(DCTcoeff[index] - 0x8000)) >> 4; } } #endif #ifdef LOG_ENCODE_TIMINGS_ON // { LOG_ENCODE_TIMINGS_ON void OutputEncodeTimingStatistics(char * szFileName, ENC_TIMING_INFO * pEncTimingInfo) { FILE * pFile; ENC_TIMING_INFO * pTempEncTimingInfo; ENC_TIMING_INFO etiTemp; int i; int iCount; FX_ENTRY("OutputEncodeTimingStatistics") pFile = fopen(szFileName, "a"); if (pFile == NULL) { ERRORMESSAGE(("%s: Error opening encode stat file\r\n", _fx_)); goto done; } /* Output the detail information */ fprintf(pFile,"\nDetail Timing Information\n"); for ( i = 0, pTempEncTimingInfo = pEncTimingInfo ; i < ENC_TIMING_INFO_FRAME_COUNT ; i++, pTempEncTimingInfo++ ) { fprintf(pFile, "Frame %d Detail Timing Information\n", i); OutputEncTimingDetail(pFile, pTempEncTimingInfo); } /* Compute the total information */ memset(&etiTemp, 0, sizeof(ENC_TIMING_INFO)); iCount = 0; for ( i = 0, pTempEncTimingInfo = pEncTimingInfo ; i < ENC_TIMING_INFO_FRAME_COUNT ; i++, pTempEncTimingInfo++ ) { iCount++; etiTemp.uEncodeFrame += pTempEncTimingInfo->uEncodeFrame; #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON etiTemp.uInputCC += pTempEncTimingInfo->uInputCC; etiTemp.uMotionEstimation += pTempEncTimingInfo->uMotionEstimation; etiTemp.uFDCT += pTempEncTimingInfo->uFDCT; etiTemp.uQRLE += pTempEncTimingInfo->uQRLE; etiTemp.uDecodeFrame += pTempEncTimingInfo->uDecodeFrame; etiTemp.uZeroingBuffer += pTempEncTimingInfo->uZeroingBuffer; #endif // } DETAILED_ENCODE_TIMINGS_ON } if (iCount > 0) { /* Output the total information */ fprintf(pFile,"Total for %d frames\n", iCount); OutputEncTimingDetail(pFile, &etiTemp); /* Compute the average */ etiTemp.uEncodeFrame = (etiTemp.uEncodeFrame + (iCount / 2)) / iCount; #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON etiTemp.uInputCC = (etiTemp.uInputCC + (iCount / 2)) / iCount; etiTemp.uMotionEstimation = (etiTemp.uMotionEstimation + (iCount / 2)) / iCount; etiTemp.uFDCT = (etiTemp.uFDCT + (iCount / 2)) / iCount; etiTemp.uQRLE = (etiTemp.uQRLE + (iCount / 2)) / iCount; etiTemp.uDecodeFrame = (etiTemp.uDecodeFrame + (iCount / 2)) / iCount; etiTemp.uZeroingBuffer = (etiTemp.uZeroingBuffer + (iCount / 2)) / iCount; #endif // } DETAILED_ENCODE_TIMINGS_ON /* Output the average information */ fprintf(pFile,"Average over %d frames\n", iCount); OutputEncTimingDetail(pFile, &etiTemp); } fclose(pFile); done: return; } void OutputEncTimingDetail(FILE * pFile, ENC_TIMING_INFO * pEncTimingInfo) { U32 uOther; U32 uRoundUp; U32 uDivisor; fprintf(pFile, "\tEncode Frame = %10d (%d milliseconds at 90Mhz)\n", pEncTimingInfo->uEncodeFrame, (pEncTimingInfo->uEncodeFrame + 45000) / 90000); uOther = pEncTimingInfo->uEncodeFrame; #ifdef DETAILED_ENCODE_TIMINGS_ON // { DETAILED_ENCODE_TIMINGS_ON /* This is needed because of the integer truncation. */ uDivisor = pEncTimingInfo->uEncodeFrame / 100; // to yield a percent uRoundUp = uDivisor / 2; fprintf(pFile, "\tInputCC = %10d (%2d%%)\n", pEncTimingInfo->uInputCC, (pEncTimingInfo->uInputCC + uRoundUp) / uDivisor); uOther -= pEncTimingInfo->uInputCC; fprintf(pFile, "\tMotionEstimation = %10d (%2d%%)\n", pEncTimingInfo->uMotionEstimation, (pEncTimingInfo->uMotionEstimation + uRoundUp) / uDivisor); uOther -= pEncTimingInfo->uMotionEstimation; fprintf(pFile, "\tFDCT = %10d (%2d%%)\n", pEncTimingInfo->uFDCT, (pEncTimingInfo->uFDCT + uRoundUp) / uDivisor); uOther -= pEncTimingInfo->uFDCT; fprintf(pFile, "\tQRLE = %10d (%2d%%)\n", pEncTimingInfo->uQRLE, (pEncTimingInfo->uQRLE + uRoundUp) / uDivisor); uOther -= pEncTimingInfo->uQRLE; fprintf(pFile, "\tDecodeFrame = %10d (%2d%%)\n", pEncTimingInfo->uDecodeFrame, (pEncTimingInfo->uDecodeFrame + uRoundUp) / uDivisor); uOther -= pEncTimingInfo->uDecodeFrame; fprintf(pFile, "\tZeroingBuffer = %10d (%2d%%)\n", pEncTimingInfo->uZeroingBuffer, (pEncTimingInfo->uZeroingBuffer + uRoundUp) / uDivisor); uOther -= pEncTimingInfo->uZeroingBuffer; fprintf(pFile, "\tOther = %10d (%2d%%)\n", uOther, (uOther + uRoundUp) / uDivisor); #endif // } DETAILED_ENCODE_TIMINGS_ON } #endif // { LOG_ENCODE_TIMINGS_ON