www.pudn.com > coremp4-1.0.zip > mp4_decode.cpp


/***************************************************************************** 
 * 
 * This program is free software ; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version. 
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
 * 
 ***************************************************************************** 
 * 
 * Authors: 
 * 
 *	Andrea	Graziani  (Ag): Original source code (Open Divx Decoder 0.4a). 
 *	Marc	Dukette   (Md) and 
 *	Pedro	Mateu     (Pm) and 
 *	Gabor	Kovacs    (Kg) Heavily modified and optimized code 
 * Michal Bacik    Porting into C++, optimizations 
 * 
 ****************************************************************************/ 
 
#include "Util.h" 
#include "SoftIdct.h" 
#include "profiling.h" 
#include "VideoCodec.h" 
#include "C_buffer.h" 
 
//---------------------------- 
 
#define MPEG4_ERROR_QPEL      0x200 
#define MPEG4_ERROR_INTERLACE 0x201 
#define MPEG4_ERROR_GMC       0x202 
#define MPEG4_ERROR_PARTITIONING 0x205 
 
#define POSX(pos) ((pos) & (MAX_MACROBLOCKS_X-1)) 
#define POSY(pos) ((pos) >> MB_X2) 
 
// maximun picture size in macroblocks (16x16) 
// example MB_X2=6 -> 2^6=64 macroblocks -> 1024 pixels 
 
#define MB_X2 6 
#define MB_Y2 6 
#define MAX_MACROBLOCKS_X (1<>16) 
 
//---------------------------- 
 
                              //macros for fast bit-access in frequently-used locations 
#define GetBits1() ((bits << bitpos++) < 0) 
#define LoadBits() \ 
   if(bitpos >= 8){ \ 
      do{ \ 
         bits = (bits << 8) | *bit_ptr++; \ 
         bitpos -= 8; \ 
      }while(bitpos >= 8); \ 
   } 
 
#define ShowBits(n) ((dword)(bits << bitpos) >> (32-(n))) 
#define FlushBits(n) bitpos += n 
 
//---------------------------- 
 
#if 0 
 
static int Divide(int a, int b){ 
   return ((a>0) ? (a+(b>>1))/b : (a-(b>>1))/b); 
} 
 
#else 
 
                              //optimized division for most common divisors 
                              // (using multiplication by reciprocal in 12.20 fixed-point format) 
 
#define RET_DIV(a, N) return (a*N+524288) >> 20; 
#define RET_SHR(a, N, S) return (a+N) >> S; 
 
static int Divide(int a, int b){ 
   switch(b){ 
   case 1: return a; 
   case 2: RET_SHR(a, 1, 1); 
   case 4: RET_SHR(a, 2, 2); 
   case 8: RET_SHR(a, 4, 3); 
   case 16: RET_SHR(a, 8, 4); 
   case 32: RET_SHR(a, 16, 5); 
   case 3: RET_DIV(a, 349525); 
   case 5: RET_DIV(a, 209715); 
   case 6: RET_DIV(a, 174763); 
   case 7: RET_DIV(a, 149797); 
   case 9: RET_DIV(a, 116509); 
   case 10: RET_DIV(a, 104858); 
   case 11: RET_DIV(a, 95325); 
   case 12: RET_DIV(a, 87382); 
   case 13: RET_DIV(a, 80660); 
   case 14: RET_DIV(a, 74899); 
   case 15: RET_DIV(a, 69905); 
   case 17: RET_DIV(a, 61681); 
   case 18: RET_DIV(a, 58255); 
   case 19: RET_DIV(a, 55189); 
   case 20: RET_DIV(a, 52429); 
   case 21: RET_DIV(a, 49933); 
   case 22: RET_DIV(a, 47663); 
   case 23: RET_DIV(a, 45591); 
   case 24: RET_DIV(a, 43691); 
   case 25: RET_DIV(a, 41943); 
   case 26: RET_DIV(a, 40330); 
   case 27: RET_DIV(a, 38837); 
   case 28: RET_DIV(a, 37450); 
   case 29: RET_DIV(a, 36158); 
   case 30: RET_DIV(a, 34953); 
   case 31: RET_DIV(a, 33825); 
   } 
   if(a>0)                               
      a += b>>1; 
   else 
      a -= b>>1; 
   return a/b; 
} 
 
#endif 
 
//---------------------------- 
 
static dword FindHighestBit(dword mask){ 
 
   int base = 0; 
   if(mask&0xffff0000){ 
      base = 16; 
      mask >>= 16; 
   } 
   if(mask&0x0000ff00){ 
      base += 8; 
      mask >>= 8; 
   } 
   if(mask&0x000000f0){ 
      base += 4; 
      mask >>= 4; 
   } 
   static const int lut[] = {-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3}; 
   return base + lut[mask]; 
} 
 
//---------------------------- 
 
static void Clear32(void* p){ 
   int *i = (int*)p; 
   i[0] = 0; i[1] = 0;  i[2] = 0; i[3] = 0; 
   i[4] = 0; i[5] = 0;  i[6] = 0; i[7] = 0; 
} 
 
//---------------------------- 
//---------------------------- 
 
struct S_bit_reader{ 
   int bits, pos; 
   const byte *ptr, *bitend; 
 
   void Init(const byte *stream, int len){ 
      bits = 0; 
      pos = 32; 
      ptr = stream; 
      bitend = stream+len+6; //adding more just to be safe 
      Flush(0); 
   } 
 
   inline void Flush(int n){ 
      pos += n; 
   } 
 
//---------------------------- 
// n=1..24 (or ..32 after bytealign) 
   inline dword Show(int n) const{ 
      return (dword)(bits << pos) >> (32-n); 
   } 
//---------------------------- 
   inline int Get(int n){ 
      int i = Show(n); 
      Flush(n); 
      return i; 
   } 
 
//---------------------------- 
   inline bool Get1(){ 
      return Get(1); 
   } 
 
   inline int BitsToNextByte() const{ 
      return 8-(pos & 7); 
   } 
   inline bool Eof() const{ 
      return (ptr >= bitend); 
   } 
 
   inline int ShowLarge(int n){ 
      int i = Show(n); 
      i |= *ptr >> (40-n-pos); 
      return i; 
   } 
   inline void Rewind(const byte *p){ 
      pos = 32; 
      ptr = p; 
   } 
   inline void ByteAlign(){ 
      pos = (pos + 7) & ~7; 
   } 
   void Load(); 
 
//---------------------------- 
 
   int GetCodedBlockPatternLuminance(); 
 
   int GetMCodedBlockPatternChrominance_i(); 
 
   int GetMCodedBlockPatternChrominance_p(); 
 
   void GetQuantMatrix(byte m[2*8*8]); 
 
//---------------------------- 
// Get single move value from bitstream. 
   int GetMoveData(int fcode); 
 
//---------------------------- 
// Get movement vector from stream. The vector is stored in stream as delta, so pass in previous value, which is added to read values. 
   int GetMovementVectorPair(int fcode, int prev_vector_pair); 
 
   int GetDCsizeLum(); 
   int GetDCsizeChr(); 
   int GetDCdiff(int dct_dc_size); 
}; 
 
//---------------------------- 
 
void S_bit_reader::Load(){ 
    
   if(pos >= 8){ 
      do{ 
         bits = (bits << 8) | *ptr++; 
         pos -= 8; 
      }while(pos >= 8); 
   }                                 
} 
 
//---------------------------- 
// Meaning of CBPY (Coded Block Pattern-luminance), indicates which blocks within a macroblock contain coded coefficients. 
// First byte encodes bits 0-3 associated with 2x2 luminance (Y) blocks in one 16x16 maxroblock. 
// Second byte encodes number of bits representing this information in the bitstream. 
static const byte tab_coded_block_pattern_luminance[] = {  
   0, 0,  0, 0,  6, 6,   9,6,   8,5,   8,5,   4,5,   4,5, 
   2, 5,  2, 5,  1, 5,   1,5,   0,4,   0,4,   0,4,   0,4,  
   12,4,  12,4,  12,4,  12,4,  10,4,  10,4,  10,4,  10,4, 
   14,4,  14,4,  14,4,  14,4,   5,4,   5,4,   5,4,   5,4, 
   13,4,  13,4,  13,4,  13,4,   3,4,   3,4,   3,4,   3,4,  
   11,4,  11,4,  11,4,  11,4,   7,4,   7,4,   7,4,   7,4  
}; 
 
//---------------------------- 
 
int S_bit_reader::GetCodedBlockPatternLuminance(){ 
                              //max 6 bits 
   int code = Show(6); 
   if(code < 2) 
      return -1; 
   if(code >= 0x30){ 
      Flush(2); 
      return 0xf; 
   } 
   code *= 2; 
   Flush(tab_coded_block_pattern_luminance[code+1]); 
   return tab_coded_block_pattern_luminance[code]; 
} 
 
//---------------------------- 
 
static const byte MCBPCtabIntra[] = { 
   0, 0, //-1,0 
   20,6,  36, 6,  52,6,  4,4,    4, 4,   4, 4,  
   4, 4,  19, 3,  19,3,  19,3,  19, 3,  19, 3,  
   19,3,  19, 3,  19,3,  35,3,  35, 3,  35, 3,  
   35,3,  35, 3,  35,3,  35,3,  35, 3,  51, 3,  
   51,3,  51, 3,  51,3,  51,3,  51, 3,  51, 3,  
   51,3 
}; 
 
//---------------------------- 
 
int S_bit_reader::GetMCodedBlockPatternChrominance_i(){ 
                              //max 9bits 
   int code = Show(9); 
   if(code == 1){ 
      Flush(9); // stuffing 
      return 0; 
   } 
   if(code < 8) 
      return -1; 
   code >>= 3; 
   if(code >= 32){ 
      Flush(1); 
      return 3; 
   } 
   code *= 2; 
   Flush(MCBPCtabIntra[code+1]); 
   return MCBPCtabIntra[code]; 
} 
 
//---------------------------- 
 
static const byte MCBPCtabInter[] = { 
   0,0, //-1,0 
   0,9, 52,9, 36,9, 20,9, 49,9, 35,8, 35,8, 19,8, 19,8, 
   50,8, 50,8, 51,7, 51,7, 51,7, 51,7, 34,7, 34,7, 34,7, 
   34,7, 18,7, 18,7, 18,7, 18,7, 33,7, 33,7, 33,7, 33,7,  
   17,7, 17,7, 17,7, 17,7, 4,6, 4,6, 4,6, 4,6, 4,6,  
   4,6, 4,6, 4,6, 48,6, 48,6, 48,6, 48,6, 48,6, 48,6,  
   48,6, 48,6, 3,5, 3,5, 3,5, 3,5, 3,5, 3,5, 3,5,  
   3,5, 3,5, 3,5, 3,5, 3,5, 3,5, 3,5, 3,5, 3,5,  
   32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4,  
   32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4,  
   32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4, 32,4,  
   32,4, 32,4, 32,4, 32,4, 32,4, 16,4, 16,4, 16,4, 16,4,  
   16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4,  
   16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4,  
   16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4, 16,4,  
   16,4, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3,  
   2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3,  
   2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3,  
   2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3,  
   2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3,  
   2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3,  
   2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3, 2,3,  
   2,3, 2,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3,  
   1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3,  
   1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3,  
   1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3,  
   1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3,  
   1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3,  
   1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3, 1,3,  
   1,3, 1,3, 1,3 
}; 
 
