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.

1133 lines
32 KiB

  1. //====== Copyright 2010, Valve Corporation, All rights reserved. ==============
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "filesystem.h"
  7. #include "tier1/strtools.h"
  8. #include "tier1/utllinkedlist.h"
  9. #include "tier1/keyvalues.h"
  10. #include "materialsystem/imaterial.h"
  11. #include "materialsystem/imaterialsystem.h"
  12. #include "materialsystem/materialsystemutil.h"
  13. #include "materialsystem/itexture.h"
  14. #include "vtf/vtf.h"
  15. #include "pixelwriter.h"
  16. #include "tier3/tier3.h"
  17. #include "platform.h"
  18. #include "avi/iquicktime.h"
  19. #include "quicktime.h"
  20. #if defined ( WIN32 )
  21. #if defined ( QUICKTIME_VIDEO )
  22. #include <WinDef.h>
  23. #include <../dx9sdk/include/dsound.h>
  24. #endif
  25. #endif
  26. #include "tier0/memdbgon.h"
  27. #define ZeroVar( var ) V_memset( &var, 0, sizeof( var) )
  28. #define SAFE_DELETE( var ) if ( var != NULL ) { delete var; var = NULL; }
  29. #define SAFE_DELETE_ARRAY( var ) if ( var != NULL ) { delete[] var; var = NULL; }
  30. #ifdef DBGFLAG_ASSERT
  31. #define AssertExit( _exp ) Assert( _exp )
  32. #define AssertExitF( _exp ) Assert( _exp )
  33. #else
  34. #define AssertExit( _exp ) if ( !( _exp ) ) return;
  35. #define AssertExitF( _exp ) if ( !( _exp ) ) return false;
  36. #endif
  37. //-----------------------------------------------------------------------------
  38. // Singleton
  39. //-----------------------------------------------------------------------------
  40. static CQuickTime g_QUICKTIME;
  41. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CQuickTime, IQuickTime, QUICKTIME_INTERFACE_VERSION, g_QUICKTIME );
  42. //-----------------------------------------------------------------------------
  43. // Inherited from ITextureRegenerator
  44. //-----------------------------------------------------------------------------
  45. void CQuicktimeMaterialRGBTextureRegenerator::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
  46. {
  47. AssertExit( pVTFTexture != NULL );
  48. // Error condition
  49. if ( ( pVTFTexture->FrameCount() > 1 ) || ( pVTFTexture->FaceCount() > 1 ) || ( pVTFTexture->MipCount() > 1 ) || ( pVTFTexture->Depth() > 1 ) )
  50. {
  51. memset( pVTFTexture->ImageData(), 0xAA, pVTFTexture->ComputeTotalSize() );
  52. return;
  53. }
  54. // do we not have a video to install, or a video buffer that is too big?
  55. if ( m_pQTMaterial->m_BitMapData == NULL /* || m_VideoFrameBufferSize < pVTFTexture->ComputeMipSize( 0 ) */ )
  56. {
  57. memset( pVTFTexture->ImageData(), 0xCC, pVTFTexture->ComputeTotalSize() );
  58. return;
  59. }
  60. // Need to verify we have compatible formats
  61. Assert( pVTFTexture->Format() == IMAGE_FORMAT_BGRA8888 );
  62. Assert( pVTFTexture->RowSizeInBytes( 0 ) >= pVTFTexture->Width() * 4 );
  63. Assert( pVTFTexture->Width() >= m_nSourceWidth );
  64. Assert( pVTFTexture->Height() >= m_nSourceHeight );
  65. // simplest of image copies, one line at a time
  66. BYTE *ImageData = pVTFTexture->ImageData();
  67. BYTE *SrcData = (BYTE*) m_pQTMaterial->m_BitMapData;
  68. int dstStride = pVTFTexture->RowSizeInBytes( 0 );
  69. int srcStride = m_nSourceWidth * 4;
  70. int rowSize = m_nSourceWidth * 4;
  71. // copy the rows of data
  72. for ( int y = 0; y < m_nSourceHeight; y++ )
  73. {
  74. memcpy( ImageData, SrcData, rowSize);
  75. ImageData+= dstStride;
  76. SrcData+= srcStride;
  77. }
  78. }
  79. void CQuicktimeMaterialRGBTextureRegenerator::Release()
  80. {
  81. // we don't invoke the destructor here, we're not using the no-release extensions
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Constructor
  85. //-----------------------------------------------------------------------------
  86. CQuickTimeMaterial::CQuickTimeMaterial() :
  87. m_pFileName( NULL )
  88. #if defined ( QUICKTIME_VIDEO )
  89. ,m_MovieGWorld( NULL ),
  90. m_QTMovie( NULL ),
  91. m_AudioContext( NULL ),
  92. m_BitMapData( NULL )
  93. #endif
  94. {
  95. Reset();
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Destructor
  99. //-----------------------------------------------------------------------------
  100. CQuickTimeMaterial::~CQuickTimeMaterial()
  101. {
  102. Reset();
  103. }
  104. void CQuickTimeMaterial::SetQTFileName( const char* theQTMovieFileName )
  105. {
  106. SAFE_DELETE_ARRAY( m_pFileName );
  107. if ( theQTMovieFileName != NULL )
  108. {
  109. int sLen = V_strlen( theQTMovieFileName );
  110. AssertMsg( sLen > 0 && sLen <= cMaxQTFileNameLen, "Bad Movie FileName" );
  111. m_pFileName = new char[ sLen + 1 ];
  112. V_strcpy( m_pFileName, theQTMovieFileName );
  113. }
  114. }
  115. void CQuickTimeMaterial::Reset()
  116. {
  117. SetQTFileName( NULL );
  118. ZeroVar( m_TextureName );
  119. ZeroVar( m_MaterialName );
  120. DestroyProceduralTexture();
  121. DestroyProceduralMaterial();
  122. SAFE_DELETE_ARRAY( m_BitMapData );
  123. m_TexCordU = 0.0f;
  124. m_TexCordV = 0.0f;
  125. m_bActive = false;
  126. m_bLoopMovie = false;
  127. m_bMoviePlaying = false;
  128. m_MovieBeganPlayingTime = 0.0;
  129. m_MovieCurrentTime = 0.0;
  130. #if defined ( QUICKTIME_VIDEO )
  131. if ( m_AudioContext != NULL )
  132. {
  133. QTAudioContextRelease( m_AudioContext );
  134. m_AudioContext = NULL;
  135. }
  136. if ( m_MovieGWorld != NULL )
  137. {
  138. DisposeGWorld( m_MovieGWorld );
  139. m_MovieGWorld = NULL;
  140. }
  141. if ( m_QTMovie != NULL )
  142. {
  143. DisposeMovie( m_QTMovie );
  144. m_QTMovie = NULL;
  145. }
  146. #endif
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Initializes the material
  150. //-----------------------------------------------------------------------------
  151. bool CQuickTimeMaterial::Init( const char *pMaterialName, const char *pFileName, const char *pPathID )
  152. {
  153. // Determine the full path name of the video file
  154. char pQTFileName[ MAX_PATH ];
  155. char pFullQTFileName[ MAX_PATH ];
  156. Q_snprintf( pQTFileName, sizeof( pQTFileName ), "%s", pFileName );
  157. V_SetExtension( pQTFileName, ".mov", sizeof( pQTFileName ) );
  158. if ( !g_pFullFileSystem->RelativePathToFullPath( pQTFileName, pPathID, pFullQTFileName, sizeof( pFullQTFileName ) ) )
  159. {
  160. // A file by that name was not found
  161. Assert( 0 );
  162. return false;
  163. }
  164. OpenQTMovie( pFullQTFileName );
  165. if ( !m_bActive )
  166. {
  167. // The file was unable to be opened
  168. Assert( 0 );
  169. return false;
  170. }
  171. // Now we can properly setup out regenerators
  172. m_TextureRegen.SetParentMaterial( this, m_VideoFrameWidth, m_VideoFrameHeight);
  173. CreateProceduralTexture( pMaterialName );
  174. CreateProceduralMaterial( pMaterialName );
  175. return true;
  176. }
  177. void CQuickTimeMaterial::Shutdown( void )
  178. {
  179. CloseQTFile();
  180. DestroyProceduralMaterial();
  181. DestroyProceduralTexture();
  182. Reset();
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: Updates our scene
  186. // Output : Returns true on success, false on failure.
  187. //-----------------------------------------------------------------------------
  188. bool CQuickTimeMaterial::Update( void )
  189. {
  190. Assert( m_bActive );
  191. #if defined ( QUICKTIME_VIDEO )
  192. OSType qTypes[1] = { VisualMediaCharacteristic };
  193. // is this our first frame?
  194. if ( !m_bMoviePlaying )
  195. {
  196. TimeValue startTime = -1;
  197. short qFlags = nextTimeMediaSample + nextTimeEdgeOK;
  198. GetMovieNextInterestingTime( m_QTMovie, qFlags, 1, qTypes, (TimeValue) 0, fixed1, &startTime, NULL);
  199. Assert( GetMoviesError() == noErr );
  200. GetMovieNextInterestingTime( m_QTMovie, nextTimeMediaSample, 1, qTypes, startTime, fixed1, &m_NextInterestingTimeToPlay, NULL);
  201. Assert( GetMoviesError() == noErr );
  202. SetMovieTimeValue( m_QTMovie, startTime );
  203. Assert( GetMoviesError() == noErr );
  204. m_LastInterestingTimePlayed = startTime;
  205. m_MovieBeganPlayingTime = Plat_FloatTime();
  206. m_bMoviePlaying = true;
  207. }
  208. else // we've drawn at least one frame before
  209. {
  210. // Get Current Time.. are we done playing the movie?
  211. double rightNow = Plat_FloatTime();
  212. m_MovieCurrentTime = rightNow - m_MovieBeganPlayingTime;
  213. // did we hit the end of the movie?
  214. if ( m_MovieCurrentTime >= m_QTMovieDurationinSec || m_NextInterestingTimeToPlay == -2 )
  215. {
  216. // If we're not looping, then report that we are done updating
  217. if ( m_bLoopMovie == false )
  218. {
  219. return false;
  220. }
  221. // ok we're looping the movie, so....
  222. // wrap around the current time
  223. while ( m_MovieCurrentTime >= m_QTMovieDurationinSec )
  224. {
  225. m_MovieBeganPlayingTime+= m_QTMovieDurationinSec;
  226. m_MovieCurrentTime = rightNow - m_MovieBeganPlayingTime;
  227. }
  228. // the next frame is set the frame 0, so it should trigger wrapping to the beginning
  229. long currentMovieTime = ( long ) ( m_MovieCurrentTime * m_QTMovieTimeScale );
  230. m_NextInterestingTimeToPlay = 0;
  231. // Reset the movie to the wrapped around time (probably should compute starttime instead of assuming 0)
  232. SetMovieTimeValue( m_QTMovie, currentMovieTime );
  233. Assert( GetMoviesError() == noErr );
  234. }
  235. // where are we in terms of QT media units?
  236. long currentMovieTime = ( long ) ( m_MovieCurrentTime * m_QTMovieTimeScale );
  237. // Enough time passed to get to next frame
  238. if ( currentMovieTime < m_NextInterestingTimeToPlay )
  239. {
  240. // nope.. use the previous frame
  241. return true;
  242. }
  243. TimeValue nextTimeAfter = -1;
  244. // do we need to skip any frames?
  245. while ( true )
  246. {
  247. // look at the sample time after the one we past
  248. GetMovieNextInterestingTime( m_QTMovie, nextTimeMediaSample, 1, qTypes, m_NextInterestingTimeToPlay, fixed1, &nextTimeAfter, NULL);
  249. OSErr lastErr = GetMoviesError();
  250. // hit the end of the movie?
  251. if ( lastErr == invalidTime )
  252. {
  253. nextTimeAfter = -2;
  254. break;
  255. }
  256. Assert( lastErr == noErr );
  257. // is there a later frame we should be showing?
  258. if ( nextTimeAfter <= currentMovieTime)
  259. {
  260. m_NextInterestingTimeToPlay = nextTimeAfter;
  261. nextTimeAfter = -1;
  262. }
  263. else
  264. {
  265. break;
  266. }
  267. }
  268. // SetMovieTimeValue( m_QTMovie, m_NextInterestingTimeToPlay );
  269. Assert( GetMoviesError() == noErr );
  270. m_LastInterestingTimePlayed = m_NextInterestingTimeToPlay;
  271. m_NextInterestingTimeToPlay = nextTimeAfter;
  272. }
  273. // move the movie along
  274. UpdateMovie( m_QTMovie );
  275. Assert( GetMoviesError() == noErr );
  276. MoviesTask( m_QTMovie, 10L );
  277. Assert( GetMoviesError() == noErr );
  278. #if defined (WIN32)
  279. HDC theHDC = (HDC) GetPortHDC( (GrafPtr) m_MovieGWorld );
  280. HBITMAP theHBITMAP = (HBITMAP) GetPortHBITMAP( (GrafPtr) m_MovieGWorld );
  281. // create the bitmapinfo header information
  282. BITMAP bmp;
  283. if ( !GetObject( theHBITMAP, sizeof(BITMAP), (LPSTR)&bmp) )
  284. {
  285. Assert( false );
  286. return false;
  287. }
  288. Assert( bmp.bmWidth == m_QTMovieRect.right );
  289. Assert( bmp.bmBitsPixel == 32 );
  290. BITMAPINFO tempInfo;
  291. V_memcpy( &tempInfo, &m_BitmapInfo, sizeof( tempInfo ) );
  292. // Retrieve the pixel bits (no color table)
  293. if ( !GetDIBits( theHDC, theHBITMAP, 0, (WORD) bmp.bmHeight, m_BitMapData, &tempInfo, DIB_RGB_COLORS))
  294. {
  295. AssertMsg( false, "writeBMP::GetDIB error" );
  296. return false;
  297. }
  298. #elif defined ( OSX )
  299. PixMapHandle thePixMap = GetGWorldPixMap( m_MovieGWorld );
  300. if ( LockPixels( thePixMap ) )
  301. {
  302. void *pPixels = GetPixBaseAddr( thePixMap );
  303. long rowStride = GetPixRowBytes( thePixMap );
  304. int rowBytes = m_VideoFrameWidth * 4;
  305. for (int y = 0; y < m_VideoFrameHeight; y++ )
  306. {
  307. BYTE *src = (BYTE*) pPixels + ( y * rowStride );
  308. BYTE *dst = (BYTE*) m_BitMapData + ( y * rowBytes );
  309. memcpy( dst, src, rowBytes );
  310. }
  311. UnlockPixels( thePixMap );
  312. }
  313. #endif
  314. // Regenerate our texture
  315. m_Texture->Download();
  316. #endif
  317. return true;
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Checks to see if the video has a new frame ready to download into the
  321. // texture
  322. //-----------------------------------------------------------------------------
  323. bool CQuickTimeMaterial::ReadyForSwap( void )
  324. {
  325. AssertExitF( m_bActive );
  326. // Waiting to play the first frame? Hell yes we are ready
  327. if ( !m_bMoviePlaying ) return true;
  328. // Get Current Time.. are we done playing the movie?
  329. double CurrentTime = Plat_FloatTime() - m_MovieBeganPlayingTime;
  330. // did we hit the end of the movie?
  331. if ( CurrentTime >= m_QTMovieDurationinSec || m_NextInterestingTimeToPlay == -2 )
  332. {
  333. // if we are looping, we have another frame, otherwise no
  334. return m_bLoopMovie;
  335. }
  336. // where are we in terms of QT media units?
  337. long currentMovieTime = ( long ) ( CurrentTime * m_QTMovieTimeScale );
  338. // Enough time passed to get to next frame??
  339. if ( currentMovieTime < m_NextInterestingTimeToPlay )
  340. {
  341. // nope.. use the previous frame
  342. return false;
  343. }
  344. // we have a new frame we want then..
  345. return true;
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Returns the material
  349. //-----------------------------------------------------------------------------
  350. IMaterial *CQuickTimeMaterial::GetMaterial()
  351. {
  352. return m_Material;
  353. }
  354. //-----------------------------------------------------------------------------
  355. // Returns the texcoord range
  356. //-----------------------------------------------------------------------------
  357. void CQuickTimeMaterial::GetTexCoordRange( float *pMaxU, float *pMaxV )
  358. {
  359. // no texture?
  360. if ( m_Texture == NULL )
  361. {
  362. *pMaxU = *pMaxV = 1.0f;
  363. return;
  364. }
  365. int nTextureWidth = m_Texture->GetActualWidth();
  366. int nTextureHeight = m_Texture->GetActualHeight();
  367. *pMaxU = (float) m_VideoFrameWidth / (float) nTextureWidth;
  368. *pMaxV = (float) m_VideoFrameHeight / (float) nTextureHeight;
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Returns the frame size of the QuickTime Video (stored in a subrect of the material itself)
  372. //-----------------------------------------------------------------------------
  373. void CQuickTimeMaterial::GetFrameSize( int *pWidth, int *pHeight )
  374. {
  375. *pWidth = m_VideoFrameWidth;
  376. *pHeight = m_VideoFrameHeight;
  377. }
  378. //-----------------------------------------------------------------------------
  379. // Computes a power of two at least as big as the passed-in number
  380. //-----------------------------------------------------------------------------
  381. static inline int ComputeGreaterPowerOfTwo( int n )
  382. {
  383. int i = 1;
  384. while ( i < n )
  385. {
  386. i <<= 1;
  387. }
  388. return i;
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Returns the frame rate of the Quicktime Video
  392. //-----------------------------------------------------------------------------
  393. int CQuickTimeMaterial::GetFrameRate( )
  394. {
  395. #if defined ( QUICKTIME_VIDEO )
  396. return m_QTMoveFrameRate;
  397. #else
  398. return 1;
  399. #endif
  400. }
  401. int CQuickTimeMaterial::GetFrameCount( )
  402. {
  403. #if defined ( QUICKTIME_VIDEO )
  404. return (int) ( m_QTMovieDurationinSec * m_QTMoveFrameRate );
  405. #else
  406. return 1;
  407. #endif
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Sets the frame for an QuickTime Material (use instead of SetTime)
  411. //-----------------------------------------------------------------------------
  412. void CQuickTimeMaterial::SetFrame( float flFrame )
  413. {
  414. flFrame;
  415. AssertMsg( false, "method not implemented " );
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Sets the movie to loop continously instead of end, or not
  419. //-----------------------------------------------------------------------------
  420. void CQuickTimeMaterial::SetLooping( bool loop )
  421. {
  422. m_bLoopMovie = loop;
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Initializes, shuts down the procedural texture
  426. //-----------------------------------------------------------------------------
  427. void CQuickTimeMaterial::CreateProceduralTexture( const char *pTextureName )
  428. {
  429. Assert( m_VideoFrameWidth >= cMinVideoFrameWidth && m_VideoFrameHeight >= cMinVideoFrameHeight &&
  430. m_VideoFrameWidth <= cMaxVideoFrameWidth && m_VideoFrameHeight <= cMaxVideoFrameHeight);
  431. Assert( pTextureName );
  432. // Choose power-of-two textures which are at least as big as the AVI
  433. int nWidth = ComputeGreaterPowerOfTwo( m_VideoFrameWidth );
  434. int nHeight = ComputeGreaterPowerOfTwo( m_VideoFrameHeight );
  435. // initialize the procedural texture as 32-it RGBA, w/o mipmaps
  436. m_Texture.InitProceduralTexture( pTextureName, "VideoCacheTextures", nWidth, nHeight,
  437. IMAGE_FORMAT_BGRA8888, TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT | TEXTUREFLAGS_NOMIP |
  438. TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_NOLOD );
  439. // Use this to get the updated frame from the remote connection
  440. m_Texture->SetTextureRegenerator( &m_TextureRegen /* , false */ );
  441. // compute the texcoords
  442. int nTextureWidth = m_Texture->GetActualWidth();
  443. int nTextureHeight = m_Texture->GetActualHeight();
  444. m_TexCordU = ( nTextureWidth > 0 ) ? (float) m_VideoFrameWidth / (float) nTextureWidth : 0.0f;
  445. m_TexCordV = ( nTextureHeight > 0 ) ? (float) m_VideoFrameHeight / (float) nTextureHeight : 0.0f;
  446. }
  447. void CQuickTimeMaterial::DestroyProceduralTexture()
  448. {
  449. if ( m_Texture != NULL )
  450. {
  451. // DO NOT Call release on the Texture Regenerator, as it will destroy this object! bad bad bad
  452. // instead we tell it to assign a NULL regenerator and flag it to not call release
  453. m_Texture->SetTextureRegenerator( NULL /*, false */ );
  454. // Texture, texture go away...
  455. m_Texture.Shutdown( true );
  456. }
  457. }
  458. //-----------------------------------------------------------------------------
  459. // Initializes, shuts down the procedural material
  460. //-----------------------------------------------------------------------------
  461. void CQuickTimeMaterial::CreateProceduralMaterial( const char *pMaterialName )
  462. {
  463. // create keyvalues if necessary
  464. KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
  465. {
  466. pVMTKeyValues->SetString( "$basetexture", m_Texture->GetName() );
  467. pVMTKeyValues->SetInt( "$nofog", 1 );
  468. pVMTKeyValues->SetInt( "$spriteorientation", 3 );
  469. pVMTKeyValues->SetInt( "$translucent", 1 );
  470. pVMTKeyValues->SetInt( "$nolod", 1 );
  471. pVMTKeyValues->SetInt( "$nomip", 1 );
  472. pVMTKeyValues->SetInt( "$gammacolorread", 0 );
  473. }
  474. // FIXME: gak, this is backwards. Why doesn't the material just see that it has a funky basetexture?
  475. m_Material.Init( pMaterialName, pVMTKeyValues );
  476. m_Material->Refresh();
  477. }
  478. void CQuickTimeMaterial::DestroyProceduralMaterial()
  479. {
  480. // Store the internal material pointer for later use
  481. IMaterial *pMaterial = m_Material;
  482. m_Material.Shutdown();
  483. materials->UncacheUnusedMaterials();
  484. // Now be sure to free that material because we don't want to reference it again later, we'll recreate it!
  485. if ( pMaterial != NULL )
  486. {
  487. pMaterial->DeleteIfUnreferenced();
  488. }
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Opens a movie file using quicktime
  492. //-----------------------------------------------------------------------------
  493. void CQuickTimeMaterial::OpenQTMovie( const char* theQTMovieFileName )
  494. {
  495. AssertExit( theQTMovieFileName != NULL );
  496. #if defined ( QUICKTIME_VIDEO )
  497. short theFile = 0;
  498. FSSpec sfFile;
  499. char fullPath[256];
  500. OSErr status = 0;
  501. // Set graphics port
  502. #if defined ( WIN32 )
  503. SetGWorld ( (CGrafPtr) GetNativeWindowPort( nil ), nil );
  504. #elif defined ( OSX )
  505. SetGWorld( nil, nil );
  506. #endif
  507. SetQTFileName( theQTMovieFileName );
  508. #if defined ( OSX )
  509. FSRef dirRef;
  510. Boolean isDir;
  511. status = FSPathMakeRef( (UInt8 *)theQTMovieFileName, &dirRef, &isDir );
  512. if ( status == noErr )
  513. {
  514. status = FSGetCatalogInfo( &dirRef, kFSCatInfoNone, NULL, NULL, &sfFile, NULL );
  515. Assert( status == noErr );
  516. }
  517. if ( status != noErr )
  518. {
  519. Reset();
  520. return;
  521. }
  522. #elif defined ( WIN32 )
  523. strcpy ( fullPath, theQTMovieFileName); // Copy full pathname
  524. c2pstr ( fullPath ); // Convert to Pascal string
  525. status = FSMakeFSSpec( 0, 0L, (const unsigned char *)&fullPath[0], &sfFile ); // Make file-system specification record
  526. AssertExit( status == noErr );
  527. #endif
  528. status = OpenMovieFile( &sfFile, &theFile, fsRdPerm) ; // Open movie file
  529. Assert( status == noErr );
  530. if ( status != noErr )
  531. {
  532. CloseMovieFile( theFile );
  533. Reset();
  534. return;
  535. }
  536. status = NewMovieFromFile ( &m_QTMovie, theFile, nil, nil, newMovieActive, nil); // Get movie from file
  537. Assert( status == noErr );
  538. if ( status != noErr )
  539. {
  540. CloseMovieFile( theFile );
  541. Reset();
  542. return;
  543. }
  544. status = CloseMovieFile (theFile); // Close movie file
  545. AssertExit( status == noErr );
  546. // Now we need to extract the time info from the QT Movie
  547. // Duration scale = 600 per second...
  548. m_QTMovieTimeScale = GetMovieTimeScale( m_QTMovie );
  549. m_QTMovieDuration = GetMovieDuration( m_QTMovie );
  550. m_QTMovieDurationinSec = float ( m_QTMovieDuration ) / float ( m_QTMovieTimeScale );
  551. Fixed movieRate = GetMoviePreferredRate( m_QTMovie );
  552. m_QTMoveFrameRate = Fix2Long( movieRate );
  553. // what size do we set the output rect to?
  554. GetMovieNaturalBoundsRect(m_QTMovie, &m_QTMovieRect);
  555. m_VideoFrameWidth = m_QTMovieRect.right;
  556. m_VideoFrameHeight = m_QTMovieRect.bottom;
  557. // Sanity check...
  558. AssertExit( m_QTMovieRect.top == 0 && m_QTMovieRect.left == 0 && m_QTMovieRect.right >= 64 && m_QTMovieRect.right <= 1920 &&
  559. m_QTMovieRect.bottom >= 48 && m_QTMovieRect.bottom <= 1200 && m_QTMovieRect.right % 4 == 0 );
  560. // Setup a bitmap to store frames...
  561. // compute image buffer size
  562. m_BitMapDataSize = 4 * m_QTMovieRect.right * m_QTMovieRect.bottom;
  563. #if defined ( WIN32 )
  564. // Initialize bitmap info
  565. ZeroVar( m_BitmapInfo );
  566. m_BitmapInfo.bmiHeader.biSize = sizeof( m_BitmapInfo.bmiHeader );
  567. m_BitmapInfo.bmiHeader.biWidth = (LONG) m_QTMovieRect.right;
  568. m_BitmapInfo.bmiHeader.biHeight = -1 * (LONG) m_QTMovieRect.bottom;
  569. m_BitmapInfo.bmiHeader.biPlanes = 1;
  570. m_BitmapInfo.bmiHeader.biBitCount = 32;
  571. m_BitmapInfo.bmiHeader.biCompression = 0; /* BI_RGB */
  572. m_BitmapInfo.bmiHeader.biSizeImage = m_BitMapDataSize;
  573. // the rest of the fields should be 0
  574. #endif
  575. // create buffer to hold a single frame
  576. m_BitMapData = new byte[ m_BitMapDataSize ];
  577. // Setup the QuiuckTime Graphics World for the Movie
  578. status = QTNewGWorld( &m_MovieGWorld, k32BGRAPixelFormat, &m_QTMovieRect, nil, nil, 0 );
  579. AssertExit( status == noErr );
  580. // perform any needed gamma correction
  581. // kQTUsePlatformDefaultGammaLevel = 0, /* When decompressing into this PixMap, gamma-correct to the platform's standard gamma. */
  582. // kQTUseSourceGammaLevel = -1L, /* When decompressing into this PixMap, don't perform gamma-correction. */
  583. // kQTCCIR601VideoGammaLevel = 0x00023333 /* 2.2, standard television video gamma.*/
  584. // Fixed cGamma1_8 = 0x0001CCCC; // Gamma 1.8
  585. // Fixed cGamma2_5 = 0x00028000; // Gamma 2.5
  586. //
  587. // On OSX it appears we need to set a gamma of 1.0 or 0x0001000 - the values are interpreted differently?
  588. #if defined ( OSX )
  589. Fixed decodeGamma = 0x00012000;
  590. #elif defined ( WIN32 )
  591. Fixed decodeGamma = 0x00023333;
  592. #endif
  593. // Get the pix map for the GWorld and adjust the gamma correction on it
  594. PixMapHandle thePixMap = GetGWorldPixMap( m_MovieGWorld );
  595. OSErr Status = QTSetPixMapHandleGammaLevel( thePixMap, decodeGamma );
  596. AssertExit( Status == noErr );
  597. Status = QTSetPixMapHandleRequestedGammaLevel( thePixMap, decodeGamma );
  598. AssertExit( Status == noErr );
  599. SetMovieGWorld( m_QTMovie, m_MovieGWorld, nil );
  600. #if defined ( WIN32 )
  601. WCHAR strGUID[39];
  602. int numBytes = StringFromGUID2( DSDEVID_DefaultPlayback, (LPOLESTR) strGUID, 39); // CLSID_DirectSound is not what you want here
  603. // create the audio context
  604. CFStringRef deviceNameStrRef = NULL;
  605. deviceNameStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault,
  606. (const UniChar*) strGUID,
  607. (CFIndex) (numBytes -1) );
  608. OSStatus result = QTAudioContextCreateForAudioDevice( NULL, deviceNameStrRef, NULL, &m_AudioContext );
  609. #elif defined ( OSX )
  610. OSStatus result = QTAudioContextCreateForAudioDevice( NULL, NULL, NULL, &m_AudioContext );
  611. #endif
  612. AssertExit( result == noErr );
  613. // Set the audio context
  614. result = SetMovieAudioContext( m_QTMovie, m_AudioContext );
  615. AssertExit( result == noErr );
  616. // Set the volume
  617. ConVarRef volumeConVar( "volume" );
  618. float sysVolume = 1.0f;
  619. if ( volumeConVar.IsValid() )
  620. sysVolume = volumeConVar.GetFloat();
  621. clamp( sysVolume, 0.0f, 1.0f);
  622. short movieVolume = (short) ( sysVolume * 256.0 );
  623. SetMovieVolume( m_QTMovie, movieVolume );
  624. // Start movie playback (get the sound rolling)
  625. StartMovie( m_QTMovie );
  626. m_bActive = true;
  627. #if defined( WIN32 )
  628. if ( deviceNameStrRef )
  629. {
  630. CFRelease( deviceNameStrRef );
  631. }
  632. #endif
  633. #endif
  634. }
  635. void CQuickTimeMaterial::CloseQTFile()
  636. {
  637. #if defined ( QUICKTIME_VIDEO )
  638. StopMovie( m_QTMovie );
  639. SAFE_DELETE_ARRAY( m_BitMapData );
  640. if ( m_AudioContext != NULL )
  641. {
  642. QTAudioContextRelease( m_AudioContext );
  643. m_AudioContext = NULL;
  644. }
  645. if ( m_MovieGWorld != NULL )
  646. {
  647. DisposeGWorld( m_MovieGWorld );
  648. m_MovieGWorld = NULL;
  649. }
  650. if ( m_QTMovie )
  651. {
  652. DisposeMovie( m_QTMovie );
  653. m_QTMovie = NULL;
  654. }
  655. SetQTFileName( NULL );
  656. #endif
  657. }
  658. //-----------------------------------------------------------------------------
  659. // Constructor/destructor
  660. //-----------------------------------------------------------------------------
  661. CQuickTime::CQuickTime()
  662. {
  663. m_bQTInitialized = false;
  664. }
  665. // ----------------------------------------------------------------------------
  666. CQuickTime::~CQuickTime()
  667. {
  668. // Make sure we shut down quicktime
  669. ShutdownQuicktime();
  670. }
  671. //-----------------------------------------------------------------------------
  672. // Connect/disconnect
  673. //-----------------------------------------------------------------------------
  674. bool CQuickTime::Connect( CreateInterfaceFn factory )
  675. {
  676. ConnectTier1Libraries( &factory, 1 );
  677. ConnectTier2Libraries( &factory, 1 );
  678. if ( !( g_pFullFileSystem && materials ) )
  679. {
  680. Msg( "Quicktime failed to connect to a required system\n" );
  681. }
  682. return ( g_pFullFileSystem && materials );
  683. }
  684. //-----------------------------------------------------------------------------
  685. // Connect/disconnect
  686. //-----------------------------------------------------------------------------
  687. void CQuickTime::Disconnect( void )
  688. {
  689. // Make sure we shut down quicktime
  690. ShutdownQuicktime();
  691. }
  692. //-----------------------------------------------------------------------------
  693. // Query Interface
  694. //-----------------------------------------------------------------------------
  695. void *CQuickTime::QueryInterface( const char *pInterfaceName )
  696. {
  697. if ( Q_strncmp( pInterfaceName, QUICKTIME_INTERFACE_VERSION, Q_strlen(QUICKTIME_INTERFACE_VERSION) + 1) == 0 )
  698. {
  699. return (IQuickTime*)this;
  700. }
  701. return NULL;
  702. }
  703. //-----------------------------------------------------------------------------
  704. // Init/shutdown
  705. //-----------------------------------------------------------------------------
  706. InitReturnVal_t CQuickTime::Init()
  707. {
  708. return SetupQuicktime() ? INIT_OK : INIT_FAILED;
  709. }
  710. //-----------------------------------------------------------------------------
  711. void CQuickTime::Shutdown()
  712. {
  713. ShutdownQuicktime();
  714. }
  715. //-----------------------------------------------------------------------------
  716. // Create/destroy an QuickTime material
  717. //-----------------------------------------------------------------------------
  718. QUICKTIMEMaterial_t CQuickTime::CreateMaterial( const char *pMaterialName, const char *pFileName, const char *pPathID, int flags )
  719. {
  720. if ( ! m_bQTInitialized )
  721. {
  722. return QUICKTIMEMATERIAL_INVALID;
  723. }
  724. QUICKTIMEMaterial_t h = m_QTMaterials.AddToTail();
  725. m_QTMaterials[h] = new CQuickTimeMaterial;
  726. if ( m_QTMaterials[h]->Init( pMaterialName, pFileName, pPathID ) == false )
  727. {
  728. delete m_QTMaterials[h];
  729. m_QTMaterials.Remove( h );
  730. return QUICKTIMEMATERIAL_INVALID;
  731. }
  732. m_QTMaterials[h]->SetLooping( ( flags & QUICKTIME_LOOP_MOVIE ) == QUICKTIME_LOOP_MOVIE );
  733. return h;
  734. }
  735. // ----------------------------------------------------------------------------
  736. void CQuickTime::DestroyMaterial( QUICKTIMEMaterial_t h )
  737. {
  738. if ( h != QUICKTIMEMATERIAL_INVALID )
  739. {
  740. m_QTMaterials[h]->Shutdown();
  741. delete m_QTMaterials[h];
  742. m_QTMaterials.Remove( h );
  743. }
  744. }
  745. //-----------------------------------------------------------------------------
  746. // Update the QuickTime Video Material
  747. // Output : Returns true on success, false on failure.
  748. //-----------------------------------------------------------------------------
  749. bool CQuickTime::Update( QUICKTIMEMaterial_t hMaterial )
  750. {
  751. return ( hMaterial == QUICKTIMEMATERIAL_INVALID ) ? false : m_QTMaterials[hMaterial]->Update();
  752. }
  753. //-----------------------------------------------------------------------------
  754. // Determine if a new frame of the movie is ready for display
  755. //-----------------------------------------------------------------------------
  756. bool CQuickTime::ReadyForSwap( QUICKTIMEMaterial_t hMaterial )
  757. {
  758. return ( hMaterial == QUICKTIMEMATERIAL_INVALID ) ? false : m_QTMaterials[hMaterial]->ReadyForSwap();
  759. }
  760. //-----------------------------------------------------------------------------
  761. // Gets the IMaterial associated with an QuickTime video material
  762. //-----------------------------------------------------------------------------
  763. IMaterial *CQuickTime::GetMaterial( QUICKTIMEMaterial_t h )
  764. {
  765. return ( h != QUICKTIMEMATERIAL_INVALID ) ? m_QTMaterials[h]->GetMaterial() : NULL;
  766. }
  767. //-----------------------------------------------------------------------------
  768. // Returns the max texture coordinate of the QuickTime Video on the texture
  769. //-----------------------------------------------------------------------------
  770. void CQuickTime::GetTexCoordRange( QUICKTIMEMaterial_t h, float *pMaxU, float *pMaxV )
  771. {
  772. if ( h != QUICKTIMEMATERIAL_INVALID )
  773. {
  774. m_QTMaterials[h]->GetTexCoordRange( pMaxU, pMaxV );
  775. }
  776. else
  777. {
  778. *pMaxU = *pMaxV = 1.0f;
  779. }
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Returns the frame size of the Quicktime Video (is a subrect of the material itself)
  783. //-----------------------------------------------------------------------------
  784. void CQuickTime::GetFrameSize( QUICKTIMEMaterial_t h, int *pWidth, int *pHeight )
  785. {
  786. if ( h != QUICKTIMEMATERIAL_INVALID )
  787. {
  788. m_QTMaterials[h]->GetFrameSize( pWidth, pHeight );
  789. }
  790. else
  791. {
  792. *pWidth = *pHeight = 1;
  793. }
  794. }
  795. //-----------------------------------------------------------------------------
  796. // Returns the frame size of the QuickTime Video (is a subrect of the material itself)
  797. //-----------------------------------------------------------------------------
  798. int CQuickTime::GetFrameRate( QUICKTIMEMaterial_t h )
  799. {
  800. return ( h == QUICKTIMEMATERIAL_INVALID ) ? -1 : m_QTMaterials[h]->GetFrameRate();
  801. }
  802. //-----------------------------------------------------------------------------
  803. // Sets the frame for an Quicktime Video material (use instead of SetTime)
  804. //-----------------------------------------------------------------------------
  805. void CQuickTime::SetFrame( QUICKTIMEMaterial_t h, float flFrame )
  806. {
  807. if ( h != QUICKTIMEMATERIAL_INVALID )
  808. {
  809. m_QTMaterials[h]->SetFrame( flFrame );
  810. }
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Returns the frame rate of the Quicktime Video
  814. //-----------------------------------------------------------------------------
  815. int CQuickTime::GetFrameCount( QUICKTIMEMaterial_t h )
  816. {
  817. return ( h == QUICKTIMEMATERIAL_INVALID ) ? -1 : m_QTMaterials[h]->GetFrameCount();
  818. }
  819. //-----------------------------------------------------------------------------
  820. // Hooks up the houtput sound device
  821. //-----------------------------------------------------------------------------
  822. bool CQuickTime::SetSoundDevice( void *pDevice )
  823. {
  824. pDevice;
  825. return true;
  826. }
  827. // ----------------------------------------------------------------------------
  828. // functions to initialize and shut down QuickTime services
  829. // ----------------------------------------------------------------------------
  830. bool CQuickTime::SetupQuicktime()
  831. {
  832. m_bQTInitialized = false;
  833. #if defined ( QUICKTIME_VIDEO )
  834. #if defined ( WIN32 )
  835. OSErr status = InitializeQTML( 0 );
  836. // if -2903 then quicktime not installed on this system
  837. if ( status != noErr )
  838. {
  839. if ( status == qtmlDllLoadErr )
  840. {
  841. //Plat_MessageBox( "VideoCache ERROR", "ERROR: QuickTime is not installed on this system. It is needed in order for the SFM Video Cache service to run" );
  842. Assert( 0 );
  843. }
  844. return false;
  845. }
  846. // Make sure we have version 7.04 or greater of quicktime
  847. long version = 0;
  848. status = Gestalt(gestaltQuickTime, &version);
  849. if ( (status != noErr) || ( version < 0x07048000) )
  850. {
  851. TerminateQTML();
  852. return false;
  853. }
  854. #endif
  855. OSErr status2 = EnterMovies(); // Initialize QuickTime Movie Toolbox
  856. if ( status2 != noErr )
  857. {
  858. Assert( 0 );
  859. #if defined ( WIN32 )
  860. TerminateQTML();
  861. #endif
  862. return false;
  863. }
  864. m_bQTInitialized = true;
  865. #endif
  866. return m_bQTInitialized;
  867. }
  868. // ----------------------------------------------------------------------------
  869. void CQuickTime::ShutdownQuicktime()
  870. {
  871. if ( m_bQTInitialized )
  872. {
  873. #if defined ( QUICKTIME_VIDEO )
  874. ExitMovies(); // Terminate QuickTime
  875. #if defined ( WIN32 )
  876. TerminateQTML(); // Terminate QTML
  877. #endif
  878. #endif
  879. m_bQTInitialized = false;
  880. }
  881. }