You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4260 lines
148 KiB
4260 lines
148 KiB
#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 <f CalcMBQUANT>
|
|
***************************************************************************/
|
|
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
|