//---------------------------- 
 
int S_bit_reader::GetMCodedBlockPatternChrominance_p(){ 
                              //max 9bits 
   int code = Show(9); 
 
   if(code == 0) 
      return -1; 
   if(code >= 256){ 
      Flush(1); 
      return 0; 
   } 
   code *= 2; 
   Flush(MCBPCtabInter[code+1]); 
   return MCBPCtabInter[code]; 
} 
 
//---------------------------- 
 
static const byte scan_table[3][64] = { 
   { 
       0,  1,  8, 16,  9,  2,  3, 10,  
      17, 24, 32, 25, 18, 11,  4,  5, 
      12, 19, 26, 33, 40, 48, 41, 34,  
      27, 20, 13,  6,  7, 14, 21, 28,  
      35, 42, 49, 56, 57, 50, 43, 36,  
      29, 22, 15, 23, 30, 37, 44, 51,  
      58, 59, 52, 45, 38, 31, 39, 46,  
      53, 60, 61, 54, 47, 55, 62, 63 
   }, 
   { 
       0,  1,  2,  3,  8,  9, 16, 17,  
      10, 11,  4,  5,  6,  7, 15, 14, 
      13, 12, 19, 18, 24, 25, 32, 33,  
      26, 27, 20, 21, 22, 23, 28, 29, 
      30, 31, 34, 35, 40, 41, 48, 49,  
      42, 43, 36, 37, 38, 39, 44, 45, 
      46, 47, 50, 51, 56, 57, 58, 59,  
      52, 53, 54, 55, 60, 61, 62, 63 
   }, 
   { 
       0,  8, 16, 24,  1,  9,  2, 10,  
      17, 25, 32, 40, 48, 56, 57, 49, 
      41, 33, 26, 18,  3, 11,  4, 12,  
      19, 27, 34, 42, 50, 58, 35, 43, 
      51, 59, 20, 28,  5, 13,  6, 14,  
      21, 29, 36, 44, 52, 60, 37, 45, 
      53, 61, 22, 30,  7, 15, 23, 31,  
      38, 46, 54, 62, 39, 47, 55, 63 
   } 
}; 
 
//---------------------------- 
 
void S_bit_reader::GetQuantMatrix(byte* m){ 
 
   int i = 0; 
   int last, value = 0; 
   do{ 
      last = value; 
      Load(); 
      value = Get(8); 
      m[scan_table[0][i++]] = (byte)value; 
   }while(value!=0 && i<64); 
   i--; 
   while(i < 64)  
      m[scan_table[0][i++]] = (byte)last; 
} 
 
//---------------------------- 
 
#define _M(mv, bits) (mv<<8) | (bits) 
 
static const short move_vector_table_0[14] = { 
   _M(3,4),_M(-3,4),_M(2,3),_M(2,3),_M(-2,3),_M(-2,3),_M(1,2),_M(1,2), 
   _M(1,2),_M(1,2),_M(-1,2),_M(-1,2),_M(-1,2),_M(-1,2), 
}, move_vector_table_1[96] = { 
   _M(12,10),_M(-12,10), 
   _M(11,10),_M(-11,10),_M(10,9),_M(10,9),_M(-10,9),_M(-10,9),_M(9,9),_M(9,9), 
   _M(-9,9),_M(-9,9),_M(8,9),_M(8,9),_M(-8,9),_M(-8,9),_M(7,7),_M(7,7), 
   _M(7,7),_M(7,7),_M(7,7),_M(7,7),_M(7,7),_M(7,7),_M(-7,7),_M(-7,7), 
   _M(-7,7),_M(-7,7),_M(-7,7),_M(-7,7),_M(-7,7),_M(-7,7),_M(6,7),_M(6,7), 
   _M(6,7),_M(6,7),_M(6,7),_M(6,7),_M(6,7),_M(6,7),_M(-6,7),_M(-6,7), 
   _M(-6,7),_M(-6,7),_M(-6,7),_M(-6,7),_M(-6,7),_M(-6,7),_M(5,7),_M(5,7), 
   _M(5,7),_M(5,7),_M(5,7),_M(5,7),_M(5,7),_M(5,7),_M(-5,7),_M(-5,7), 
   _M(-5,7),_M(-5,7),_M(-5,7),_M(-5,7),_M(-5,7),_M(-5,7),_M(4,6),_M(4,6), 
   _M(4,6),_M(4,6),_M(4,6),_M(4,6),_M(4,6),_M(4,6),_M(4,6),_M(4,6), 
   _M(4,6),_M(4,6),_M(4,6),_M(4,6),_M(4,6),_M(4,6),_M(-4,6),_M(-4,6), 
   _M(-4,6),_M(-4,6),_M(-4,6),_M(-4,6),_M(-4,6),_M(-4,6),_M(-4,6),_M(-4,6), 
   _M(-4,6),_M(-4,6),_M(-4,6),_M(-4,6),_M(-4,6),_M(-4,6) 
}, move_vector_table_2[124] = { 
   _M(32,12),_M(-32,12), 
   _M(31,12),_M(-31,12),_M(30,11),_M(30,11),_M(-30,11),_M(-30,11),_M(29,11),_M(29,11), 
   _M(-29,11),_M(-29,11),_M(28,11),_M(28,11),_M(-28,11),_M(-28,11),_M(27,11),_M(27,11), 
   _M(-27,11),_M(-27,11),_M(26,11),_M(26,11),_M(-26,11),_M(-26,11),_M(25,11),_M(25,11), 
   _M(-25,11),_M(-25,11),_M(24,10),_M(24,10),_M(24,10),_M(24,10),_M(-24,10),_M(-24,10), 
   _M(-24,10),_M(-24,10),_M(23,10),_M(23,10),_M(23,10),_M(23,10),_M(-23,10),_M(-23,10), 
   _M(-23,10),_M(-23,10),_M(22,10),_M(22,10),_M(22,10),_M(22,10),_M(-22,10),_M(-22,10), 
   _M(-22,10),_M(-22,10),_M(21,10),_M(21,10),_M(21,10),_M(21,10),_M(-21,10),_M(-21,10), 
   _M(-21,10),_M(-21,10),_M(20,10),_M(20,10),_M(20,10),_M(20,10),_M(-20,10),_M(-20,10), 
   _M(-20,10),_M(-20,10),_M(19,10),_M(19,10),_M(19,10),_M(19,10),_M(-19,10),_M(-19,10), 
   _M(-19,10),_M(-19,10),_M(18,10),_M(18,10),_M(18,10),_M(18,10),_M(-18,10),_M(-18,10), 
   _M(-18,10),_M(-18,10),_M(17,10),_M(17,10),_M(17,10),_M(17,10),_M(-17,10),_M(-17,10), 
   _M(-17,10),_M(-17,10),_M(16,10),_M(16,10),_M(16,10),_M(16,10),_M(-16,10),_M(-16,10), 
   _M(-16,10),_M(-16,10),_M(15,10),_M(15,10),_M(15,10),_M(15,10),_M(-15,10),_M(-15,10), 
   _M(-15,10),_M(-15,10),_M(14,10),_M(14,10),_M(14,10),_M(14,10),_M(-14,10),_M(-14,10), 
   _M(-14,10),_M(-14,10),_M(13,10),_M(13,10),_M(13,10),_M(13,10),_M(-13,10),_M(-13,10), 
   _M(-13,10),_M(-13,10) 
}; 
 
//---------------------------- 
 
int S_bit_reader::GetMoveData(int fcode){ 
 
   Load(); 
   if(Get1()){ 
                              //no movement (coded in 1 bit) 
      return 0; 
   } 
   int code = Show(12); 
   if(code >= 512)  
      code = move_vector_table_0[(code >> 8) - 2]; 
   else 
   if(code >= 128) 
      code = move_vector_table_1[(code >> 2) - 32]; 
   else 
      code = move_vector_table_2[code-4];  
 
   Flush(code & 255); 
   code >>= 8; 
 
   if(fcode){ 
      code <<= fcode; 
      int res = Get(fcode); 
      res -= (1 << fcode) - 1; 
      if(code < 0) 
         res = -res; 
      code += res; 
   } 
   return code; 
} 
 
//---------------------------- 
 
int S_bit_reader::GetMovementVectorPair(int fcode, int prev_vector_pair){ 
 
   --fcode; 
   int mv_x = GetMoveData(fcode); 
   int mv_y = GetMoveData(fcode);  
   int range = 1 << (fcode+6); 
   int high = (range/2) - 1; 
   int low = -(range/2); 
 
   mv_x += MOVE_VECTOR_X(prev_vector_pair); 
   if(mv_x < low) 
      mv_x += range; 
   if(mv_x > high) 
      mv_x -= range; 
   mv_y += MOVE_VECTOR_Y(prev_vector_pair); 
   if(mv_y < low) 
      mv_y += range; 
   if(mv_y > high) 
      mv_y -= range; 
   return MAKE_MOVEMENT_VECTOR_PAIR(mv_x, mv_y); 
} 
 
//---------------------------- 
 
int S_bit_reader::GetDCsizeLum(){ 
 
   int code = Show(11); 
 
   for(int i=0; i<8; i++){ 
      if((code >> i)==1){ 
         Flush(11-i); 
         return 12-i; 
      } 
   } 
   code >>= 8; 
   if(code == 1){ 
      Flush(3); 
      return 4; 
   } 
   if(code == 2){ 
      Flush(3); 
      return 3; 
   } 
   if(code == 3){ 
      Flush(3); 
      return 0; 
   } 
   code >>=1; 
   if(code == 2){ 
      Flush(2); 
      return 2; 
   } 
   if(code == 3){ 
      Flush(2); 
      return 1; 
   }      
   return 0; 
} 
 
//---------------------------- 
 
int S_bit_reader::GetDCsizeChr(){ 
 
   int i,code = Show(12); 
   for (i=0;i<10;i++){ 
      if ((code >> i)==1) { 
         Flush(12-i); 
         return 12-i; 
      } 
   } 
   return 3 - Get(2); 
} 
 
//---------------------------- 
 
int S_bit_reader::GetDCdiff(int dct_dc_size){ 
   int code = Show(32); //we need only dct_dc_size bits (but in the higher bits) 
   int adj = 0; 
   Flush(dct_dc_size); 
   if(code >= 0) 
      adj = (-1 << dct_dc_size) + 1; 
   return adj + ((dword)code >> (32-dct_dc_size)); 
} 
 
//---------------------------- 
 
//---------------------------- 
//---------------------------- 
 
class C_mp4_decode_imp: public C_mp4_decode{ 
   enum{ RESCUE = 0x80 }; 
 
   enum{ VOL_START_CODE_MASK = 0x0f }; 
 
   enum{ 
      VOL_START_CODE = 0x120, 
      VOP_START_CODE = 0x1b6 
   }; 
 
   enum{ 
      ASPECT_SQUARE = 1, 
      ASPECT_625TYPE_43 = 2, 
      ASPECT_525TYPE_43 =3, 
      ASPECT_625TYPE_169 = 8, 
      ASPECT_525TYPE_169 = 9, 
      ASPECT_CUSTOM = 15, 
   }; 
 
//---------------------------- 
 
