Counter Strike : Global Offensive Source Code
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.

307 lines
7.8 KiB

  1. //====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "tier0/platform.h"
  7. #include "mpafile.h" // also includes vbrheader.h
  8. #include "tier0/dbg.h"
  9. // NOTE: This has to be the last file included!
  10. #include "tier0/memdbgon.h"
  11. #ifndef MAKEFOURCC
  12. #define MAKEFOURCC(ch0, ch1, ch2, ch3) \
  13. ((uint32)(BYTE)(ch0) | ((uint32)(BYTE)(ch1) << 8) | \
  14. ((uint32)(BYTE)(ch2) << 16) | ((uint32)(BYTE)(ch3) << 24 ))
  15. #endif //defined(MAKEFOURCC)
  16. // XING Header offset: 1. index = lsf, 2. index = mono
  17. uint32 CVBRHeader::m_dwXINGOffsets[2][2] =
  18. {
  19. // MPEG 1 (not mono, mono)
  20. { 32 + MPA_HEADER_SIZE, 17 + MPA_HEADER_SIZE },
  21. // MPEG 2/2.5
  22. { 17 + MPA_HEADER_SIZE, 9 + MPA_HEADER_SIZE }
  23. };
  24. // first test with this static method, if it does exist
  25. bool CVBRHeader::IsVBRHeaderAvailable( CMPAFile* pMPAFile, VBRHeaderType& HeaderType, uint32& dwOffset )
  26. {
  27. Assert(pMPAFile);
  28. // where does VBR header begin (XING)
  29. uint32 dwNewOffset = dwOffset + m_dwXINGOffsets[pMPAFile->m_pMPAHeader->IsLSF()][pMPAFile->m_pMPAHeader->IsMono()];
  30. // check for XING header first
  31. if( CheckXING( pMPAFile, dwNewOffset ) )
  32. {
  33. HeaderType = XINGHeader;
  34. // seek offset back to header begin
  35. dwOffset = dwNewOffset - 4;
  36. return true;
  37. }
  38. // VBRI header always at fixed offset
  39. dwNewOffset = dwOffset + 32 + MPA_HEADER_SIZE;
  40. if( CheckVBRI( pMPAFile, dwNewOffset ) )
  41. {
  42. HeaderType = VBRIHeader;
  43. // seek offset back to header begin
  44. dwOffset = dwNewOffset - 4;
  45. return true;
  46. }
  47. HeaderType = NoHeader;
  48. return false;
  49. }
  50. CVBRHeader::CVBRHeader( CMPAFile* pMPAFile, VBRHeaderType HeaderType, uint32 dwOffset ) :
  51. m_pMPAFile( pMPAFile ), m_pnToc(NULL), m_HeaderType( HeaderType ), m_dwOffset(dwOffset), m_dwFrames(0), m_dwBytes(0)
  52. {
  53. switch( m_HeaderType )
  54. {
  55. case NoHeader:
  56. // no Header found
  57. throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
  58. break;
  59. case XINGHeader:
  60. if( !ExtractXINGHeader( m_dwOffset ) )
  61. throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
  62. break;
  63. case VBRIHeader:
  64. if( !ExtractVBRIHeader( m_dwOffset ) )
  65. throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
  66. break;
  67. }
  68. // calc bitrate
  69. if( m_dwBytes > 0 && m_dwFrames > 0 )
  70. {
  71. // calc number of seconds
  72. m_dwBytesPerSec = m_pMPAFile->m_pMPAHeader->GetBytesPerSecond( m_dwFrames, m_dwBytes );
  73. }
  74. else // incomplete header found
  75. {
  76. throw CMPAException( CMPAException::IncompleteVBRHeader, pMPAFile->GetFilename(), NULL, false );
  77. }
  78. }
  79. bool CVBRHeader::CheckID( CMPAFile* pMPAFile, char ch0, char ch1, char ch2, char ch3, uint32& dwOffset )
  80. {
  81. return ( pMPAFile->ExtractBytes( dwOffset, 4 ) == MAKEFOURCC( ch3, ch2, ch1, ch0 ) );
  82. }
  83. bool CVBRHeader::CheckXING( CMPAFile* pMPAFile, uint32& dwOffset )
  84. {
  85. // XING ID found?
  86. if( !CheckID( pMPAFile, 'X', 'i', 'n', 'g', dwOffset) && !CheckID( pMPAFile, 'I', 'n', 'f', 'o', dwOffset) )
  87. return false;
  88. return true;
  89. }
  90. bool CVBRHeader::CheckVBRI( CMPAFile* pMPAFile, uint32& dwOffset )
  91. {
  92. // VBRI ID found?
  93. if( !CheckID( pMPAFile, 'V', 'B', 'R', 'I', dwOffset ) )
  94. return false;
  95. return true;
  96. }
  97. // currently not used
  98. bool CVBRHeader::ExtractLAMETag( uint32 dwOffset )
  99. {
  100. // LAME ID found?
  101. if( !CheckID( m_pMPAFile, 'L', 'A', 'M', 'E', dwOffset ) && !CheckID( m_pMPAFile, 'G', 'O', 'G', 'O', dwOffset ) )
  102. return false;
  103. return true;
  104. }
  105. bool CVBRHeader::ExtractXINGHeader( uint32 dwOffset )
  106. {
  107. /* XING VBR-Header
  108. size description
  109. 4 'Xing' or 'Info'
  110. 4 flags (indicates which fields are used)
  111. 4 frames (optional)
  112. 4 bytes (optional)
  113. 100 toc (optional)
  114. 4 a VBR quality indicator: 0=best 100=worst (optional)
  115. */
  116. if( !CheckXING( m_pMPAFile, dwOffset ) )
  117. return false;
  118. uint32 dwFlags;
  119. // get flags (mandatory in XING header)
  120. dwFlags = m_pMPAFile->ExtractBytes( dwOffset, 4 );
  121. // extract total number of frames in file
  122. if(dwFlags & FRAMES_FLAG)
  123. m_dwFrames = m_pMPAFile->ExtractBytes(dwOffset,4);
  124. // extract total number of bytes in file
  125. if(dwFlags & BYTES_FLAG)
  126. m_dwBytes = m_pMPAFile->ExtractBytes(dwOffset,4);
  127. // extract TOC (for more accurate seeking)
  128. if (dwFlags & TOC_FLAG)
  129. {
  130. m_dwTableSize = 100;
  131. m_pnToc = new int[m_dwTableSize];
  132. if( m_pnToc )
  133. {
  134. for(uint32 i=0;i<m_dwTableSize;i++)
  135. m_pnToc[i] = m_pMPAFile->ExtractBytes( dwOffset, 1 );
  136. }
  137. }
  138. m_dwQuality = (uint32)-1;
  139. if(dwFlags & VBR_SCALE_FLAG )
  140. m_dwQuality = m_pMPAFile->ExtractBytes(dwOffset, 4);
  141. return true;
  142. }
  143. bool CVBRHeader::ExtractVBRIHeader( uint32 dwOffset )
  144. {
  145. /* FhG VBRI Header
  146. size description
  147. 4 'VBRI' (ID)
  148. 2 version
  149. 2 delay
  150. 2 quality
  151. 4 # bytes
  152. 4 # frames
  153. 2 table size (for TOC)
  154. 2 table scale (for TOC)
  155. 2 size of table entry (max. size = 4 byte (must be stored in an integer))
  156. 2 frames per table entry
  157. ?? dynamic table consisting out of frames with size 1-4
  158. whole length in table size! (for TOC)
  159. */
  160. if( !CheckVBRI( m_pMPAFile, dwOffset ) )
  161. return false;
  162. // extract all fields from header (all mandatory)
  163. m_dwVersion = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  164. m_fDelay = (float)m_pMPAFile->ExtractBytes(dwOffset, 2 );
  165. m_dwQuality = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  166. m_dwBytes = m_pMPAFile->ExtractBytes(dwOffset, 4 );
  167. m_dwFrames = m_pMPAFile->ExtractBytes(dwOffset, 4 );
  168. m_dwTableSize = m_pMPAFile->ExtractBytes(dwOffset, 2 ) + 1; //!!!
  169. m_dwTableScale = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  170. m_dwBytesPerEntry = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  171. m_dwFramesPerEntry = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  172. // extract TOC (for more accurate seeking)
  173. m_pnToc = new int[m_dwTableSize];
  174. if( m_pnToc )
  175. {
  176. for ( unsigned int i = 0 ; i < m_dwTableSize ; i++)
  177. {
  178. m_pnToc[i] = m_pMPAFile->ExtractBytes(dwOffset, m_dwBytesPerEntry );
  179. }
  180. }
  181. return true;
  182. }
  183. CVBRHeader::~CVBRHeader(void)
  184. {
  185. if( m_pnToc )
  186. delete[] m_pnToc;
  187. }
  188. // get byte position for percentage value (fPercent) of file
  189. bool CVBRHeader::SeekPoint(float fPercent, uint32& dwSeekPoint)
  190. {
  191. if( !m_pnToc || m_dwBytes == 0 )
  192. return false;
  193. if( fPercent < 0.0f )
  194. fPercent = 0.0f;
  195. if( fPercent > 100.0f )
  196. fPercent = 100.0f;
  197. switch( m_HeaderType )
  198. {
  199. case XINGHeader:
  200. dwSeekPoint = SeekPointXING( fPercent );
  201. break;
  202. case VBRIHeader:
  203. dwSeekPoint = SeekPointVBRI( fPercent );
  204. break;
  205. }
  206. return true;
  207. }
  208. uint32 CVBRHeader::SeekPointXING(float fPercent) const
  209. {
  210. // interpolate in TOC to get file seek point in bytes
  211. int a;
  212. float fa, fb, fx;
  213. a = (int)fPercent;
  214. if( a > 99 ) a = 99;
  215. fa = (float)m_pnToc[a];
  216. if( a < 99 )
  217. {
  218. fb = (float)m_pnToc[a+1];
  219. }
  220. else
  221. {
  222. fb = 256.0f;
  223. }
  224. fx = fa + (fb-fa)*(fPercent-a);
  225. uint32 dwSeekpoint = (int)((1.0f/256.0f)*fx*m_dwBytes);
  226. return dwSeekpoint;
  227. }
  228. uint32 CVBRHeader::SeekPointVBRI(float fPercent) const
  229. {
  230. return SeekPointByTimeVBRI( (fPercent/100.0f) * m_pMPAFile->m_pMPAHeader->GetLengthSecond( m_dwFrames ) * 1000.0f );
  231. }
  232. uint32 CVBRHeader::SeekPointByTimeVBRI(float fEntryTimeMS) const
  233. {
  234. unsigned int i=0, fraction = 0;
  235. uint32 dwSeekPoint = 0;
  236. float fLengthMS;
  237. float fLengthMSPerTOCEntry;
  238. float fAccumulatedTimeMS = 0.0f ;
  239. fLengthMS = (float)m_pMPAFile->m_pMPAHeader->GetLengthSecond( m_dwFrames ) * 1000.0f ;
  240. fLengthMSPerTOCEntry = fLengthMS / (float)m_dwTableSize;
  241. if ( fEntryTimeMS > fLengthMS )
  242. fEntryTimeMS = fLengthMS;
  243. while ( fAccumulatedTimeMS <= fEntryTimeMS )
  244. {
  245. dwSeekPoint += m_pnToc[i++];
  246. fAccumulatedTimeMS += fLengthMSPerTOCEntry;
  247. }
  248. // Searched too far; correct result
  249. fraction = ( (int)(((( fAccumulatedTimeMS - fEntryTimeMS ) / fLengthMSPerTOCEntry )
  250. + (1.0f/(2.0f*(float)m_dwFramesPerEntry))) * (float)m_dwFramesPerEntry));
  251. dwSeekPoint -= (uint32)((float)m_pnToc[i-1] * (float)(fraction)
  252. / (float)m_dwFramesPerEntry);
  253. return dwSeekPoint;
  254. }