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.

1184 lines
32 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "avi/iavi.h"
  7. #include "avi.h"
  8. #include "filesystem.h"
  9. #include "tier1/strtools.h"
  10. #include "tier1/utllinkedlist.h"
  11. #include "tier1/keyvalues.h"
  12. #include "materialsystem/imaterial.h"
  13. #include "materialsystem/imaterialsystem.h"
  14. #include "materialsystem/materialsystemutil.h"
  15. #include "materialsystem/itexture.h"
  16. #include "vtf/vtf.h"
  17. #include "pixelwriter.h"
  18. #include "tier3/tier3.h"
  19. #pragma warning( disable : 4201 )
  20. #define WIN32_LEAN_AND_MEAN
  21. #include <windows.h>
  22. #include <vfw.h>
  23. #pragma warning( default : 4201 )
  24. DWORD g_dwLastValidCodec = 0;
  25. //-----------------------------------------------------------------------------
  26. //
  27. // Class used to write out AVI files
  28. //
  29. //-----------------------------------------------------------------------------
  30. class CAviFile
  31. {
  32. public:
  33. CAviFile();
  34. void Init( const AVIParams_t& params, void *hWnd );
  35. void Shutdown();
  36. void AppendMovieSound( short *buf, size_t bufsize );
  37. void AppendMovieFrame( const BGR888_t *pRGBData );
  38. private:
  39. void Reset();
  40. void CreateVideoStreams( const AVIParams_t& params, void *hWnd );
  41. void CreateAudioStream();
  42. bool m_bValid;
  43. int m_nWidth;
  44. int m_nHeight;
  45. IAVIFile *m_pAVIFile;
  46. WAVEFORMATEX m_wFormat;
  47. int m_nFrameRate;
  48. int m_nFrameScale;
  49. IAVIStream *m_pAudioStream;
  50. IAVIStream *m_pVideoStream;
  51. IAVIStream *m_pCompressedStream;
  52. int m_nFrame;
  53. int m_nSample;
  54. HDC m_memdc;
  55. HBITMAP m_DIBSection;
  56. BITMAPINFO m_bi;
  57. BITMAPINFOHEADER *m_bih;
  58. };
  59. //-----------------------------------------------------------------------------
  60. // Constructor
  61. //-----------------------------------------------------------------------------
  62. CAviFile::CAviFile()
  63. {
  64. Reset();
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Reset the avi file
  68. //-----------------------------------------------------------------------------
  69. void CAviFile::Reset()
  70. {
  71. Q_memset( &m_wFormat, 0, sizeof( m_wFormat ) );
  72. Q_memset( &m_bi, 0, sizeof( m_bi ) );
  73. m_bValid = false;
  74. m_nWidth = 0;
  75. m_nHeight = 0;
  76. m_pAVIFile = NULL;
  77. m_nFrameRate = 0;
  78. m_nFrameScale = 1;
  79. m_pAudioStream = NULL;
  80. m_pVideoStream = NULL;
  81. m_pCompressedStream = NULL;
  82. m_nFrame = 0;
  83. m_nSample = 0;
  84. m_memdc = ( HDC )0;
  85. m_DIBSection = ( HBITMAP )0;
  86. m_bih = &m_bi.bmiHeader;
  87. m_bih->biSize = sizeof( *m_bih );
  88. //m_bih->biWidth = xxx
  89. //m_bih->biHeight = xxx
  90. m_bih->biPlanes = 1;
  91. m_bih->biBitCount = 24;
  92. m_bih->biCompression = BI_RGB;
  93. //m_bih->biSizeImage = ( ( m_bih->biWidth * m_bih->biBitCount/8 + 3 )& 0xFFFFFFFC ) * m_bih->biHeight;
  94. m_bih->biXPelsPerMeter = 10000;
  95. m_bih->biYPelsPerMeter = 10000;
  96. m_bih->biClrUsed = 0;
  97. m_bih->biClrImportant = 0;
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Start recording an AVI
  101. //-----------------------------------------------------------------------------
  102. void CAviFile::Init( const AVIParams_t& params, void *hWnd )
  103. {
  104. Reset();
  105. char avifilename[ 512 ];
  106. char fullavifilename[ 512 ];
  107. Q_snprintf( avifilename, sizeof( avifilename ), "%s", params.m_pFileName );
  108. Q_SetExtension( avifilename, ".avi", sizeof( avifilename ) );
  109. g_pFullFileSystem->RelativePathToFullPath( avifilename, params.m_pPathID, fullavifilename, sizeof( fullavifilename ) );
  110. if ( g_pFullFileSystem->FileExists( fullavifilename, params.m_pPathID ) )
  111. {
  112. g_pFullFileSystem->RemoveFile( fullavifilename, params.m_pPathID );
  113. }
  114. HRESULT hr = AVIFileOpen( &m_pAVIFile, fullavifilename, OF_WRITE | OF_CREATE, NULL );
  115. if ( hr != AVIERR_OK )
  116. return;
  117. m_wFormat.cbSize = sizeof( m_wFormat );
  118. m_wFormat.wFormatTag = WAVE_FORMAT_PCM;
  119. m_wFormat.nChannels = params.m_nNumChannels;
  120. m_wFormat.nSamplesPerSec = params.m_nSampleRate;
  121. m_wFormat.nBlockAlign = params.m_nNumChannels * ( params.m_nSampleBits == 8 ? 1 : 2 );
  122. m_wFormat.nAvgBytesPerSec = m_wFormat.nBlockAlign * params.m_nSampleRate;
  123. m_wFormat.wBitsPerSample = params.m_nSampleBits;
  124. m_nFrameRate = params.m_nFrameRate;
  125. m_nFrameScale = params.m_nFrameScale;
  126. m_bValid = true;
  127. m_nHeight = params.m_nHeight;
  128. m_nWidth = params.m_nWidth;
  129. CreateVideoStreams( params, hWnd );
  130. CreateAudioStream();
  131. }
  132. void CAviFile::Shutdown()
  133. {
  134. if ( m_pAudioStream )
  135. {
  136. AVIStreamRelease( m_pAudioStream );
  137. m_pAudioStream = NULL;
  138. }
  139. if ( m_pVideoStream )
  140. {
  141. AVIStreamRelease( m_pVideoStream );
  142. m_pVideoStream = NULL;
  143. }
  144. if ( m_pCompressedStream )
  145. {
  146. AVIStreamRelease( m_pCompressedStream );
  147. m_pCompressedStream = NULL;
  148. }
  149. if ( m_pAVIFile )
  150. {
  151. AVIFileRelease( m_pAVIFile );
  152. m_pAVIFile = NULL;
  153. }
  154. if ( m_DIBSection != 0 )
  155. {
  156. DeleteObject( m_DIBSection );
  157. }
  158. if ( m_memdc != 0 )
  159. {
  160. // Release the compatible DC
  161. DeleteDC( m_memdc );
  162. }
  163. Reset();
  164. }
  165. static unsigned int FormatAviMessage( HRESULT code, char *buf, unsigned int len)
  166. {
  167. const char *msg="unknown avi result code";
  168. switch (code)
  169. {
  170. case S_OK: msg="Success"; break;
  171. case AVIERR_BADFORMAT: msg="AVIERR_BADFORMAT: corrupt file or unrecognized format"; break;
  172. case AVIERR_MEMORY: msg="AVIERR_MEMORY: insufficient memory"; break;
  173. case AVIERR_FILEREAD: msg="AVIERR_FILEREAD: disk error while reading file"; break;
  174. case AVIERR_FILEOPEN: msg="AVIERR_FILEOPEN: disk error while opening file"; break;
  175. case REGDB_E_CLASSNOTREG: msg="REGDB_E_CLASSNOTREG: file type not recognised"; break;
  176. case AVIERR_READONLY: msg="AVIERR_READONLY: file is read-only"; break;
  177. case AVIERR_NOCOMPRESSOR: msg="AVIERR_NOCOMPRESSOR: a suitable compressor could not be found"; break;
  178. case AVIERR_UNSUPPORTED: msg="AVIERR_UNSUPPORTED: compression is not supported for this type of data"; break;
  179. case AVIERR_INTERNAL: msg="AVIERR_INTERNAL: internal error"; break;
  180. case AVIERR_BADFLAGS: msg="AVIERR_BADFLAGS"; break;
  181. case AVIERR_BADPARAM: msg="AVIERR_BADPARAM"; break;
  182. case AVIERR_BADSIZE: msg="AVIERR_BADSIZE"; break;
  183. case AVIERR_BADHANDLE: msg="AVIERR_BADHANDLE"; break;
  184. case AVIERR_FILEWRITE: msg="AVIERR_FILEWRITE: disk error while writing file"; break;
  185. case AVIERR_COMPRESSOR: msg="AVIERR_COMPRESSOR"; break;
  186. case AVIERR_NODATA: msg="AVIERR_READONLY"; break;
  187. case AVIERR_BUFFERTOOSMALL: msg="AVIERR_BUFFERTOOSMALL"; break;
  188. case AVIERR_CANTCOMPRESS: msg="AVIERR_CANTCOMPRESS"; break;
  189. case AVIERR_USERABORT: msg="AVIERR_USERABORT"; break;
  190. case AVIERR_ERROR: msg="AVIERR_ERROR"; break;
  191. }
  192. unsigned int mlen = (unsigned int)Q_strlen( msg );
  193. if ( buf==0 || len==0 )
  194. return mlen;
  195. unsigned int n=mlen;
  196. if (n+1>len)
  197. {
  198. n=len-1;
  199. }
  200. strncpy(buf,msg,n);
  201. buf[n]=0;
  202. return mlen;
  203. }
  204. static void ReportError( HRESULT hr )
  205. {
  206. char buf[ 512 ];
  207. FormatAviMessage( hr, buf, sizeof( buf ) );
  208. Warning( "%s\n", buf );
  209. }
  210. void CAviFile::CreateVideoStreams( const AVIParams_t& params, void *hWnd )
  211. {
  212. AVISTREAMINFO streaminfo;
  213. Q_memset( &streaminfo, 0, sizeof( streaminfo ) ) ;
  214. streaminfo.fccType = streamtypeVIDEO;
  215. streaminfo.fccHandler = 0;
  216. streaminfo.dwScale = params.m_nFrameScale;
  217. streaminfo.dwRate = params.m_nFrameRate;
  218. streaminfo.dwSuggestedBufferSize = params.m_nWidth * params.m_nHeight * 3;
  219. SetRect( &streaminfo.rcFrame, 0, 0, params.m_nWidth, params.m_nHeight );
  220. HRESULT hr = AVIFileCreateStream( m_pAVIFile, &m_pVideoStream, &streaminfo );
  221. if ( hr != AVIERR_OK )
  222. {
  223. m_bValid = false;
  224. ReportError( hr );
  225. return;
  226. }
  227. AVICOMPRESSOPTIONS compression;
  228. Q_memset( &compression, 0, sizeof( compression ) );
  229. AVICOMPRESSOPTIONS *aopts[1];
  230. aopts[ 0 ] = &compression;
  231. // Choose DIVX compressor for now
  232. Warning( "FIXME: DIVX only for now\n" );
  233. if ( params.m_bGetCodecFromUser )
  234. {
  235. // FIXME: This won't work so well in full screen!!!
  236. if ( !AVISaveOptions( (HWND)hWnd, 0, 1, &m_pVideoStream, aopts ) )
  237. {
  238. m_bValid = false;
  239. return;
  240. }
  241. // Cache for next time
  242. g_dwLastValidCodec = compression.fccHandler;
  243. }
  244. else
  245. {
  246. compression.fccHandler = g_dwLastValidCodec ? g_dwLastValidCodec : mmioFOURCC( 'd', 'i', 'b', ' ' );
  247. }
  248. hr = AVIMakeCompressedStream( &m_pCompressedStream, m_pVideoStream, &compression, NULL );
  249. if ( hr != AVIERR_OK )
  250. {
  251. m_bValid = false;
  252. ReportError( hr );
  253. return;
  254. }
  255. // Create a compatible DC
  256. HDC hdcscreen = GetDC( GetDesktopWindow() );
  257. m_memdc = CreateCompatibleDC(hdcscreen);
  258. ReleaseDC( GetDesktopWindow(), hdcscreen );
  259. // Set up a DIBSection for the screen
  260. m_bih->biWidth = params.m_nWidth;
  261. m_bih->biHeight = params.m_nHeight;
  262. m_bih->biSizeImage = ( ( m_bih->biWidth * m_bih->biBitCount / 8 + 3 )& 0xFFFFFFFC ) * m_bih->biHeight;
  263. // Create the DIBSection
  264. void *bits;
  265. m_DIBSection = CreateDIBSection
  266. (
  267. m_memdc,
  268. ( BITMAPINFO *)m_bih,
  269. DIB_RGB_COLORS,
  270. &bits,
  271. NULL,
  272. NULL
  273. );
  274. // Get at the DIBSection object
  275. DIBSECTION dibs;
  276. GetObject( m_DIBSection, sizeof( dibs ), &dibs );
  277. // Set the stream format
  278. hr = AVIStreamSetFormat(
  279. m_pCompressedStream,
  280. 0,
  281. &dibs.dsBmih,
  282. dibs.dsBmih.biSize + dibs.dsBmih.biClrUsed *sizeof( RGBQUAD )
  283. );
  284. if ( hr != AVIERR_OK )
  285. {
  286. m_bValid = false;
  287. ReportError( hr );
  288. return;
  289. }
  290. }
  291. void CAviFile::CreateAudioStream()
  292. {
  293. AVISTREAMINFO audiostream;
  294. Q_memset( &audiostream, 0, sizeof( audiostream ) );
  295. audiostream.fccType = streamtypeAUDIO;
  296. audiostream.dwScale = m_wFormat.nBlockAlign;
  297. audiostream.dwRate = m_wFormat.nSamplesPerSec * m_wFormat.nBlockAlign;
  298. audiostream.dwSampleSize = m_wFormat.nBlockAlign;
  299. audiostream.dwQuality = (DWORD)-1;
  300. HRESULT hr = AVIFileCreateStream( m_pAVIFile, &m_pAudioStream, &audiostream );
  301. if ( hr != AVIERR_OK )
  302. {
  303. m_bValid = false;
  304. ReportError( hr );
  305. return;
  306. }
  307. hr = AVIStreamSetFormat( m_pAudioStream, 0, &m_wFormat, sizeof( m_wFormat ) );
  308. if ( hr != AVIERR_OK )
  309. {
  310. m_bValid = false;
  311. ReportError( hr );
  312. return;
  313. }
  314. }
  315. void CAviFile::AppendMovieSound( short *buf, size_t bufsize )
  316. {
  317. if ( !m_bValid )
  318. return;
  319. unsigned long numsamps = bufsize / sizeof( short ); // numbytes*8 / au->wfx.wBitsPerSample;
  320. //
  321. // now we can write the data
  322. HRESULT hr = AVIStreamWrite
  323. (
  324. m_pAudioStream,
  325. m_nSample,
  326. numsamps,
  327. buf,
  328. bufsize,
  329. 0,
  330. NULL,
  331. NULL
  332. );
  333. if ( hr != AVIERR_OK )
  334. {
  335. m_bValid = false;
  336. ReportError( hr );
  337. return;
  338. }
  339. m_nSample += numsamps;
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Adds a frame of the movie to the AVI
  343. //-----------------------------------------------------------------------------
  344. void CAviFile::AppendMovieFrame( const BGR888_t *pRGBData )
  345. {
  346. if ( !m_bValid )
  347. return;
  348. DIBSECTION dibs;
  349. HGDIOBJ hOldObject = SelectObject( m_memdc, m_DIBSection );
  350. // Update the DIBSection bits
  351. // FIXME: Have to invert this vertically since passing in negative
  352. // biHeights in the m_bih field doesn't make the system know it's a top-down AVI
  353. int scanlines = 0;
  354. for ( int i = 0; i < m_nHeight; ++i )
  355. {
  356. scanlines += SetDIBits( m_memdc, m_DIBSection, m_nHeight - i - 1, 1, pRGBData,
  357. ( CONST BITMAPINFO * )m_bih, DIB_RGB_COLORS );
  358. pRGBData += m_nWidth;
  359. }
  360. int objectSize = GetObject( m_DIBSection, sizeof( dibs ), &dibs );
  361. if ( scanlines != m_nHeight || objectSize != sizeof( DIBSECTION ))
  362. {
  363. SelectObject( m_memdc, hOldObject );
  364. m_bValid = false;
  365. return;
  366. }
  367. // Now we can add the frame
  368. HRESULT hr = AVIStreamWrite(
  369. m_pCompressedStream,
  370. m_nFrame,
  371. 1,
  372. dibs.dsBm.bmBits,
  373. dibs.dsBmih.biSizeImage,
  374. AVIIF_KEYFRAME,
  375. NULL,
  376. NULL );
  377. SelectObject( m_memdc, hOldObject );
  378. if ( hr != AVIERR_OK )
  379. {
  380. m_bValid = false;
  381. ReportError( hr );
  382. return;
  383. }
  384. ++m_nFrame;
  385. }
  386. //-----------------------------------------------------------------------------
  387. //
  388. // Class used to associated AVI files with IMaterials
  389. //
  390. //-----------------------------------------------------------------------------
  391. class CAVIMaterial : public ITextureRegenerator
  392. {
  393. public:
  394. CAVIMaterial();
  395. // Initializes, shuts down the material
  396. bool Init( const char *pMaterialName, const char *pFileName, const char *pPathID );
  397. void Shutdown();
  398. // Inherited from ITextureRegenerator
  399. virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect );
  400. virtual void Release();
  401. // Returns the material
  402. IMaterial *GetMaterial();
  403. // Returns the texcoord range
  404. void GetTexCoordRange( float *pMaxU, float *pMaxV );
  405. // Returns the frame size of the AVI (stored in a subrect of the material itself)
  406. void GetFrameSize( int *pWidth, int *pHeight );
  407. // Sets the current time
  408. void SetTime( float flTime );
  409. // Returns the frame rate/count of the AVI
  410. int GetFrameRate( );
  411. int GetFrameCount( );
  412. // Sets the frame for an AVI material (use instead of SetTime)
  413. void SetFrame( float flFrame );
  414. private:
  415. // Initializes, shuts down the procedural texture
  416. void CreateProceduralTexture( const char *pTextureName );
  417. void DestroyProceduralTexture();
  418. // Initializes, shuts down the procedural material
  419. void CreateProceduralMaterial( const char *pMaterialName );
  420. void DestroyProceduralMaterial();
  421. // Initializes, shuts down the video stream
  422. void CreateVideoStream( );
  423. void DestroyVideoStream( );
  424. CMaterialReference m_Material;
  425. CTextureReference m_Texture;
  426. IAVIFile *m_pAVIFile;
  427. IAVIStream *m_pAVIStream;
  428. IGetFrame *m_pGetFrame;
  429. int m_nAVIWidth;
  430. int m_nAVIHeight;
  431. int m_nFrameRate;
  432. int m_nFrameCount;
  433. int m_nCurrentSample;
  434. HDC m_memdc;
  435. HBITMAP m_DIBSection;
  436. BITMAPINFO m_bi;
  437. BITMAPINFOHEADER *m_bih;
  438. };
  439. //-----------------------------------------------------------------------------
  440. // Constructor
  441. //-----------------------------------------------------------------------------
  442. CAVIMaterial::CAVIMaterial()
  443. {
  444. Q_memset( &m_bi, 0, sizeof( m_bi ) );
  445. m_memdc = ( HDC )0;
  446. m_DIBSection = ( HBITMAP )0;
  447. m_pAVIStream = NULL;
  448. m_pAVIFile = NULL;
  449. m_pGetFrame = NULL;
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Initializes the material
  453. //-----------------------------------------------------------------------------
  454. bool CAVIMaterial::Init( const char *pMaterialName, const char *pFileName, const char *pPathID )
  455. {
  456. // Determine the full path name of the AVI
  457. char pAVIFileName[ 512 ];
  458. char pFullAVIFileName[ 512 ];
  459. Q_snprintf( pAVIFileName, sizeof( pAVIFileName ), "%s", pFileName );
  460. Q_DefaultExtension( pAVIFileName, ".avi", sizeof( pAVIFileName ) );
  461. g_pFullFileSystem->RelativePathToFullPath( pAVIFileName, pPathID, pFullAVIFileName, sizeof( pFullAVIFileName ) );
  462. HRESULT hr = AVIFileOpen( &m_pAVIFile, pFullAVIFileName, OF_READ, NULL );
  463. if ( hr != AVIERR_OK )
  464. {
  465. Warning( "AVI '%s' not found\n", pFullAVIFileName );
  466. m_nAVIWidth = 64;
  467. m_nAVIHeight = 64;
  468. m_nFrameRate = 1;
  469. m_nFrameCount = 1;
  470. m_Material.Init( "debug/debugempty", TEXTURE_GROUP_OTHER );
  471. return false;
  472. }
  473. // Get AVI size
  474. AVIFILEINFO info;
  475. AVIFileInfo( m_pAVIFile, &info, sizeof(info) );
  476. m_nAVIWidth = info.dwWidth;
  477. m_nAVIHeight = info.dwHeight;
  478. m_nFrameRate = (int)( (float)info.dwRate / (float)info.dwScale + 0.5f );
  479. CreateProceduralTexture( pMaterialName );
  480. CreateProceduralMaterial( pMaterialName );
  481. CreateVideoStream();
  482. // Get frame count
  483. m_nFrameCount = MAX( 0, AVIStreamLength( m_pAVIStream ) );
  484. m_Texture->Download();
  485. return true;
  486. }
  487. void CAVIMaterial::Shutdown()
  488. {
  489. DestroyVideoStream();
  490. DestroyProceduralMaterial( );
  491. DestroyProceduralTexture( );
  492. if ( m_pAVIFile )
  493. {
  494. AVIFileRelease( m_pAVIFile );
  495. m_pAVIFile = NULL;
  496. }
  497. }
  498. //-----------------------------------------------------------------------------
  499. // Returns the material
  500. //-----------------------------------------------------------------------------
  501. IMaterial *CAVIMaterial::GetMaterial()
  502. {
  503. return m_Material;
  504. }
  505. //-----------------------------------------------------------------------------
  506. // Returns the texcoord range
  507. //-----------------------------------------------------------------------------
  508. void CAVIMaterial::GetTexCoordRange( float *pMaxU, float *pMaxV )
  509. {
  510. if ( !m_Texture )
  511. {
  512. *pMaxU = *pMaxV = 1.0f;
  513. return;
  514. }
  515. int nTextureWidth = m_Texture->GetActualWidth();
  516. int nTextureHeight = m_Texture->GetActualHeight();
  517. if ( nTextureWidth )
  518. *pMaxU = (float)m_nAVIWidth / (float)nTextureWidth;
  519. else
  520. *pMaxU = 0.0f;
  521. if ( nTextureHeight )
  522. *pMaxV = (float)m_nAVIHeight / (float)nTextureHeight;
  523. else
  524. *pMaxV = 0.0f;
  525. }
  526. //-----------------------------------------------------------------------------
  527. // Returns the frame size of the AVI (stored in a subrect of the material itself)
  528. //-----------------------------------------------------------------------------
  529. void CAVIMaterial::GetFrameSize( int *pWidth, int *pHeight )
  530. {
  531. *pWidth = m_nAVIWidth;
  532. *pHeight = m_nAVIHeight;
  533. }
  534. //-----------------------------------------------------------------------------
  535. // Computes a power of two at least as big as the passed-in number
  536. //-----------------------------------------------------------------------------
  537. static inline int ComputeGreaterPowerOfTwo( int n )
  538. {
  539. int i = 1;
  540. while ( i < n )
  541. {
  542. i <<= 1;
  543. }
  544. return i;
  545. }
  546. //-----------------------------------------------------------------------------
  547. // Initializes, shuts down the procedural texture
  548. //-----------------------------------------------------------------------------
  549. void CAVIMaterial::CreateProceduralTexture( const char *pTextureName )
  550. {
  551. // Choose power-of-two textures which are at least as big as the AVI
  552. int nWidth = ComputeGreaterPowerOfTwo( m_nAVIWidth );
  553. int nHeight = ComputeGreaterPowerOfTwo( m_nAVIHeight );
  554. m_Texture.InitProceduralTexture( pTextureName, "avi", nWidth, nHeight,
  555. IMAGE_FORMAT_RGBA8888, TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT | TEXTUREFLAGS_NOMIP |
  556. TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY );
  557. m_Texture->SetTextureRegenerator( this );
  558. }
  559. void CAVIMaterial::DestroyProceduralTexture()
  560. {
  561. if (m_Texture)
  562. {
  563. m_Texture->SetTextureRegenerator( NULL );
  564. m_Texture.Shutdown();
  565. }
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Initializes, shuts down the procedural material
  569. //-----------------------------------------------------------------------------
  570. void CAVIMaterial::CreateProceduralMaterial( const char *pMaterialName )
  571. {
  572. // FIXME: gak, this is backwards. Why doesn't the material just see that it has a funky basetexture?
  573. char vmtfilename[ 512 ];
  574. Q_strcpy( vmtfilename, pMaterialName );
  575. Q_SetExtension( vmtfilename, ".vmt", sizeof( vmtfilename ) );
  576. KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
  577. if (!pVMTKeyValues->LoadFromFile( g_pFullFileSystem , vmtfilename, "GAME" ))
  578. {
  579. pVMTKeyValues->SetString( "$basetexture", m_Texture->GetName() );
  580. pVMTKeyValues->SetInt( "$nofog", 1 );
  581. pVMTKeyValues->SetInt( "$spriteorientation", 3 );
  582. pVMTKeyValues->SetInt( "$translucent", 1 );
  583. }
  584. m_Material.Init( pMaterialName, pVMTKeyValues );
  585. m_Material->Refresh();
  586. }
  587. void CAVIMaterial::DestroyProceduralMaterial()
  588. {
  589. m_Material.Shutdown();
  590. }
  591. //-----------------------------------------------------------------------------
  592. // Sets the current time
  593. //-----------------------------------------------------------------------------
  594. void CAVIMaterial::SetTime( float flTime )
  595. {
  596. if ( m_pAVIStream )
  597. {
  598. // Round to the nearest frame
  599. // FIXME: Strangely, AVIStreamTimeToSample gets off by several frames if you're a ways down the stream
  600. // int nCurrentSample = AVIStreamTimeToSample( m_pAVIStream, ( flTime + 0.5f / m_nFrameRate )* 1000.0f );
  601. int nCurrentSample = (int)( flTime * m_nFrameRate + 0.5f );
  602. if ( m_nCurrentSample != nCurrentSample )
  603. {
  604. m_nCurrentSample = nCurrentSample;
  605. m_Texture->Download();
  606. }
  607. }
  608. }
  609. //-----------------------------------------------------------------------------
  610. // Returns the frame rate of the AVI
  611. //-----------------------------------------------------------------------------
  612. int CAVIMaterial::GetFrameRate( )
  613. {
  614. return m_nFrameRate;
  615. }
  616. int CAVIMaterial::GetFrameCount( )
  617. {
  618. return m_nFrameCount;
  619. }
  620. //-----------------------------------------------------------------------------
  621. // Sets the frame for an AVI material (use instead of SetTime)
  622. //-----------------------------------------------------------------------------
  623. void CAVIMaterial::SetFrame( float flFrame )
  624. {
  625. if ( m_pAVIStream )
  626. {
  627. int nCurrentSample = (int)( flFrame + 0.5f );
  628. if ( m_nCurrentSample != nCurrentSample )
  629. {
  630. m_nCurrentSample = nCurrentSample;
  631. m_Texture->Download();
  632. }
  633. }
  634. }
  635. //-----------------------------------------------------------------------------
  636. // Initializes, shuts down the video stream
  637. //-----------------------------------------------------------------------------
  638. void CAVIMaterial::CreateVideoStream( )
  639. {
  640. HRESULT hr = AVIFileGetStream( m_pAVIFile, &m_pAVIStream, streamtypeVIDEO, 0 );
  641. if ( hr != AVIERR_OK )
  642. {
  643. ReportError( hr );
  644. return;
  645. }
  646. m_nCurrentSample = AVIStreamStart( m_pAVIStream );
  647. // Create a compatible DC
  648. HDC hdcscreen = GetDC( GetDesktopWindow() );
  649. m_memdc = CreateCompatibleDC( hdcscreen );
  650. ReleaseDC( GetDesktopWindow(), hdcscreen );
  651. // Set up a DIBSection for the screen
  652. m_bih = &m_bi.bmiHeader;
  653. m_bih->biSize = sizeof( *m_bih );
  654. m_bih->biWidth = m_nAVIWidth;
  655. m_bih->biHeight = m_nAVIHeight;
  656. m_bih->biPlanes = 1;
  657. m_bih->biBitCount = 32;
  658. m_bih->biCompression = BI_RGB;
  659. m_bih->biSizeImage = ( ( m_bih->biWidth * m_bih->biBitCount / 8 + 3 )& 0xFFFFFFFC ) * m_bih->biHeight;
  660. m_bih->biXPelsPerMeter = 10000;
  661. m_bih->biYPelsPerMeter = 10000;
  662. m_bih->biClrUsed = 0;
  663. m_bih->biClrImportant = 0;
  664. // Create the DIBSection
  665. void *bits;
  666. m_DIBSection = CreateDIBSection( m_memdc, ( BITMAPINFO *)m_bih, DIB_RGB_COLORS, &bits, NULL, NULL );
  667. // Get at the DIBSection object
  668. DIBSECTION dibs;
  669. GetObject( m_DIBSection, sizeof( dibs ), &dibs );
  670. m_pGetFrame = AVIStreamGetFrameOpen( m_pAVIStream, &dibs.dsBmih );
  671. }
  672. void CAVIMaterial::DestroyVideoStream( )
  673. {
  674. if ( m_pGetFrame )
  675. {
  676. AVIStreamGetFrameClose( m_pGetFrame );
  677. m_pGetFrame = NULL;
  678. }
  679. if ( m_DIBSection != 0 )
  680. {
  681. DeleteObject( m_DIBSection );
  682. m_DIBSection = (HBITMAP)0;
  683. }
  684. if ( m_memdc != 0 )
  685. {
  686. // Release the compatible DC
  687. DeleteDC( m_memdc );
  688. m_memdc = (HDC)0;
  689. }
  690. if ( m_pAVIStream )
  691. {
  692. AVIStreamRelease( m_pAVIStream );
  693. m_pAVIStream = NULL;
  694. }
  695. }
  696. //-----------------------------------------------------------------------------
  697. // Inherited from ITextureRegenerator
  698. //-----------------------------------------------------------------------------
  699. void CAVIMaterial::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
  700. {
  701. CPixelWriter pixelWriter;
  702. LPBITMAPINFOHEADER lpbih;
  703. unsigned char *pData;
  704. int i, y, nIncY;
  705. // Error condition
  706. if ( !m_pAVIStream || !m_pGetFrame || (pVTFTexture->FrameCount() > 1) ||
  707. (pVTFTexture->FaceCount() > 1) || (pVTFTexture->MipCount() > 1) || (pVTFTexture->Depth() > 1) )
  708. {
  709. goto AVIMaterialError;
  710. }
  711. lpbih = (LPBITMAPINFOHEADER)AVIStreamGetFrame( m_pGetFrame, m_nCurrentSample );
  712. if ( !lpbih )
  713. goto AVIMaterialError;
  714. // Set up the pixel writer to write into the VTF texture
  715. pixelWriter.SetPixelMemory( pVTFTexture->Format(),
  716. pVTFTexture->ImageData( ), pVTFTexture->RowSizeInBytes( 0 ) );
  717. int nWidth = pVTFTexture->Width();
  718. int nHeight = pVTFTexture->Height();
  719. int nBihHeight = abs( lpbih->biHeight );
  720. if ( lpbih->biWidth > nWidth || nBihHeight > nHeight )
  721. goto AVIMaterialError;
  722. pData = (unsigned char *)lpbih + lpbih->biSize;
  723. if ( lpbih->biBitCount == 8 )
  724. {
  725. // This is the palette
  726. pData += 256 * sizeof(RGBQUAD);
  727. }
  728. if ( (( lpbih->biBitCount == 16 ) || ( lpbih->biBitCount == 32 )) && ( lpbih->biCompression == BI_BITFIELDS ) )
  729. {
  730. pData += 3 * sizeof(DWORD);
  731. // MASKS NOT IMPLEMENTED YET
  732. Assert( 0 );
  733. }
  734. int nStride = ( lpbih->biWidth * lpbih->biBitCount / 8 + 3 ) & 0xFFFFFFFC;
  735. if ( lpbih->biHeight > 0 )
  736. {
  737. y = nBihHeight - 1;
  738. nIncY = -1;
  739. }
  740. else
  741. {
  742. y = 0;
  743. nIncY = 1;
  744. }
  745. if ( lpbih->biBitCount == 24)
  746. {
  747. for ( i = 0; i < nBihHeight; ++i, pData += nStride, y += nIncY )
  748. {
  749. pixelWriter.Seek( 0, y );
  750. BGR888_t *pAVIPixel = (BGR888_t*)pData;
  751. for (int x = 0; x < lpbih->biWidth; ++x, ++pAVIPixel)
  752. {
  753. pixelWriter.WritePixel( pAVIPixel->r, pAVIPixel->g, pAVIPixel->b, 255 );
  754. }
  755. }
  756. }
  757. else if (lpbih->biBitCount == 32)
  758. {
  759. for ( i = 0; i < nBihHeight; ++i, pData += nStride, y += nIncY )
  760. {
  761. pixelWriter.Seek( 0, y );
  762. BGRA8888_t *pAVIPixel = (BGRA8888_t*)pData;
  763. for (int x = 0; x < lpbih->biWidth; ++x, ++pAVIPixel)
  764. {
  765. pixelWriter.WritePixel( pAVIPixel->r, pAVIPixel->g, pAVIPixel->b, pAVIPixel->a );
  766. }
  767. }
  768. }
  769. return;
  770. AVIMaterialError:
  771. int nBytes = pVTFTexture->ComputeTotalSize();
  772. memset( pVTFTexture->ImageData(), 0xFF, nBytes );
  773. return;
  774. }
  775. void CAVIMaterial::Release()
  776. {
  777. }
  778. //-----------------------------------------------------------------------------
  779. //
  780. // Implementation of IAvi
  781. //
  782. //-----------------------------------------------------------------------------
  783. class CAvi : public CTier3AppSystem< IAvi >
  784. {
  785. typedef CTier3AppSystem< IAvi > BaseClass;
  786. public:
  787. CAvi();
  788. // Inherited from IAppSystem
  789. virtual bool Connect( CreateInterfaceFn factory );
  790. virtual void *QueryInterface( const char *pInterfaceName );
  791. virtual InitReturnVal_t Init();
  792. virtual void Shutdown();
  793. // Inherited from IAvi
  794. virtual void SetMainWindow( void* hWnd );
  795. virtual AVIHandle_t StartAVI( const AVIParams_t& params );
  796. virtual void FinishAVI( AVIHandle_t h );
  797. virtual void AppendMovieSound( AVIHandle_t h, short *buf, size_t bufsize );
  798. virtual void AppendMovieFrame( AVIHandle_t h, const BGR888_t *pRGBData );
  799. virtual AVIMaterial_t CreateAVIMaterial( const char *pMaterialName, const char *pFileName, const char *pPathID );
  800. virtual void DestroyAVIMaterial( AVIMaterial_t hMaterial );
  801. virtual void SetTime( AVIMaterial_t hMaterial, float flTime );
  802. virtual IMaterial* GetMaterial( AVIMaterial_t hMaterial );
  803. virtual void GetTexCoordRange( AVIMaterial_t hMaterial, float *pMaxU, float *pMaxV );
  804. virtual void GetFrameSize( AVIMaterial_t hMaterial, int *pWidth, int *pHeight );
  805. virtual int GetFrameRate( AVIMaterial_t hMaterial );
  806. virtual void SetFrame( AVIMaterial_t hMaterial, float flFrame );
  807. virtual int GetFrameCount( AVIMaterial_t hMaterial );
  808. private:
  809. HWND m_hWnd;
  810. CUtlLinkedList< CAviFile, AVIHandle_t > m_AVIFiles;
  811. // NOTE: Have to use pointers here since AVIMaterials inherit from ITextureRegenerator
  812. // The realloc screws up the pointers held to ITextureRegenerators in the material system.
  813. CUtlLinkedList< CAVIMaterial*, AVIMaterial_t > m_AVIMaterials;
  814. };
  815. //-----------------------------------------------------------------------------
  816. // Singleton
  817. //-----------------------------------------------------------------------------
  818. static CAvi g_AVI;
  819. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CAvi, IAvi, AVI_INTERFACE_VERSION, g_AVI );
  820. //-----------------------------------------------------------------------------
  821. // Constructor/destructor
  822. //-----------------------------------------------------------------------------
  823. CAvi::CAvi()
  824. {
  825. m_hWnd = NULL;
  826. }
  827. //-----------------------------------------------------------------------------
  828. // Connect/disconnect
  829. //-----------------------------------------------------------------------------
  830. bool CAvi::Connect( CreateInterfaceFn factory )
  831. {
  832. if ( !BaseClass::Connect( factory ) )
  833. return false;
  834. if ( !( g_pFullFileSystem && materials ) )
  835. {
  836. Msg( "Avi failed to connect to a required system\n" );
  837. }
  838. return ( g_pFullFileSystem && materials );
  839. }
  840. //-----------------------------------------------------------------------------
  841. // Query Interface
  842. //-----------------------------------------------------------------------------
  843. void *CAvi::QueryInterface( const char *pInterfaceName )
  844. {
  845. if (!Q_strncmp( pInterfaceName, AVI_INTERFACE_VERSION, Q_strlen(AVI_INTERFACE_VERSION) + 1))
  846. return (IAvi*)this;
  847. return NULL;
  848. }
  849. //-----------------------------------------------------------------------------
  850. // Init/shutdown
  851. //-----------------------------------------------------------------------------
  852. InitReturnVal_t CAvi::Init()
  853. {
  854. InitReturnVal_t nRetVal = BaseClass::Init();
  855. if ( nRetVal != INIT_OK )
  856. return nRetVal;
  857. AVIFileInit();
  858. return INIT_OK;
  859. }
  860. void CAvi::Shutdown()
  861. {
  862. AVIFileExit();
  863. BaseClass::Shutdown();
  864. }
  865. //-----------------------------------------------------------------------------
  866. // Sets the main window
  867. //-----------------------------------------------------------------------------
  868. void CAvi::SetMainWindow( void* hWnd )
  869. {
  870. m_hWnd = (HWND)hWnd;
  871. }
  872. //-----------------------------------------------------------------------------
  873. // Start, finish recording an AVI
  874. //-----------------------------------------------------------------------------
  875. AVIHandle_t CAvi::StartAVI( const AVIParams_t& params )
  876. {
  877. AVIHandle_t h = m_AVIFiles.AddToTail();
  878. m_AVIFiles[h].Init( params, m_hWnd );
  879. return h;
  880. }
  881. void CAvi::FinishAVI( AVIHandle_t h )
  882. {
  883. if ( h != AVIHANDLE_INVALID )
  884. {
  885. m_AVIFiles[h].Shutdown();
  886. m_AVIFiles.Remove( h );
  887. }
  888. }
  889. //-----------------------------------------------------------------------------
  890. // Add sound buffer
  891. //-----------------------------------------------------------------------------
  892. void CAvi::AppendMovieSound( AVIHandle_t h, short *buf, size_t bufsize )
  893. {
  894. if ( h != AVIHANDLE_INVALID )
  895. {
  896. m_AVIFiles[h].AppendMovieSound( buf, bufsize );
  897. }
  898. }
  899. //-----------------------------------------------------------------------------
  900. // Add movie frame
  901. //-----------------------------------------------------------------------------
  902. void CAvi::AppendMovieFrame( AVIHandle_t h, const BGR888_t *pRGBData )
  903. {
  904. if ( h != AVIHANDLE_INVALID )
  905. {
  906. m_AVIFiles[h].AppendMovieFrame( pRGBData );
  907. }
  908. }
  909. //-----------------------------------------------------------------------------
  910. // Create/destroy an AVI material
  911. //-----------------------------------------------------------------------------
  912. AVIMaterial_t CAvi::CreateAVIMaterial( const char *pMaterialName, const char *pFileName, const char *pPathID )
  913. {
  914. AVIMaterial_t h = m_AVIMaterials.AddToTail();
  915. m_AVIMaterials[h] = new CAVIMaterial;
  916. m_AVIMaterials[h]->Init( pMaterialName, pFileName, pPathID );
  917. return h;
  918. }
  919. void CAvi::DestroyAVIMaterial( AVIMaterial_t h )
  920. {
  921. if ( h != AVIMATERIAL_INVALID )
  922. {
  923. m_AVIMaterials[h]->Shutdown();
  924. delete m_AVIMaterials[h];
  925. m_AVIMaterials.Remove( h );
  926. }
  927. }
  928. //-----------------------------------------------------------------------------
  929. // Sets the time for an AVI material
  930. //-----------------------------------------------------------------------------
  931. void CAvi::SetTime( AVIMaterial_t h, float flTime )
  932. {
  933. if ( h != AVIMATERIAL_INVALID )
  934. {
  935. m_AVIMaterials[h]->SetTime( flTime );
  936. }
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Gets the IMaterial associated with an AVI material
  940. //-----------------------------------------------------------------------------
  941. IMaterial* CAvi::GetMaterial( AVIMaterial_t h )
  942. {
  943. if ( h != AVIMATERIAL_INVALID )
  944. return m_AVIMaterials[h]->GetMaterial();
  945. return NULL;
  946. }
  947. //-----------------------------------------------------------------------------
  948. // Returns the max texture coordinate of the AVI
  949. //-----------------------------------------------------------------------------
  950. void CAvi::GetTexCoordRange( AVIMaterial_t h, float *pMaxU, float *pMaxV )
  951. {
  952. if ( h != AVIMATERIAL_INVALID )
  953. {
  954. m_AVIMaterials[h]->GetTexCoordRange( pMaxU, pMaxV );
  955. }
  956. else
  957. {
  958. *pMaxU = *pMaxV = 1.0f;
  959. }
  960. }
  961. //-----------------------------------------------------------------------------
  962. // Returns the frame size of the AVI (is a subrect of the material itself)
  963. //-----------------------------------------------------------------------------
  964. void CAvi::GetFrameSize( AVIMaterial_t h, int *pWidth, int *pHeight )
  965. {
  966. if ( h != AVIMATERIAL_INVALID )
  967. {
  968. m_AVIMaterials[h]->GetFrameSize( pWidth, pHeight );
  969. }
  970. else
  971. {
  972. *pWidth = *pHeight = 1;
  973. }
  974. }
  975. //-----------------------------------------------------------------------------
  976. // Returns the frame rate of the AVI
  977. //-----------------------------------------------------------------------------
  978. int CAvi::GetFrameRate( AVIMaterial_t h )
  979. {
  980. if ( h != AVIMATERIAL_INVALID )
  981. return m_AVIMaterials[h]->GetFrameRate();
  982. return 1;
  983. }
  984. int CAvi::GetFrameCount( AVIMaterial_t h )
  985. {
  986. if ( h != AVIMATERIAL_INVALID )
  987. return m_AVIMaterials[h]->GetFrameCount();
  988. return 1;
  989. }
  990. //-----------------------------------------------------------------------------
  991. // Sets the frame for an AVI material (use instead of SetTime)
  992. //-----------------------------------------------------------------------------
  993. void CAvi::SetFrame( AVIMaterial_t h, float flFrame )
  994. {
  995. if ( h != AVIMATERIAL_INVALID )
  996. {
  997. m_AVIMaterials[h]->SetFrame( flFrame );
  998. }
  999. }