   enum E_FRAME_TYPE{ 
      I_VOP, 
      P_VOP, 
      B_VOP, 
      S_VOP, 
      N_VOP, 
   }; 
 
//---------------------------- 
 
   enum{ 
      INTER = 0,              //prediction 
      INTER_Q = 1, 
      INTER4V = 2, 
      INTRA = 3,              //no prediction 
      INTRA_Q = 4, 
   }; 
 
//---------------------------- 
 
   enum E_SPRITE_TYPE{ 
      SPRITE_NO, 
      SPRITE_STATIC, 
      SPRITE_GMC, 
   }; 
 
//---------------------------- 
 
   enum{ 
      DIRECT = 0, 
      INTERPOLATE = 1, 
      BACKWARD = 2, 
      FORWARD = 3, 
   }; 
 
//---------------------------- 
 
#ifdef PROFILE 
   C_profiler *prof; 
#endif 
 
   C_softidct soft_idct; 
   S_bit_reader bit_reader; 
 
   bool has_b_frames; 
 
   int quant_precision;       //in how many bits is quantizier encoded 
 
   dword video_sx, video_sy; 
 
   bool valid_vol;            //VOL information was successfully read 
   bool interlaced; 
   bool ac_pred_flag; 
   int frame_index; 
   int last_ref_frame_index;  //index of last I or P frame 
 
                              //frame index, when the last time particular macro-block changed 
                              // (need to add 'block_update_base_indx' for getting actual index) 
                              // also contains RESCUE flag 
   byte block_update_data[MAX_MACROBLOCKS_X*MAX_MACROBLOCKS_Y]; 
   int mb_pos_end;            //index of end of macro-block table 'block_update_data' 
   int block_update_base_indx;   //base frame index for above (typically set to last 'P' frame's index) 
   int mb_xsize, mb_ysize;      //number of macroblocks for width/height 
 
   E_SPRITE_TYPE sprite_type; 
   int quantizer; 
 
   int intra_dc_threshold; 
	int fcode_forward, fcode_backward; 
	int resync_marker_bits; 
 
   short _block[64+16/sizeof(short)]; 
   short *block_8x8;          //16-byte aligned, used for 8x8 block computations (intra, inter) 
 
   int time_increment_resolution;   //video framerate * 1000 
   int last_ref_time;         //last time of reference frame (I or P, not B) 
 
	// b-frame 
	int time_pp, time_bp; 
	int last_bframe; 
   int trb, trb_trd;          //8.8 fixed-point 
 
//---------------------------- 
 
	int resyncpos; 
 
	int time_increment_bits; 
	bool quant_type;             //type of quantizied - mpeg (true) or mpeg4 (false) 
 
	// motion compensation buffer 
	int *mv_buf; //32bytes aligned 
	int* _mv_buf; 
	int mv_bufmask; 
	int move_buf_invalid; 
 
                              //packed B frame 
   C_buffer packed_b_frame; 
   dword packed_b_size; 
 
	int refframe; 
 
	byte quant_matrix[2][8*8]; // [intra / inter] 
 
	short dc_lum[4*MAX_MACROBLOCKS_X*2];		//[4][double width row] 
	short ac_left_lum[2][8];		//[lower/upper][8] 
	short ac_top_lum[2*MAX_MACROBLOCKS_X][8]; //[double witdh row][8] 
 
	short dc_chr[2][2*MAX_MACROBLOCKS_X];		//[U/V][2][normal width row] 
	short ac_left_chr[2][8];		//[U/V][8] 
	short ac_top_chr[MAX_MACROBLOCKS_X][2][8];//[normal width row][U/V][8] 
 
   int _mv_bufrow[4*(MAX_MACROBLOCKS_X+1)+32/sizeof(int)]; // add one extra block for left border + 32align 
 
//---------------------------- 
 
   void RescuePredict(int pos); 
 
//---------------------------- 
// Read all frame's headers (VOL, VOP). Return true if VOP was found and read. 
   bool ReadFrameHeaders(E_FRAME_TYPE &frame_prediction_type); 
 
//---------------------------- 
// Read volume header. 
   void ReadHeader_vol(); 
 
//---------------------------- 
// Read picture header. 
   void ReadHeader_vop(E_FRAME_TYPE &frame_prediction_type); 
 
//---------------------------- 
 
   int ReSync(); 
 
//---------------------------- 
 
   bool ResyncMarker(); 
 
//---------------------------- 
 
   int GetPreviousMoveVector(int block_num, int pos); 
 
//---------------------------- 
   int Vld_block_mpeg(const word *table, const byte *scan, int len); 
   int Vld_block_mpeg4(const word *table, const byte *scan, int len); 
 
//---------------------------- 
 
   short *Dc_recon(int block_num, int pos, int dc_scaler, int &predict_dir); 
   void Ac_recon(int block_num, int pos, int predict_dir); 
 
//---------------------------- 
// Decode block with no prediction. 
   void BlockIntra(int pos, int coded_block_pattern); 
 
//---------------------------- 
// Decode block with prediction. 
   void BlockInter(int coded_block_pattern); 
 
//---------------------------- 
   void BlockDirect(int pos, int dmv, int coded_block_pattern); 
 
//---------------------------- 
 
   void IVOP(); 
   void PVOP(); 
   void BVOP(); 
 
//---------------------------- 
   bool Frame(const byte *strm, int len, bool drop_b_frames); 
 
public: 
 
//---------------------------- 
 
   bool SetIDCTBufferCount(int count); 
 
   bool UpdateCount(); 
   bool Init(); 
 
   bool Mp4_vop(const byte *strm, bool drop_b_frames, E_FRAME_TYPE frame_prediction_type); 
 
   virtual bool Process(const byte *packet, dword len, bool drop_b_frames, S_yuv_image &out); 
 
//---------------------------- 
 
   C_mp4_decode_imp(dword sx, dword sy, void *profiler); 
   ~C_mp4_decode_imp(){ 
      delete[] _mv_buf; 
   } 
}; 
 
//---------------------------- 
 
bool C_mp4_decode_imp::Process(const byte *packet, dword len, bool drop_b_frames, S_yuv_image &out){ 
 
   out.type = 0; 
#ifdef PROFILE 
   if(frame_index==10) 
      prof->ResetAverage(); 
#endif 
   /* 
   if(frame_to_show!=-1){ // pending frame? 
      SendData(out); 
      frame_to_show = -1; 
      assert(out.type); 
      return true; 
   } 
      */ 
//       if(has_b_frames && FrameTime>0 && RefTime >= FrameTime) 
//          RefTime -= FrameTime; 
   if(!Frame(packet, len, drop_b_frames)) 
      return false; 
                              //send to display 
   if(!soft_idct.GetBuffer(out.y, out.u, out.v)){ 
      assert(0); 
      return false; 
   } 
   out.pitch = soft_idct.GetBufferPitch(); 
   out.type = 1; 
   return true; 
} 
 
//---------------------------- 
 
bool C_mp4_decode_imp::SetIDCTBufferCount(int c){ 
 
   if(!soft_idct.SetBufferCount(c)) 
      return false; 
   return UpdateCount(); 
} 
 
//---------------------------- 
// msb..lsb 
// 4bit flushbits, 1bit last, 6bit run, 5bit level 
 
                              //no prediction 
static const word vld_intra[112+96+120+64+16] = { 
   0x7881,0x7861,0x70c1,0x78a1,0x70e1,0x7042,0x7023,0x7009, 
   0x6802,0x6802,0x60a1,0x60a1,0x6841,0x6841,0x6821,0x6821, 
   0x6081,0x6081,0x6061,0x6061,0x6008,0x6008,0x6007,0x6007, 
   0x6022,0x6022,0x6006,0x6006,0x5041,0x5041,0x5041,0x5041, 
   0x5005,0x5005,0x5005,0x5005,0x5004,0x5004,0x5004,0x5004, 
   0x4801,0x4801,0x4801,0x4801,0x4801,0x4801,0x4801,0x4801, 
   0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001, 
   0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001, 
   0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001, 
   0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001, 
   0x3002,0x3002,0x3002,0x3002,0x3002,0x3002,0x3002,0x3002, 
   0x3002,0x3002,0x3002,0x3002,0x3002,0x3002,0x3002,0x3002, 
   0x4021,0x4021,0x4021,0x4021,0x4021,0x4021,0x4021,0x4021, 
   0x4003,0x4003,0x4003,0x4003,0x4003,0x4003,0x4003,0x4003, 
   0xa012,0xa011,0x99c1,0x99c1,0x99a1,0x99a1,0x9981,0x9981, 
   0x9961,0x9961,0x9941,0x9941,0x9822,0x9822,0x9804,0x9804, 
   0x9181,0x9181,0x9161,0x9161,0x90e2,0x90e2,0x90c2,0x90c2, 
   0x90a2,0x90a2,0x9063,0x9063,0x9043,0x9043,0x9026,0x9026, 
   0x9025,0x9025,0x9010,0x9010,0x9082,0x9082,0x900f,0x900f, 
   0x900e,0x900e,0x900d,0x900d,0x8901,0x8901,0x8901,0x8901, 
   0x88e1,0x88e1,0x88e1,0x88e1,0x88c1,0x88c1,0x88c1,0x88c1, 
   0x8803,0x8803,0x8803,0x8803,0x8141,0x8141,0x8141,0x8141, 
   0x8121,0x8121,0x8121,0x8121,0x8101,0x8101,0x8101,0x8101, 
   0x8921,0x8921,0x8921,0x8921,0x8062,0x8062,0x8062,0x8062, 
   0x8024,0x8024,0x8024,0x8024,0x800c,0x800c,0x800c,0x800c, 
   0x800b,0x800b,0x800b,0x800b,0x800a,0x800a,0x800a,0x800a, 
   0xb807,0xb807,0xb806,0xb806,0xb016,0xb016,0xb015,0xb015, 
   0xa842,0xa842,0xa842,0xa842,0xa823,0xa823,0xa823,0xa823, 
   0xa805,0xa805,0xa805,0xa805,0xa1a1,0xa1a1,0xa1a1,0xa1a1, 
   0xa0a3,0xa0a3,0xa0a3,0xa0a3,0xa102,0xa102,0xa102,0xa102, 
   0xa083,0xa083,0xa083,0xa083,0xa064,0xa064,0xa064,0xa064, 
   0xa044,0xa044,0xa044,0xa044,0xa027,0xa027,0xa027,0xa027, 
   0xa014,0xa014,0xa014,0xa014,0xa013,0xa013,0xa013,0xa013, 
   0xb017,0xb017,0xb018,0xb018,0xb028,0xb028,0xb122,0xb122, 
   0xb862,0xb862,0xb882,0xb882,0xb9e1,0xb9e1,0xba01,0xba01, 
   0xc019,0xc01a,0xc01b,0xc029,0xc0c3,0xc02a,0xc045,0xc0e3, 
   0xc1c1,0xc808,0xc8a2,0xc8c2,0xca21,0xca41,0xca61,0xca81, 
   0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff, 
   0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff, 
   0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff, 
   0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff, 
    
   0x0a1b,0x0405,0x0303,0x0303,0x0202,0x0101,0x0101,0x0001, 
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x0308,0x0202,0x0202,0x0102,0x0101,0x0101,0x0101,0x0101, 
   0x0101,0x0101,0x0001,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x0f02,0x080a,0x0304,0x0202,0x0202,0x0102,0x0101,0x0101, 
   0x1501,0x0207,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101 
}; 
 
//---------------------------- 
                              //prediction 
static const word vld_inter[112+96+120+64+16] = { 
   0x7901,0x78e1,0x78c1,0x78a1,0x7181,0x7161,0x7141,0x7004, 
   0x6881,0x6881,0x6861,0x6861,0x6841,0x6841,0x6821,0x6821, 
   0x6121,0x6121,0x6101,0x6101,0x60e1,0x60e1,0x60c1,0x60c1, 
   0x6022,0x6022,0x6003,0x6003,0x50a1,0x50a1,0x50a1,0x50a1, 
   0x5081,0x5081,0x5081,0x5081,0x5061,0x5061,0x5061,0x5061, 
   0x4801,0x4801,0x4801,0x4801,0x4801,0x4801,0x4801,0x4801, 
   0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001, 
   0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001, 
   0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001, 
   0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001,0x2001, 
   0x3021,0x3021,0x3021,0x3021,0x3021,0x3021,0x3021,0x3021, 
   0x3021,0x3021,0x3021,0x3021,0x3021,0x3021,0x3021,0x3021, 
   0x4041,0x4041,0x4041,0x4041,0x4041,0x4041,0x4041,0x4041, 
   0x4002,0x4002,0x4002,0x4002,0x4002,0x4002,0x4002,0x4002, 
   0xa009,0xa008,0x9b01,0x9b01,0x9ae1,0x9ae1,0x9ac1,0x9ac1, 
   0x9aa1,0x9aa1,0x9a81,0x9a81,0x9a61,0x9a61,0x9a41,0x9a41, 
   0x9a21,0x9a21,0x9802,0x9802,0x92c1,0x92c1,0x92a1,0x92a1, 
   0x9281,0x9281,0x9261,0x9261,0x9241,0x9241,0x9221,0x9221, 
   0x9201,0x9201,0x91e1,0x91e1,0x9082,0x9082,0x9062,0x9062, 
   0x9007,0x9007,0x9006,0x9006,0x8a01,0x8a01,0x8a01,0x8a01, 
   0x89e1,0x89e1,0x89e1,0x89e1,0x89c1,0x89c1,0x89c1,0x89c1, 
   0x89a1,0x89a1,0x89a1,0x89a1,0x8981,0x8981,0x8981,0x8981, 
   0x8961,0x8961,0x8961,0x8961,0x8941,0x8941,0x8941,0x8941, 
   0x8921,0x8921,0x8921,0x8921,0x81c1,0x81c1,0x81c1,0x81c1, 
   0x81a1,0x81a1,0x81a1,0x81a1,0x8042,0x8042,0x8042,0x8042, 
   0x8023,0x8023,0x8023,0x8023,0x8005,0x8005,0x8005,0x8005, 
   0xb822,0xb822,0xb803,0xb803,0xb00b,0xb00b,0xb00a,0xb00a, 
   0xab81,0xab81,0xab81,0xab81,0xab61,0xab61,0xab61,0xab61, 
   0xab41,0xab41,0xab41,0xab41,0xab21,0xab21,0xab21,0xab21, 
   0xa122,0xa122,0xa122,0xa122,0xa102,0xa102,0xa102,0xa102, 
   0xa0e2,0xa0e2,0xa0e2,0xa0e2,0xa0c2,0xa0c2,0xa0c2,0xa0c2, 
   0xa0a2,0xa0a2,0xa0a2,0xa0a2,0xa063,0xa063,0xa063,0xa063, 
   0xa043,0xa043,0xa043,0xa043,0xa024,0xa024,0xa024,0xa024, 
   0xb00c,0xb00c,0xb025,0xb025,0xb2e1,0xb2e1,0xb301,0xb301, 
   0xbba1,0xbba1,0xbbc1,0xbbc1,0xbbe1,0xbbe1,0xbc01,0xbc01, 
   0xc026,0xc044,0xc083,0xc0a3,0xc0c3,0xc142,0xc321,0xc341, 
   0xcc21,0xcc41,0xcc61,0xcc81,0xcca1,0xccc1,0xcce1,0xcd01, 
   0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff, 
   0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff, 
   0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff, 
   0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff, 
 
   0x060c,0x0304,0x0303,0x0203,0x0202,0x0102,0x0101,0x0101, 
   0x0101,0x0101,0x0101,0x0101,0x0101,0x0001,0x0000,0x0000, 
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x0203,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101, 
   0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101, 
   0x0101,0x0101,0x0101,0x0101,0x0001,0x0000,0x0000,0x0000, 
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 
   0x1b02,0x070b,0x0203,0x0102,0x0101,0x0101,0x0101,0x0101, 
   0x2901,0x0102,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101 
}; 
 
//---------------------------- 
 
void C_mp4_decode_imp::RescuePredict(int pos){ 
 
   int lumpos = 2*POSX(pos) + (POSY(pos) << (MB_X2+2)); 
 
   if(pos>=MAX_MACROBLOCKS_X+1 && (block_update_data[pos-MAX_MACROBLOCKS_X-1]&RESCUE)){ 
                              //rescue -A- DC value 
      dc_lum[(lumpos-MAX_MACROBLOCKS_X*2) & DC_LUM_MASK] = 1024; 
      dc_chr[0][(pos-MAX_MACROBLOCKS_X) & DC_CHR_MASK] = 1024; 
      dc_chr[1][(pos-MAX_MACROBLOCKS_X) & DC_CHR_MASK] = 1024; 
   } 
 
                              //left 
   if(pos>=1 && (block_update_data[pos-1]&RESCUE)){ 
      // rescue -B- DC values 
      dc_lum[(lumpos) & DC_LUM_MASK] = 1024; 
      dc_lum[(lumpos+MAX_MACROBLOCKS_X*2) & DC_LUM_MASK] = 1024; 
      dc_chr[0][pos & DC_CHR_MASK] = 1024; 
      dc_chr[1][pos & DC_CHR_MASK] = 1024; 
       
      if(ac_pred_flag){ 
                              //rescue -B- AC values 
         Clear32(ac_left_lum); 
         Clear32(ac_left_chr); 
      } 
   } 
                              //top 
   if(pos>=MAX_MACROBLOCKS_X && (block_update_data[pos-MAX_MACROBLOCKS_X]&RESCUE)){ 
       
      // rescue -C- DC values 
      dc_lum[(lumpos+1-MAX_MACROBLOCKS_X*2) & DC_LUM_MASK] = 1024; 
      dc_lum[(lumpos+2-MAX_MACROBLOCKS_X*2) & DC_LUM_MASK] = 1024; 
      dc_chr[0][(pos+1-MAX_MACROBLOCKS_X) & DC_CHR_MASK] = 1024; 
      dc_chr[1][(pos+1-MAX_MACROBLOCKS_X) & DC_CHR_MASK] = 1024; 
       
      if(ac_pred_flag){ 
                              //rescue -C- AC values 
         Clear32(ac_top_lum[lumpos & (MAX_MACROBLOCKS_X*2-1)]); 
         Clear32(ac_top_chr[pos & (MAX_MACROBLOCKS_X-1)]); 
      } 
   } 
} 
 
//---------------------------- 
 
bool C_mp4_decode_imp::UpdateCount(){ 
 
   int *mv; 
   if(soft_idct.GetBufferCount() >= 3){ 
      if(mv_bufmask != -1) 
         move_buf_invalid = 1; 
      mv_bufmask = -1; // full size mv buffer needed 
      if(!_mv_buf){ 
         int size = 32 + 4*sizeof(int) * (mb_pos_end+1); // add one extra block for left border 
         _mv_buf = new int[size/4]; 
         if(!_mv_buf) 
            return false; 
         MemSet(_mv_buf,0,size); 
      } 
      mv = _mv_buf; 
   }else{ 
      move_buf_invalid = 1; 
      mv_bufmask = MAX_MACROBLOCKS_X-1; //only one row of mv buffer needed 
      mv = _mv_bufrow; 
   } 
   mv_buf = (int*)(((int)mv + 31 + 4*sizeof(int)) & ~31); 
   return true; 
} 
 
//---------------------------- 
 
bool C_mp4_decode_imp::Mp4_vop(const byte *strm, bool drop_b_frames, E_FRAME_TYPE frame_prediction_type){ 
 
   resyncpos = 0; 
 
   switch(frame_prediction_type){ 
   case N_VOP: 
      if(packed_b_size){ 
         bit_reader.Init(packed_b_frame.Begin(), packed_b_size); 
         packed_b_size = 0; 
         if(ReadFrameHeaders(frame_prediction_type)){ 
            bool ok = Mp4_vop(packed_b_frame.Begin(), drop_b_frames, frame_prediction_type); 
            if(ok) 
               return true; 
         } 
      } 
      return (frame_index > 0); 
   case P_VOP: 
                              //P (predicted) frames are predicted from the most recently reconstructed I or P frame 
                              //each macroblock in a P frame can either come with a vector and difference DCT coefficients for a close match in the last I or P, 
                              // or it can just be "intra" coded if there was no good match 
      if(frame_index < 1) 
         return false; 
                              //flow... 
   case I_VOP: 
                              //I (intra) frames are coded as individual still images (JPG) 
 
                              //I/P-frames are the "reference" frames 
                              // they are decode into refframe  
                              // P-frames depend on last refenrence frame: refframe^1 
 
      refframe ^= 1; 
 
      if(has_b_frames) 
         ++last_bframe; 
 
      int frame_to_show; 
      if(!has_b_frames || frame_index==0) 
         frame_to_show = refframe; // show this refframe 
      else 
         frame_to_show = refframe ^ 1; // show last refframe  
 
      if(frame_prediction_type == I_VOP) { 
         soft_idct.FrameStart(frame_index, refframe, -1, -1, frame_to_show); 
         IVOP(); 
      }else{ 
         soft_idct.FrameStart(frame_index, refframe, refframe ^ 1, -1, frame_to_show); 
         PVOP(); 
      } 
      break; 
 
   case B_VOP: 
                              //B (bidirectional) frames are predicted from the closest two I or P frames, one in the past and one in the future 
      if(frame_index < 2 || move_buf_invalid) 
         return false; 
 
      last_bframe = 0; 
 
      if(drop_b_frames){ 
                              //ignore b-frames 
         return false; 
      } 
 
      if(move_buf_invalid == 2){ 
         move_buf_invalid = 0; 
         MemSet(mv_buf, 0, 4*sizeof(int)*mb_pos_end); 
      } 
 
      assert(has_b_frames); 
      /* 
      if(soft_idct.GetBufferCount()<3){ 
         SetIDCTBufferCount(3); 
         if(move_buf_invalid) 
            return false; 
      } 
      */ 
      /* 
                              //save this b-frame if there was already a packed one 
      if(frame_to_show >= 0){ 
                              //rewind 
         bit_reader.Rewind(strm); 
         return true; 
      } 
      */ 
      soft_idct.FrameStart(-frame_index, 2, refframe,refframe ^ 1, 2); 
       
      if(time_pp){ 
         int TRB = time_pp - time_bp; 
         trb = (TRB<<8) / time_pp; 
         trb_trd = ((TRB - time_pp)<<8) / time_pp; 
      }else{ 
         trb = 0; 
         trb_trd = 0; 
      } 
      BVOP(); 
      break; 
 
   default: 
      return false; 
   } 
 
   frame_index++; 
 
                              //make sure that data in block_update_data don't overflow 
   const int MAX_D = 128; 
   if((frame_index - block_update_base_indx) >= MAX_D){ 
      for(int pos=0; pos= (MAX_D-8)) 
               i -= (MAX_D-8); 
            else 
               i = 0; 
            block_update_data[pos] = i|r; 
         } 
      } 
      block_update_base_indx += MAX_D-8; 
   } 
   return true; 
} 
 
//---------------------------- 
 
bool C_mp4_decode_imp::Frame(const byte* ptr, int len, bool drop_b_frames){ 
 
   E_FRAME_TYPE frame_prediction_type; 
   if(!len){ 
                              //null frame, return last rendered frame 
      return true; 
   } 
 
   PROF_S(PROF_DECODE); 
 
   bit_reader.Init(ptr, len); 
 
   if(!ReadFrameHeaders(frame_prediction_type) || !valid_vol) 
      return false; //invalid header or no vol information 
 
   bool ok = Mp4_vop(ptr, drop_b_frames, frame_prediction_type); 
   PROF_E(PROF_DECODE); 
 
#if defined _DEBUG && 0 
                              //mark frame type 
   if(ok){ 
      byte *y, *u, *v; 
      if(soft_idct.GetBuffer(y, u, v)){ 
         dword pi = soft_idct.GetBufferPitch(); 
         for(int i=0; i<3; i++){ 
            byte *d = !i ? y : i==1 ? u : v; 
            dword p = pi; 
            dword s = 16; 
            if(i){ 
               p /= 2; 
               s /= 2; 
               d += p*4*4; 
            }else 
               d += p*8*4; 
            byte val; 
            switch(frame_prediction_type){ 
            case I_VOP: val = !i ? 0x00 : i==1 ? 0x00 : 0xff; break; 
            case P_VOP: val = !i ? 0x80 : i==1 ? 0x80 : 0x80; break; 
            case B_VOP: val = !i ? 0x80 : i==1 ? 0xff : 0x00; break; 
            default: val = !i ? 0x0 : i==1 ? 0x80 : 0x80; 
            } 
            for(int y=s; y--; ){ 
               for(int x=s; x--; ){ 
                  d[x] = val; 
               } 
               d += p; 
            } 
         } 
      } 
   } 
#endif 
   if(ok){ 
                              //check for packed b-frame 
      const byte *pos = bit_reader.ptr - 4 + ((bit_reader.pos+7) >> 3); 
 
      len -= (pos - ptr); 
      if(len > 4){ 
         if(!has_b_frames){ 
            has_b_frames = true; 
            SetIDCTBufferCount(3); 
         } 
                              //save packed b-frame 
         if(packed_b_frame.Size() < len) 
            packed_b_frame.Resize((len+8+0x1fff) & ~0x1fff); 
         packed_b_size = len; 
         MemCpy(packed_b_frame.Begin(), pos, len); 
      } 
   } 
   return ok; 
} 
 
//---------------------------- 
 
void C_mp4_decode_imp::Ac_recon(int block_num, int pos, int predict_dir){ 
 
   int i; 
   short *ac_top; 
   short *ac_left; 
   if(block_num < 4){ 
                              //Y 
      ac_top = ac_top_lum[pos & (MAX_MACROBLOCKS_X*2-1)]; 
      ac_left = ac_left_lum[(pos >> (MB_X2+1)) & 1]; 
      i = MAX_MACROBLOCKS_X*2-1; //stride-1 
   }else{ 
                              //UV 
      ac_top = ac_top_chr[pos & (MAX_MACROBLOCKS_X-1)][block_num & 1]; 
      ac_left = ac_left_chr[block_num & 1]; 
      i = MAX_MACROBLOCKS_X-1; //stride-1 
   } 
   if(ac_pred_flag){ 
      if(predict_dir == 1){ 
                              //TOP 
         if(pos > i){ 
            for(i = 1; i < 8; i++) 
               block_8x8[i] = (short)(block_8x8[i] + ac_top[i]); 
         } 
      }else{ 
                              //LEFT 
         if(pos & i){ 
            for(i = 1; i < 8; i++) 
               block_8x8[i<<3] = (short)(block_8x8[i<<3] + ac_left[i]); 
         } 
      } 
   } 
   for(i = 1; i < 8; i++){ 
      ac_top[i] = block_8x8[i]; 
      ac_left[i] = block_8x8[i<<3]; 
   } 
} 
 
//---------------------------- 
 
short *C_mp4_decode_imp::Dc_recon(int block_num, int pos, int dc_scaler, int &predict_dir){ 
 
   short *dc; 
 
   int Fb = 1024; 
   int Fc = 1024; 
 
   if(block_num < 4){ 
                              //Y 
      dc = dc_lum; 
      if(pos >= MAX_MACROBLOCKS_X*2){ 
         Fb = dc[(pos-MAX_MACROBLOCKS_X*2) & DC_LUM_MASK]; 
         Fc = dc[(pos+1-MAX_MACROBLOCKS_X*2) & DC_LUM_MASK]; 
      } 
      dc += pos & DC_LUM_MASK; 
   }else{ 
                              //UV 
      dc = dc_chr[block_num & 1]; 
      if(pos >= MAX_MACROBLOCKS_X){ 
         Fb=dc[(pos-MAX_MACROBLOCKS_X) & DC_CHR_MASK]; 
         Fc=dc[(pos+1-MAX_MACROBLOCKS_X) & DC_CHR_MASK]; 
      } 
      dc += pos & DC_CHR_MASK; 
   } 
   int Fa = dc[0]; 
   if(Abs(Fb - Fa) < Abs(Fb - Fc)){ 
                              //TOP 
      predict_dir = 1; 
      Fa = Fc; 
   }else{ 
                              //LEFT 
      predict_dir = 0; 
   } 
   /* 
   if(Fa>0) 
      Fa += dc_scaler>>1; 
   else 
      Fa -= dc_scaler>>1; 
   Fa /= dc_scaler; 
      /**/ 
   //assert(Divide(Fa, dc_scaler) == ( ((Fa>0)?(Fa+(dc_scaler>>1)):(Fa-(dc_scaler>>1)))/dc_scaler )); 
   Fa = Divide(Fa, dc_scaler); 
 
   ++dc; 
   Fa *= dc_scaler; 
   *dc = (short)Fa; 
   return dc; 
} 
 
//---------------------------- 
 
static void ClearBlock(short *block){ 
 
   int *i = (int*)block; 
   const int *ie = i+32; 
   do { 
      i[3] = i[2] = i[1] = i[0] = 0; 
      i[7] = i[6] = i[5] = i[4] = 0; 
      i += 8; 
   }while(i!=ie); 
} 
 
//---------------------------- 
 
void C_mp4_decode_imp::BlockIntra(int pos, int coded_block_pattern){ 
 
   soft_idct.BeginBlock(POSX(pos), POSY(pos)); 
 
   PROF_S(PROF_BLOCK_INTRA); 
   short *block = block_8x8; 
                              //macroblock consists of four luminance blocks and the two spatially corresponding color difference blocks 
   for(int i=0; i<6; ++i){ 
      int subpos; 
      int dc_scaler = quantizer; 
 
      ClearBlock(block); 
      bit_reader.Load(); 
                              //stream: 24bit available 
      if(i < 4){ 
                              //Y 
         if(dc_scaler >24) 
            dc_scaler = dc_scaler*2 - 16; 
         else 
         if(dc_scaler>8) 
            dc_scaler = (dc_scaler + 8); 
         else 
         if(dc_scaler>4) 
            dc_scaler <<= 1; 
         else 
            dc_scaler = 8; 
 
                              //convert 1x1 -> 2x2 (accodring to block number) 
         subpos = 2*POSX(pos) + (POSY(pos) << (MB_X2+2)); 
         subpos += (i & 1) + ((i & 2) << MB_X2); 
      }else{ 
                              //UV 
         if(dc_scaler > 24) 
            dc_scaler = (dc_scaler - 6); 
         else 
         if(dc_scaler > 4) 
            dc_scaler = (dc_scaler + 13)>>1; 
         else 
            dc_scaler = 8; 
 
         subpos = pos; 
      } 
      int len; 
      if(quantizer < intra_dc_threshold){ 
         int dct_dc_size, dct_dc_diff; 
 
         dct_dc_diff = 0; 
         dct_dc_size = i<4 ? bit_reader.GetDCsizeLum() : bit_reader.GetDCsizeChr(); //max11bit : max12bit 
 
         if(dct_dc_size){ 
            dct_dc_diff = bit_reader.GetDCdiff(dct_dc_size); 
            if(dct_dc_size > 8)           
               bit_reader.Flush(1); // marker bit   
         } 
         *block = (short)(dct_dc_diff * dc_scaler); 
         len = 1; 
      }else 
         len = 0; 
                              //dc reconstruction, prediction direction 
      int predict_dir; 
      short *dc_addr = Dc_recon(i, subpos, dc_scaler, predict_dir); 
      int scantype; 
      if(ac_pred_flag){ 
         if(predict_dir == 1)   //TOP 
            scantype = IDCTSCAN_ALT_HORI; 
         else 
            scantype = IDCTSCAN_ALT_VERT; 
      }else 
         scantype = IDCTSCAN_ZIGZAG; 
 
      if((coded_block_pattern << (26+i)) < 0){ 
                              //coded block 
         if(quant_type) 
            len = Vld_block_mpeg(vld_intra, scan_table[scantype], len); 
         else 
            len = Vld_block_mpeg4(vld_intra, scan_table[scantype], len); 
      } 
                              //dc add  
      if(quantizer >= intra_dc_threshold){ 
         if(quant_type) 
            *block = (short)(Divide(*block*8, quant_matrix[0][0] * quantizer) * dc_scaler); 
         else 
            *block = (short)(Divide(*block, quantizer*2) * dc_scaler); 
      } 
      *block = *dc_addr = (short)(*block + *dc_addr); 
       
                              //ac reconstruction 
      Ac_recon(i, subpos, predict_dir); 
      soft_idct.Intra8x8(block, Max(len, 14), scantype); 
   } 
   PROF_E(PROF_BLOCK_INTRA); 
} 
 
//---------------------------- 
 
void C_mp4_decode_imp::BlockInter(int coded_block_pattern){ 
 
                              //texture decoding add 
                              //macroblock consists of four luminance blocks and the two spatially corresponding color difference blocks 
   for(int i = 0; i < 6; i++){ 
      int len = 0; 
      if((coded_block_pattern << (26+i)) < 0){ 
                              //coded 
         ClearBlock(block_8x8); 
         if(quant_type) 
            len = Vld_block_mpeg(vld_inter, scan_table[IDCTSCAN_ZIGZAG], len); 
         else 
            len = Vld_block_mpeg4(vld_inter, scan_table[IDCTSCAN_ZIGZAG], len); 
      } 
      soft_idct.Inter8x8(block_8x8, len); 
   } 
} 
 
//---------------------------- 
 
static const signed char DQtab[4] = { 
   -1, -2, 1, 2 
}; 
 
//---------------------------- 
 
void C_mp4_decode_imp::IVOP(){ 
 
   PROF_S(PROF_I_VOP); 
 
   move_buf_invalid = 2; // invalid, but can be cleared to make it valid 
   last_ref_frame_index = frame_index; 
   block_update_base_indx = frame_index; 
   MemSet(block_update_data, 0, mb_pos_end); // set all block to current frame (and clear rescue flag) 
 
   resync_marker_bits = 17; 
 
   for(int pos=0; pos=mb_pos_end) 
               return; 
         } 
                              //stream: 24bits available 
         int mcbpc = bit_reader.GetMCodedBlockPatternChrominance_i(); 
                              //stream: 15bits available 
         ac_pred_flag = bit_reader.Get1(); 
         int coded_block_pattern = (bit_reader.GetCodedBlockPatternLuminance() << 2) | ((mcbpc >> 4) & 3); 
             
         if((mcbpc & 7) == INTRA_Q){ 
                              //mb_type 
            int q = quantizer + DQtab[bit_reader.Get(2)]; 
            quantizer = q<1 ? 1: q>31 ? 31 : q; 
         } 
         BlockIntra(pos, coded_block_pattern); 
      } 
   } 
   PROF_E(PROF_I_VOP); 
} 
 
//---------------------------- 
 
static int MvChroma(int v){ 
   v |= ((v & 0x3)!=0) << 1;     //adjust dx 
   v |= ((v & 0x30000)!=0) << 17;   //adjust dy 
 
   v >>= 1; //shift 
 
   //replace dx sign bit with old signed bit 
   v &= ~0x8000;         
   v |= (v & 0x4000) << 1; 
 
   return v; 
} 
 
//---------------------------- 
 
static const byte roundtab[16] = { 
   0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2 
}; 
 
//---------------------------- 
 
#define sign(x) ((x)<0?-1:1) 
 
static int MvChroma4(const int* mv){ 
   int dx = MOVE_VECTOR_X(mv[0]) + MOVE_VECTOR_X(mv[1]) + MOVE_VECTOR_X(mv[2]) + MOVE_VECTOR_X(mv[3]); 
   int dy = MOVE_VECTOR_Y(mv[0]) + MOVE_VECTOR_Y(mv[1]) + MOVE_VECTOR_Y(mv[2]) + MOVE_VECTOR_Y(mv[3]); 
 
   if (dx) 
      dx = sign(dx) * (roundtab[Abs(dx) & 0xF] + ((Abs(dx) >> 4) <<1)); 
    
   if (dy) 
      dy = sign(dy) * (roundtab[Abs(dy) & 0xF] + ((Abs(dy) >> 4) <<1)); 
 
   return MAKE_MOVEMENT_VECTOR_PAIR(dx, dy); 
} 
 
//---------------------------- 
 
void C_mp4_decode_imp::PVOP(){ 
 
   PROF_S(PROF_P_VOP); 
                              //if no b-frames, we need only one row of mv vectors 
   int j; 
 
   move_buf_invalid = 0; 
   byte currframemap = byte(frame_index - block_update_base_indx); 
   last_ref_frame_index = frame_index; 
   resync_marker_bits = 16 + fcode_forward; 
 
   int last_frame_index = soft_idct.GetPreviousDstFrameIndex(); 
 
   for(int pos=0; pos=mb_pos_end) 
               return; 
 
         } 
                              //stream: 24bits available 
         if(!bit_reader.Get1()){ 
             
                              //coded 
            int mcbpc = bit_reader.GetMCodedBlockPatternChrominance_p(); 
            int mb_type = mcbpc & 7; 
            mcbpc = (mcbpc >> 4) & 3; 
 
            //stream: 14bits available 
 
            block_update_data[pos] = (byte)currframemap; 
            if(mb_type >= INTRA){ 
 
               ac_pred_flag = bit_reader.Get1(); 
               int coded_block_pattern = (bit_reader.GetCodedBlockPatternLuminance() << 2) | mcbpc; 
 
                              //stream: 7bits available 
               if(mb_type == INTRA_Q){ 
                  int q = quantizer + DQtab[bit_reader.Get(2)]; //DQtab[dquant] 
                  quantizer = q<1 ? 1: (q>31 ? 31:q); 
               } 
 
               //stream: 5bits available 
 
               mv[3] = mv[2] = mv[1] = mv[0] = 0; 
 
               RescuePredict(pos);  //Restore AC_DC values 
               BlockIntra(pos, coded_block_pattern); 
            }else{ 
               //return; 
               soft_idct.BeginBlock(POSX(pos), POSY(pos)); 
 
               block_update_data[pos] |= RESCUE; // set rescue needed flag 
 
               int coded_block_pattern = ((15-bit_reader.GetCodedBlockPatternLuminance()) << 2) | mcbpc; 
 
               //stream: 8bits available 
 
               // we will use mv[4],mv[5] for temporary purposes 
               // in the next macroblock it will be overwrite (mv incremented by 4) 
 
               switch(mb_type){ 
               case INTER4V: 
                  for(j = 0; j<4; j++) 
                     mv[j] = bit_reader.GetMovementVectorPair(fcode_forward, GetPreviousMoveVector(j, pos)); 
                  mv[5] = mv[4] = MvChroma4(mv); 
                  soft_idct.SetMotionCompensationData(mv, NULL); 
                  break; 
 
               case INTER_Q: 
                  { 
                     int q = quantizer + DQtab[bit_reader.Get(2)]; 
                     quantizer = q<1 ? 1: (q>31 ? 31:q); 
                  } 
 
               default: //INTER 
                  mv[3] = mv[2] = mv[1] = mv[0] = bit_reader.GetMovementVectorPair(fcode_forward, GetPreviousMoveVector(0, pos)); 
                  mv[5] = mv[4] = MvChroma(mv[0]); 
 
                  soft_idct.SetMotionCompensationData(mv, NULL); 
                  break; 
               } 
               BlockInter(coded_block_pattern); 
            } 
         }else{ 
                              //not coded macroblock 
            int n = block_update_base_indx + (block_update_data[pos]&0x7f); 
            block_update_data[pos] |= RESCUE; 
 
                              //copy needed or the buffer already has this block? 
            if(last_frame_index < n) 
               soft_idct.Copy16x16(POSX(pos), POSY(pos), 0); 
 
            mv[3] = mv[2] = mv[1] = mv[0] = 0; 
         } 
      } 
   } 
   PROF_E(PROF_P_VOP); 
} 
 
//---------------------------- 
 
#define vld_code \ 
   code = ShowBits(12); \ 
   if(code >> 9) \ 
      code = (code >> 5) - 16 + TABLE_1; \ 
   else if (code >> 7) \ 
      code = (code >> 2) - 32 + TABLE_2; \ 
   else /* if (code >= 8)  but we don't care about invalid huffman codes */ \ 
      code = code - 8 + TABLE_3;          \ 
   code = table[code];                    \ 
   FlushBits(code >> 12); 
 
#define TABLE_1         0 
#define TABLE_2         112 
#define TABLE_3         112+96 
#define TABLE_LMAX      112+96+120 
#define TABLE_RMAX      112+96+120+64 
 
//---------------------------- 
 
int C_mp4_decode_imp::Vld_block_mpeg(const word *table, const byte *scan, int len){ 
 
   int q_add = 0; 
   int q_scale = quantizer << 1; 
   byte *q_matrix = quant_matrix[0]; 
   const byte *bit_ptr = bit_reader.ptr; 
   int bitpos = bit_reader.pos, bits = bit_reader.bits; 
 
   if(table == vld_inter){ 
      q_add = quantizer; 
      q_matrix += 8*8; 
   } 
 
   do {// event vld 
      int code,level; 
 
      LoadBits(); 
      vld_code; 
      level = code & 31; // 0x7FFF ESCAPE -> level = 31 
      if(level != 31){ 
         code >>= 5; 
         code &= 127; 
         len += code; // last|run 
      }else{ 
         LoadBits(); 
                              //this value is escaped 
         if(!GetBits1()){ 
                              //Type 1 
            vld_code; 
            level = code & 31; 
            code >>= 5; 
            code &= 127; 
            len   += code; // last|run 
            level += ((byte*)(table+TABLE_LMAX))[code]; // table_lmax[last][run] 
         }else 
         if(!GetBits1()){ 
                              //Type 2    
            vld_code; 
            len += (code >> 5) & 127; 
            code &= 31; 
            level = code; 
            if (code > 11) code = 11; 
            if (len >= 64) code += 16; // add (last bit << 4) 
            len += ((byte*)(table+TABLE_RMAX))[code]; // table_rmax[last][min(11,level)] 
         }else{ 
                              //Type 3  - fixed length codes 
            code = ShowBits(20); 
            FlushBits(21); 
            level = (code << 20) >> 20; //sign extend the lower 12 bits 
            if (level < 0) 
               level = -level; 
            len   += code >> 13;  // last|run 
            level *= q_scale; 
            level += q_add; 
            level *= q_matrix[scan[len & 63]]; 
            level >>= 4; 
 
            if ((code << 20) < 0) 
               level = -level; 
 
            block_8x8[scan[len & 63]] = (short)level; 
            continue; 
         } 
      } 
 
      code = scan[len & 63]; 
 
      level *= q_scale; 
      level += q_add; 
      level *= q_matrix[code]; 
      level >>= 4; 
 
      if(GetBits1())  
         level = -level; 
 
      block_8x8[code] = (short)level; 
   }while(++len < 64); 
 
   bit_reader.pos = bitpos; 
   bit_reader.ptr = bit_ptr; 
   bit_reader.bits = bits; 
   return len - 64; 
} 
 
//---------------------------- 
 
#define vld_write \ 
   block_8x8[scan[len & 63]] = (short)level; \ 
   if(++len < 64) continue; \ 
   bit_reader.pos = bitpos; \ 
   bit_reader.ptr = bit_ptr; \ 
   bit_reader.bits = bits; \ 
   return len - 64; 
 
//---------------------------- 
 
#define vld_quant \ 
   level *= q_scale; \ 
   level += q_add; \ 
   if(GetBits1()) \ 
      level = -level; 
 
//---------------------------- 
 
int C_mp4_decode_imp::Vld_block_mpeg4(const word *table, const byte *scan, int len){ 
 
   int q_scale = quantizer << 1; 
   int q_add = (quantizer-1) | 1; 
   const byte *bit_ptr = bit_reader.ptr; 
   int bitpos = bit_reader.pos, bits = bit_reader.bits; 
 
   while(true){ 
                              //event vld 
      int code; 
      LoadBits(); 
 
      vld_code; 
      int level = code&31; // 0x7FFF ESCAPE -> level = 31 
      if(level != 31){ 
         code >>= 5; 
         code &= 127; 
         len += code; // last|run 
         vld_quant 
         vld_write 
      }else{ 
                              //this value is escaped 
         LoadBits(); 
         if(!GetBits1()){ 
                              //Type 1 
            vld_code; 
            level = code & 31; 
            code >>= 5; 
            code &= 127; 
            len   += code; // last|run 
            level += ((byte*)(table+TABLE_LMAX))[code]; // table_lmax[last][run] 
            vld_quant 
            vld_write 
         }else 
         if(!GetBits1()){ 
                              //Type 2 
            vld_code; 
            len += (code >> 5) & 127; 
            code &= 31; 
            level = code; 
            if (code > 11) code = 11; 
            if (len >= 64) code += 16; // add (last bit << 4) 
            len += ((byte*)(table+TABLE_RMAX))[code]; // table_rmax[last][min(11,level)] 
            vld_quant 
            vld_write 
         }else{ 
                              //Type 3  - fixed length codes 
            code = ShowBits(21) >> 1; 
            FlushBits(21); 
            level = (code << 20) >> 20; //sign extend the lower 12 bits 
            level *= q_scale; 
            len   += code >> 13;  // last|run 
            if(level > 0) 
               level += q_add; 
            else 
               level -= q_add; 
 
            vld_write; 
         } 
      } 
   } 
} 
 
//---------------------------- 
 
static const byte def_quant_inter[64] = { 
   16, 17, 18, 19, 20, 21, 22, 23, 
   17, 18, 19, 20, 21, 22, 23, 24, 
   18, 19, 20, 21, 22, 23, 24, 25, 
   19, 20, 21, 22, 23, 24, 26, 27, 
   20, 21, 22, 23, 25, 26, 27, 28, 
   21, 22, 23, 24, 26, 27, 28, 30, 
   22, 23, 24, 26, 27, 28, 30, 31, 
   23, 24, 25, 27, 28, 30, 31, 33 
}, def_quant_intra[64] = { 
    8, 17, 18, 19, 21, 23, 25, 27, 
   17, 18, 19, 21, 23, 25, 27, 28, 
   20, 21, 22, 23, 24, 26, 28, 30, 
   21, 22, 23, 24, 26, 28, 30, 32, 
   22, 23, 24, 26, 28, 30, 32, 35, 
   23, 24, 26, 28, 30, 32, 35, 38, 
   25, 26, 28, 30, 32, 35, 38, 41, 
   27, 28, 30, 32, 35, 38, 41, 45 
}; 
 
//---------------------------- 
 
void C_mp4_decode_imp::ReadHeader_vol(){ 
 
   bit_reader.Flush(32+1+8); // start_code + random_accessible_vol + video_object_type_indication 
   bit_reader.Load(); 
     
   int visual_object_layer_verid; 
   if(bit_reader.Get1()){ 
                              //object layer identifier 
      visual_object_layer_verid = bit_reader.Get(4); 
      bit_reader.Flush(3); // video_object_layer_priority 
   }else{ 
      visual_object_layer_verid = 1; 
   } 
 
   int aspect = bit_reader.Get(4); // aspect ratio 
   if(aspect == ASPECT_CUSTOM) { 
      bit_reader.Load(); 
      bit_reader.Get(8); //aspect_width 
      bit_reader.Get(8); //aspect_height 
   } 
 
   if(bit_reader.Get1()){ // vol control parameters 
      bit_reader.Flush(2); // chroma_format 
      has_b_frames = !bit_reader.Get1(); 
      if(has_b_frames) // b-frames 
         SetIDCTBufferCount(3); 
      if(bit_reader.Get1()) // vbv parameters 
         bit_reader.Flush(15+1+15+1+15+1+3+11+1+15+1); 
   } 
 
   bit_reader.Flush(2+1); // shape + marker 
   bit_reader.Load(); 
 
   time_increment_resolution = bit_reader.Get(16); 
   if(time_increment_resolution <= 0) 
      time_increment_resolution = 1; 
   time_increment_bits = FindHighestBit(time_increment_resolution-1)+1; 
 
   bit_reader.Flush(1); // marker 
   if(bit_reader.Get1()) //fixed_vop_rate 
      bit_reader.Flush(time_increment_bits); 
 
   bit_reader.Flush(1); // marker 
   bit_reader.Load(); 
   int width = bit_reader.Get(13); //width 
   bit_reader.Flush(1); 
   bit_reader.Load(); 
   int height = bit_reader.Get(13); //height 
   bit_reader.Flush(1); // marker 
   interlaced = bit_reader.Get1(); 
   /* 
   if (interlaced && showerror && NotSupported(MPEG4_ERROR_INTERLACE)) // interlace 
      return; 
      */ 
 
   bit_reader.Flush(1); // obmc_disable 
 
   if(width!=video_sx || height!=video_sy){ 
      valid_vol = false; 
      return; 
   } 
 
   bit_reader.Load(); 
   sprite_type = (E_SPRITE_TYPE)bit_reader.Get((visual_object_layer_verid==1) ? 1 : 2); 
   if(sprite_type == SPRITE_STATIC || sprite_type == SPRITE_GMC){ 
      //assert(0); 
      //if(showerror && NotSupported(MPEG4_ERROR_GMC)) 
         //return; 
 
      if(sprite_type != SPRITE_GMC) { 
         bit_reader.Flush(13+1+13+1+13+1+13+1); //width+marker+height+marker+left+marker+top+marker 
         bit_reader.Load(); 
      } 
                              //sprite warping points 
      bit_reader.Get(6); 
                              //sprite warping accuracy 
      bit_reader.Get(2); 
      bit_reader.Get1(); // brightness change not supported 
 
      if(sprite_type != SPRITE_GMC) 
         bit_reader.Flush(1); // low_latency_sprite_enable 
   } 
       
   bit_reader.Load(); 
   if(bit_reader.Get1()){  // not 8 bit 
      quant_precision = bit_reader.Get(4); 
      bit_reader.Flush(4); // bit per pixel 
   }else  
      quant_precision = 5; 
 
   bool quant_type = bit_reader.Get1(); 
   if(quant_type){ // quant type 
      if(bit_reader.Get1())  // load intra 
         bit_reader.GetQuantMatrix(quant_matrix[0]); 
      else 
         MemCpy(quant_matrix[0], def_quant_intra, sizeof(def_quant_intra)); 
 
      if(bit_reader.Get1())  // load inter 
         bit_reader.GetQuantMatrix(quant_matrix[1]); 
      else 
         MemCpy(quant_matrix[1], def_quant_inter, sizeof(def_quant_inter)); 
   } 
 
   bit_reader.Load(); 
   if(visual_object_layer_verid != 1) { 
      if(bit_reader.Get1()){// && showerror)// && NotSupported(MPEG4_ERROR_QPEL)) 
                              //quarter-pel 
         assert(0); 
         //return; 
      } 
   }  
 
   bit_reader.Flush(1); // complexity estimation 
   bit_reader.Flush(1); // resync marker disabled (?) 
 
   if(bit_reader.Get1()){ 
                              //partitioning 
      bit_reader.Flush(1); 
      assert(0); 
      //if(showerror)// && NotSupported(MPEG4_ERROR_PARTITIONING)) 
         //return; 
      //return; 
   } 
 
   if(visual_object_layer_verid != 1) { 
      if(bit_reader.Get1()) //new pred not supported 
         bit_reader.Flush(2+1); // req msg type, seg type 
      bit_reader.Flush(1); //reduced res not supported 
   } 
 
   if(bit_reader.Get1()) // scalability 
      bit_reader.Flush(1+4+1+5+5+5+5+1); // not supported 
 
   valid_vol = true; 
   //showerror = 0; 
} 
 
//---------------------------- 
 
static const byte tab_intra_dc_threshold[] = { 
   32, 
   13, 
   15, 
   17, 
   19, 
   21, 
   23, 
   1, 
}; 
 
//---------------------------- 
 
void C_mp4_decode_imp::ReadHeader_vop(E_FRAME_TYPE &frame_prediction_type){ 
 
   bit_reader.Flush(32); 
   bit_reader.Load(); 
 
   frame_prediction_type = (E_FRAME_TYPE)bit_reader.Get(2); 
 
   while(bit_reader.Get1()) 
      bit_reader.Load(); 
 
   bit_reader.Flush(1); //marker 
   bit_reader.Load(); 
 
   int time_increment = bit_reader.Get(time_increment_bits); 
   if(time_increment_resolution){ 
      if(frame_prediction_type != B_VOP){ 
         time_pp = (time_increment_resolution + time_increment - last_ref_time) % time_increment_resolution; 
         last_ref_time = time_increment; 
      }else{ 
         time_bp = (time_increment_resolution + last_ref_time - time_increment) % time_increment_resolution; 
      } 
   } 
 
   bit_reader.Flush(1); //marker 
   bit_reader.Load(); 
 
   if(!bit_reader.Get1()){ 
      frame_prediction_type = N_VOP; 
      return; 
   }   
 
   bool rounding = false; 
   if(frame_prediction_type == P_VOP || frame_prediction_type == S_VOP) 
      rounding = bit_reader.Get1(); 
   soft_idct.SetRounding(rounding); 
 
   intra_dc_threshold = tab_intra_dc_threshold[bit_reader.Get(3)]; 
 
   if(interlaced) 
      bit_reader.Flush(2); //top_field_first, alternate_scan 
 
   //if (frame_prediction_type == S_VOP && (sprite_type == SPRITE_STATIC || sprite_type == SPRITE_GMC)) 
      //spritetrajectory(this); 
   quantizer = bit_reader.Get(quant_precision); // vop quant 
 
   bit_reader.Load(); 
   if(frame_prediction_type != I_VOP)  
      fcode_forward = bit_reader.Get(3);  
 
   if(frame_prediction_type == B_VOP)  
      fcode_backward = bit_reader.Get(3);  
} 
 
//---------------------------- 
 
bool C_mp4_decode_imp::ReadFrameHeaders(E_FRAME_TYPE &frame_prediction_type){ 
 
   while(true){ 
      bit_reader.ByteAlign(); 
      bit_reader.Load(); 
 
      if(bit_reader.Eof()) 
         return false; 
      int code = bit_reader.Show(32); 
 
      if((code & ~VOL_START_CODE_MASK) == VOL_START_CODE){ 
         ReadHeader_vol(); 
      }else 
      if(code == VOP_START_CODE){ 
         ReadHeader_vop(frame_prediction_type); 
         return true; 
      }else 
         bit_reader.Flush(8); 
   } 
} 
 
//---------------------------- 
 
void C_mp4_decode_imp::BlockDirect(int pos, int dmv, int coded_block_pattern){ 
 
   int fmv[6]; 
   int bmv[6]; 
 
   int *mv = &mv_buf[pos*4]; 
    
   for(int j=0; j<4; ++j,++mv){ 
      int dx, dy, v; 
 
      v = trb * MOVE_VECTOR_X(*mv); 
      if(v<0) //best code for (v<0) 
         --v; 
      dx = v >> 8; 
      if(dx<0) ++dx; 
 
      v = trb * MOVE_VECTOR_Y(*mv); 
      if(v<0) //best code for (v<0) 
         --v; 
      dy = v >> 8; 
      if(dy<0) ++dy; 
 
      dx += MOVE_VECTOR_X(dmv); 
      dy += MOVE_VECTOR_Y(dmv); 
 
      fmv[j] = MAKE_MOVEMENT_VECTOR_PAIR(dx, dy); 
       
      if(dmv & 0xffff) 
         dx -= MOVE_VECTOR_X(*mv); 
      else{ 
         v = trb_trd * MOVE_VECTOR_X(*mv); 
         if(v<0) //best code for (v<0) 
            --v; 
         dx = v >> 8; 
         if(dx<0) ++dx; 
      } 
      if(dmv & 0xffff0000) 
         dy -= MOVE_VECTOR_Y(*mv); 
      else{ 
         v = trb_trd * MOVE_VECTOR_Y(*mv); 
         if(v<0) //best code for (v<0) 
            --v; 
         dy = v >> 8; 
         if(dy<0) ++dy; 
      } 
      bmv[j] = MAKE_MOVEMENT_VECTOR_PAIR(dx, dy); 
   } 
 
   fmv[5]=fmv[4]=MvChroma4(fmv); 
   bmv[5]=bmv[4]=MvChroma4(bmv); 
 
   soft_idct.SetMotionCompensationData(bmv, fmv); 
 
   BlockInter(coded_block_pattern); 
} 
 
//---------------------------- 
 
void C_mp4_decode_imp::BVOP(){ 
 
   PROF_S(PROF_B_VOP); 
   int fmv[6]; 
   int bmv[6]; 
   int pos; 
 
   resync_marker_bits = 16 + Max(fcode_backward, fcode_forward); 
 
   int last_frame_index = soft_idct.GetPreviousDstFrameIndex(); 
 
   for(pos=0; pos=mb_pos_end) 
               return; 
            fprev = bprev = 0; 
         } 
 
         int n = block_update_base_indx + (block_update_data[pos]&0x7f); 
 
                              //did the last refframe touch this block? 
         if(last_ref_frame_index > n){ 
                              //no change: this block is the same in both backward and forward buffer 
 
            // copy needed? 
            if(last_frame_index < n && last_frame_index >= -last_ref_frame_index) 
               soft_idct.Copy16x16(POSX(pos),POSY(pos),1); 
         }else{ 
                              //stream: 24bits available 
            soft_idct.BeginBlock(POSX(pos), POSY(pos)); 
 
            if(!bit_reader.Get1()){ 
               int mb_type; 
               n = bit_reader.Get(1); 
 
               for(mb_type=0; mb_type<=3; ++mb_type){ 
                  if(bit_reader.Get1()) 
                     break; 
               } 
 
               int coded_block_pattern; 
 
                              //stream: 19bits available 
               if(!n) 
                  coded_block_pattern = bit_reader.Get(6); 
               else 
                  coded_block_pattern = 0; 
 
               //stream: 13bits available 
 
               if(mb_type != DIRECT){ 
                  if(coded_block_pattern && bit_reader.Get1()){ 
                     int q = quantizer + (bit_reader.Get1() ? 2:-2); 
                     quantizer = q<1 ? 1: (q>31 ? 31:q); 
                  } 
 
                  switch(mb_type){ 
                  case BACKWARD: 
                     bmv[3] = bmv[2] = bmv[1] = bmv[0] = bprev = bit_reader.GetMovementVectorPair(fcode_backward, bprev); 
                     bmv[5] = bmv[4] = MvChroma(bprev); 
                     soft_idct.SetMotionCompensationData(bmv, NULL); 
                     break; 
 
                  case FORWARD: 
                     fmv[3]=fmv[2]=fmv[1]=fmv[0]=fprev = bit_reader.GetMovementVectorPair(fcode_forward, fprev); 
                     fmv[5]=fmv[4]=MvChroma(fprev); 
                     soft_idct.SetMotionCompensationData(NULL, fmv); 
                     break; 
 
                  default: //case INTERPOLATE: 
                     fmv[3]=fmv[2]=fmv[1]=fmv[0]=fprev = bit_reader.GetMovementVectorPair(fcode_forward, fprev); 
                     fmv[5]=fmv[4]=MvChroma(fprev); 
                     bmv[3]=bmv[2]=bmv[1]=bmv[0]=bprev = bit_reader.GetMovementVectorPair(fcode_backward, bprev); 
                     bmv[5]=bmv[4]=MvChroma(bprev); 
                     soft_idct.SetMotionCompensationData(bmv, fmv); 
                     break; 
                  } 
                  BlockInter(coded_block_pattern); 
               }else{ 
                  BlockDirect(pos, bit_reader.GetMovementVectorPair(1, 0), coded_block_pattern); 
               } 
            }else{ 
               BlockDirect(pos, 0, 0); 
            } 
         } 
      } 
   } 
   PROF_E(PROF_B_VOP); 
} 
 
//---------------------------- 
 
int C_mp4_decode_imp::GetPreviousMoveVector(int block_num, int pos){ 
 
   int p1, p2, p3; 
   int *mv = &mv_buf[(pos & mv_bufmask)*4]; 
   int *mvlast = &mv_buf[((pos-MAX_MACROBLOCKS_X) & mv_bufmask)*4]; 
 
   // we are allowed to access borders on left and right (always zero) 
   switch(block_num){ 
   case 0: 
      if(pos == resyncpos)  
         return 0; 
      if(pos < resyncpos+MAX_MACROBLOCKS_X){ 
         if(pos != resyncpos+MAX_MACROBLOCKS_X-1) 
            return mv[-4+1]; 
 
         if(POSX(pos)==0) 
            return mvlast[4+2]; 
 
         p1 = mv[-4+1]; 
         p2 = 0; 
         p3 = mvlast[4+2]; 
         break; 
      } 
      p1 = mv[-4+1]; 
      p2 = mvlast[2]; 
      p3 = mvlast[4+2]; 
      break; 
   case 1: 
      if(pos < resyncpos+MAX_MACROBLOCKS_X){ 
         if(pos != resyncpos+MAX_MACROBLOCKS_X-1) 
            return mv[0]; 
 
         p1 = mv[0]; 
         p2 = 0; 
         p3 = mvlast[4+2]; 
         break; 
      } 
      p1 = mv[0]; 
      p2 = mvlast[3]; 
      p3 = mvlast[4+2]; 
      break;    
   case 2: 
      p1 = mv[-4+3]; 
      p2 = mv[0]; 
      p3 = mv[1]; 
      if(pos == resyncpos) 
         p1 = 0; 
      break; 
   default: //3 
      p1 = mv[2]; 
      p2 = mv[0]; 
      p3 = mv[1]; 
      break; 
   } 
   int temp = Min(Max(p1, p2), Min(Max(p2, p3), Max(p1, p3))); 
   p1 = (p1<<16)>>16; 
   p2 = (p2<<16)>>16; 
   p3 = (p3<<16)>>16; 
   p1 = Min(Max(p1, p2), Min(Max(p2, p3), Max(p1, p3))); 
 
   return ((temp&0xffff0000) | (p1&0xffff)); 
} 
 
//---------------------------- 
 
bool C_mp4_decode_imp::ResyncMarker(){ 
 
   int bits = bit_reader.BitsToNextByte(); 
   int code = bit_reader.Show(bits); 
 
   return (code == (1 << (bits-1)) - 1) && 
      (bit_reader.ShowLarge(bits + resync_marker_bits) &  
      ((1 << resync_marker_bits)-1)) == 1; 
} 
 
//---------------------------- 
 
int C_mp4_decode_imp::ReSync(){ 
 
   int i = bit_reader.BitsToNextByte(); 
   bit_reader.Flush(i); 
   bit_reader.Load(); 
 
   for(i=0; i<32; ++i){ 
      if(bit_reader.Get1()) 
         break; 
      if(i==16) 
         bit_reader.Load(); 
   } 
   if(i+1 != resync_marker_bits) 
      return mb_pos_end; 
 
   bit_reader.Load(); 
   int mb_bits = FindHighestBit(mb_xsize*mb_ysize)+1; 
   int x = bit_reader.Get(mb_bits); 
   int y = x/mb_xsize; 
   x -= y*mb_xsize; 
 
   bit_reader.Load(); 
   i = bit_reader.Get(quant_precision); 
   if(i) 
      quantizer = i; 
 
   if(bit_reader.Get1()){ 
                              //extension 
      return mb_pos_end; //not supported yet 
   } 
 
   for (i = 0; i < 4*MAX_MACROBLOCKS_X*2; i++) 
      dc_lum[i] = 1024; 
 
   for (i = 0; i < 2*MAX_MACROBLOCKS_X; i++) { 
      dc_chr[0][i] = 1024; 
      dc_chr[1][i] = 1024; 
   } 
 
   MemSet(ac_left_lum,0,sizeof(ac_left_lum)); 
   MemSet(ac_top_lum,0,sizeof(ac_top_lum)); 
   MemSet(ac_left_chr,0,sizeof(ac_left_chr)); 
   MemSet(ac_top_chr,0,sizeof(ac_top_chr)); 
 
   resyncpos = x+y*MAX_MACROBLOCKS_X; 
   return resyncpos; 
} 
 
//---------------------------- 
 
bool C_mp4_decode_imp::Init(){ 
 
   if(video_sx > (MAX_MACROBLOCKS_X-1)*16 || video_sy > (MAX_MACROBLOCKS_Y-1)*16) 
      return false; 
   return SetIDCTBufferCount(2); 
} 
 
//---------------------------- 
 
C_mp4_decode_imp::C_mp4_decode_imp(dword sx, dword sy, void *_prof): 
   has_b_frames(false), 
   quant_precision(5), 
   video_sx(sx), video_sy(sy), 
   soft_idct(sx, sy), 
   valid_vol(false), 
   interlaced(false), 
   ac_pred_flag(false), 
   sprite_type(SPRITE_NO), 
   frame_index(0), 
   last_ref_frame_index(-1), 
   mb_pos_end(0), 
   block_update_base_indx(0), 
   mb_xsize((sx + 15)>>4), mb_ysize((sy + 15)>>4), 
   quantizer(0), 
   intra_dc_threshold(0), 
   fcode_forward(0), 
   fcode_backward(0), 
   resync_marker_bits(0), 
   time_increment_resolution(30000), 
   time_increment_bits(15), 
   last_ref_time(0), 
   time_pp(0), 
   time_bp(0), 
   last_bframe(0), 
   refframe(0), 
   resyncpos(0), 
   quant_type(false), 
   mv_bufmask(0), 
   move_buf_invalid(0), 
#ifdef PROFILE 
   prof((C_profiler*)_prof), 
#endif 
   packed_b_size(0), 
	mv_buf(NULL), 
	_mv_buf(NULL) 
{ 
   MemSet(quant_matrix, 0, sizeof(quant_matrix)); 
   MemSet(dc_lum, 0, sizeof(dc_lum)); 
   MemSet(ac_left_lum, 0, sizeof(ac_left_lum)); 
   MemSet(ac_top_lum, 0, sizeof(ac_top_lum)); 
   MemSet(dc_chr, 0, sizeof(dc_chr)); 
   MemSet(ac_left_chr, 0, sizeof(ac_left_chr)); 
   MemSet(ac_top_chr, 0, sizeof(ac_top_chr)); 
   MemSet(_mv_bufrow, 0, sizeof(_mv_bufrow)); 
 
   block_8x8 = (short*)(((dword)(_block) + 15) & ~15); 
   mb_pos_end = mb_ysize * MAX_MACROBLOCKS_X; 
 
   int i; 
   for(i = 0; i < 4*MAX_MACROBLOCKS_X*2; i++) 
      dc_lum[i] = 1024; 
   for(i = 0; i < 2*MAX_MACROBLOCKS_X; i++){ 
      dc_chr[0][i] = 1024; 
      dc_chr[1][i] = 1024; 
   } 
} 
 
//---------------------------- 
 
C_mp4_decode *CreateMp4Decode(dword sx, dword sy, void *profiler){ 
 
   C_mp4_decode_imp *dec = new C_mp4_decode_imp(sx, sy, profiler); 
   if(dec){ 
      if(!dec->Init()){ 
         delete dec; 
         dec = NULL; 
      } 
   } 
   return dec; 
} 
 
//----------------------------