Team Fortress 2 Source Code as on 22/4/2020
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.

962 lines
27 KiB

  1. //========= Copyright 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 "quicktime_material.h"
  19. #if defined ( WIN32 )
  20. #include <WinDef.h>
  21. #include <../dx9sdk/include/dsound.h>
  22. #endif
  23. #include "tier0/memdbgon.h"
  24. // ===========================================================================
  25. // CQuicktimeMaterialRGBTextureRegenerator - Inherited from ITextureRegenerator
  26. // Copies and converts the buffer bits to texture bits
  27. // Currently only supports 32-bit BGRA
  28. // ===========================================================================
  29. CQuicktimeMaterialRGBTextureRegenerator::CQuicktimeMaterialRGBTextureRegenerator() :
  30. m_SrcGWorld( nullptr ),
  31. m_nSourceWidth( 0 ),
  32. m_nSourceHeight( 0 )
  33. {
  34. }
  35. CQuicktimeMaterialRGBTextureRegenerator::~CQuicktimeMaterialRGBTextureRegenerator()
  36. {
  37. // nothing to do
  38. }
  39. void CQuicktimeMaterialRGBTextureRegenerator::SetSourceGWorld( GWorldPtr theGWorld, int nWidth, int nHeight )
  40. {
  41. m_SrcGWorld = theGWorld;
  42. m_nSourceWidth = nWidth;
  43. m_nSourceHeight = nHeight;
  44. }
  45. void CQuicktimeMaterialRGBTextureRegenerator::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
  46. {
  47. AssertExit( pVTFTexture != nullptr );
  48. // Error condition, should only have 1 frame, 1 face, 1 mip level
  49. if ( ( pVTFTexture->FrameCount() > 1 ) || ( pVTFTexture->FaceCount() > 1 ) || ( pVTFTexture->MipCount() > 1 ) || ( pVTFTexture->Depth() > 1 ) )
  50. {
  51. WarningAssert( "Texture Properties Incorrect ");
  52. memset( pVTFTexture->ImageData(), 0xAA, pVTFTexture->ComputeTotalSize() );
  53. return;
  54. }
  55. // Make sure we have a valid video image source
  56. if ( m_SrcGWorld == nullptr )
  57. {
  58. WarningAssert( "Video texture source not set" );
  59. memset( pVTFTexture->ImageData(), 0xCC, pVTFTexture->ComputeTotalSize() );
  60. return;
  61. }
  62. // Verify the destination texture is set up correctly
  63. Assert( pVTFTexture->Format() == IMAGE_FORMAT_BGRA8888 );
  64. Assert( pVTFTexture->RowSizeInBytes( 0 ) >= pVTFTexture->Width() * 4 );
  65. Assert( pVTFTexture->Width() >= m_nSourceWidth );
  66. Assert( pVTFTexture->Height() >= m_nSourceHeight );
  67. // Copy directly from the Quicktime GWorld
  68. PixMapHandle thePixMap = GetGWorldPixMap( m_SrcGWorld );
  69. if ( LockPixels( thePixMap ) )
  70. {
  71. BYTE *pImageData = pVTFTexture->ImageData();
  72. int dstStride = pVTFTexture->RowSizeInBytes( 0 );
  73. BYTE *pSrcData = (BYTE*) GetPixBaseAddr( thePixMap );
  74. long srcStride = QTGetPixMapHandleRowBytes( thePixMap );
  75. int rowSize = m_nSourceWidth * 4;
  76. for (int y = 0; y < m_nSourceHeight; y++ )
  77. {
  78. memcpy( pImageData, pSrcData, rowSize );
  79. pImageData+= dstStride;
  80. pSrcData+= srcStride;
  81. }
  82. UnlockPixels( thePixMap );
  83. }
  84. else
  85. {
  86. WarningAssert( "LockPixels Failed" );
  87. }
  88. }
  89. void CQuicktimeMaterialRGBTextureRegenerator::Release()
  90. {
  91. // we don't invoke the destructor here, we're not using the no-release extensions
  92. }
  93. // ===========================================================================
  94. // CQuickTimeMaterial class - creates a material, opens a QuickTime movie
  95. // and plays the movie onto the material
  96. // ===========================================================================
  97. //-----------------------------------------------------------------------------
  98. // CQuickTimeMaterial Constructor
  99. //-----------------------------------------------------------------------------
  100. CQuickTimeMaterial::CQuickTimeMaterial() :
  101. m_pFileName( nullptr ),
  102. m_MovieGWorld( nullptr ),
  103. m_QTMovie( nullptr ),
  104. m_AudioContext( nullptr ),
  105. m_bInitCalled( false )
  106. {
  107. Reset();
  108. }
  109. //-----------------------------------------------------------------------------
  110. // CQuickTimeMaterial Destructor
  111. //-----------------------------------------------------------------------------
  112. CQuickTimeMaterial::~CQuickTimeMaterial()
  113. {
  114. Reset();
  115. }
  116. void CQuickTimeMaterial::Reset()
  117. {
  118. SetQTFileName( nullptr );
  119. DestroyProceduralTexture();
  120. DestroyProceduralMaterial();
  121. m_TexCordU = 0.0f;
  122. m_TexCordV = 0.0f;
  123. m_VideoFrameWidth = 0;
  124. m_VideoFrameHeight = 0;
  125. m_PlaybackFlags = VideoPlaybackFlags::NO_PLAYBACK_OPTIONS;
  126. m_bMovieInitialized = false;
  127. m_bMoviePlaying = false;
  128. m_bMovieFinishedPlaying = false;
  129. m_bMoviePaused = false;
  130. m_bLoopMovie = false;
  131. m_bHasAudio = false;
  132. m_bMuted = false;
  133. m_CurrentVolume = 0.0f;
  134. m_QTMovieTimeScale = 0;
  135. m_QTMovieDuration = 0;
  136. m_QTMovieDurationinSec = 0.0f;
  137. m_QTMovieFrameRate.SetFPS( 0, false );
  138. SAFE_RELEASE_AUDIOCONTEXT( m_AudioContext );
  139. SAFE_DISPOSE_GWORLD( m_MovieGWorld );
  140. SAFE_DISPOSE_MOVIE( m_QTMovie );
  141. m_LastResult = VideoResult::SUCCESS;
  142. }
  143. void CQuickTimeMaterial::SetQTFileName( const char *theQTMovieFileName )
  144. {
  145. SAFE_DELETE_ARRAY( m_pFileName );
  146. if ( theQTMovieFileName != nullptr )
  147. {
  148. AssertMsg( V_strlen( theQTMovieFileName ) <= MAX_QT_FILENAME_LEN, "Bad Quicktime Movie Filename" );
  149. m_pFileName = COPY_STRING( theQTMovieFileName );
  150. }
  151. }
  152. VideoResult_t CQuickTimeMaterial::SetResult( VideoResult_t status )
  153. {
  154. m_LastResult = status;
  155. return status;
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Video information functions
  159. //-----------------------------------------------------------------------------
  160. //-----------------------------------------------------------------------------
  161. // Returns the resolved filename of the video, as it might differ from
  162. // what the user supplied, (also with absolute path)
  163. //-----------------------------------------------------------------------------
  164. const char *CQuickTimeMaterial::GetVideoFileName()
  165. {
  166. return m_pFileName;
  167. }
  168. VideoFrameRate_t &CQuickTimeMaterial::GetVideoFrameRate()
  169. {
  170. return m_QTMovieFrameRate;
  171. }
  172. VideoResult_t CQuickTimeMaterial::GetLastResult()
  173. {
  174. return m_LastResult;
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Audio Functions
  178. //-----------------------------------------------------------------------------
  179. bool CQuickTimeMaterial::HasAudio()
  180. {
  181. return m_bHasAudio;
  182. }
  183. bool CQuickTimeMaterial::SetVolume( float fVolume )
  184. {
  185. clamp( fVolume, 0.0f, 1.0f );
  186. m_CurrentVolume = fVolume;
  187. if ( m_AudioContext != nullptr && m_bHasAudio )
  188. {
  189. short movieVolume = (short) ( m_CurrentVolume * 256.0f );
  190. SetMovieVolume( m_QTMovie, movieVolume );
  191. SetResult( VideoResult::SUCCESS );
  192. return true;
  193. }
  194. SetResult( VideoResult::AUDIO_ERROR_OCCURED );
  195. return false;
  196. }
  197. float CQuickTimeMaterial::GetVolume()
  198. {
  199. return m_CurrentVolume;
  200. }
  201. void CQuickTimeMaterial::SetMuted( bool bMuteState )
  202. {
  203. AssertExitFunc( m_bMoviePlaying, SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE) );
  204. SetResult( VideoResult::SUCCESS );
  205. if ( bMuteState == m_bMuted ) // no change?
  206. {
  207. return;
  208. }
  209. m_bMuted = bMuteState;
  210. if ( m_bHasAudio )
  211. {
  212. OSStatus result = SetMovieAudioMute( m_QTMovie, m_bMuted, 0 );
  213. AssertExitFunc( result == noErr, SetResult( VideoResult::AUDIO_ERROR_OCCURED) );
  214. }
  215. SetResult( VideoResult::SUCCESS );
  216. }
  217. bool CQuickTimeMaterial::IsMuted()
  218. {
  219. return m_bMuted;
  220. }
  221. VideoResult_t CQuickTimeMaterial::SoundDeviceCommand( VideoSoundDeviceOperation_t operation, void *pDevice, void *pData )
  222. {
  223. AssertExitV( m_bMovieInitialized || m_bMoviePlaying, VideoResult::OPERATION_OUT_OF_SEQUENCE );
  224. switch( operation )
  225. {
  226. // On win32, we try and create an audio context from a GUID
  227. case VideoSoundDeviceOperation::SET_DIRECT_SOUND_DEVICE:
  228. {
  229. #if defined ( WIN32 )
  230. SAFE_RELEASE_AUDIOCONTEXT( m_AudioContext );
  231. return ( CreateMovieAudioContext( m_bHasAudio, m_QTMovie, &m_AudioContext ) ? SetResult( VideoResult::SUCCESS ) : SetResult( VideoResult::AUDIO_ERROR_OCCURED ) );
  232. #else
  233. // On any other OS, we don't support this operation
  234. return SetResult( VideoResult::OPERATION_NOT_SUPPORTED );
  235. #endif
  236. }
  237. case VideoSoundDeviceOperation::SET_SOUND_MANAGER_DEVICE:
  238. {
  239. #if defined ( OSX )
  240. SAFE_RELEASE_AUDIOCONTEXT( m_AudioContext );
  241. return ( CreateMovieAudioContext( m_bHasAudio, m_QTMovie, &m_AudioContext ) ? SetResult( VideoResult::SUCCESS ) : SetResult( VideoResult::AUDIO_ERROR_OCCURED ) );
  242. #else
  243. // On any other OS, we don't support this operation
  244. return SetResult( VideoResult::OPERATION_NOT_SUPPORTED );
  245. #endif
  246. }
  247. case VideoSoundDeviceOperation::SET_LIB_AUDIO_DEVICE:
  248. case VideoSoundDeviceOperation::HOOK_X_AUDIO:
  249. case VideoSoundDeviceOperation::SET_MILES_SOUND_DEVICE:
  250. {
  251. return SetResult( VideoResult::OPERATION_NOT_SUPPORTED );
  252. }
  253. default:
  254. {
  255. return SetResult( VideoResult::BAD_INPUT_PARAMETERS );
  256. }
  257. }
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Initializes the video material
  261. //-----------------------------------------------------------------------------
  262. bool CQuickTimeMaterial::Init( const char *pMaterialName, const char *pFileName, VideoPlaybackFlags_t flags )
  263. {
  264. SetResult( VideoResult::BAD_INPUT_PARAMETERS );
  265. AssertExitF( IS_NOT_EMPTY( pFileName ) );
  266. AssertExitF( m_bInitCalled == false );
  267. m_PlaybackFlags = flags;
  268. OpenQTMovie( pFileName ); // Open up the Quicktime file
  269. if ( !m_bMovieInitialized )
  270. {
  271. return false; // Something bad happened when we went to open
  272. }
  273. // Now we can properly setup our regenerators
  274. m_TextureRegen.SetSourceGWorld( m_MovieGWorld, m_VideoFrameWidth, m_VideoFrameHeight );
  275. CreateProceduralTexture( pMaterialName );
  276. CreateProceduralMaterial( pMaterialName );
  277. // Start movie playback
  278. if ( !BITFLAGS_SET( m_PlaybackFlags, VideoPlaybackFlags::DONT_AUTO_START_VIDEO ) )
  279. {
  280. StartVideo();
  281. }
  282. m_bInitCalled = true; // Look, if you only got one shot...
  283. return true;
  284. }
  285. void CQuickTimeMaterial::Shutdown( void )
  286. {
  287. StopVideo();
  288. Reset();
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Video playback state functions
  292. //-----------------------------------------------------------------------------
  293. bool CQuickTimeMaterial::IsVideoReadyToPlay()
  294. {
  295. return m_bMovieInitialized;
  296. }
  297. bool CQuickTimeMaterial::IsVideoPlaying()
  298. {
  299. return m_bMoviePlaying;
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Checks to see if the video has a new frame ready to be rendered and
  303. // downloaded into the texture and eventually display
  304. //-----------------------------------------------------------------------------
  305. bool CQuickTimeMaterial::IsNewFrameReady( void )
  306. {
  307. // Are we waiting to start playing the first frame? if so, tell them we are ready!
  308. if ( m_bMovieInitialized == true )
  309. {
  310. return true;
  311. }
  312. // We better be playing the movie
  313. AssertExitF( m_bMoviePlaying );
  314. // paused?
  315. if ( m_bMoviePaused )
  316. {
  317. return false;
  318. }
  319. TimeValue curMovieTime = GetMovieTime( m_QTMovie, nullptr );
  320. if ( curMovieTime >= m_QTMovieDuration || m_NextInterestingTimeToPlay == NO_MORE_INTERESTING_TIMES )
  321. {
  322. // if we are looping, we have another frame, otherwise no
  323. return m_bLoopMovie;
  324. }
  325. // Enough time passed to get to next frame??
  326. if ( curMovieTime < m_NextInterestingTimeToPlay )
  327. {
  328. // nope.. use the previous frame
  329. return false;
  330. }
  331. // we have a new frame we want then..
  332. return true;
  333. }
  334. bool CQuickTimeMaterial::IsFinishedPlaying()
  335. {
  336. return m_bMovieFinishedPlaying;
  337. }
  338. void CQuickTimeMaterial::SetLooping( bool bLoopVideo )
  339. {
  340. m_bLoopMovie = bLoopVideo;
  341. }
  342. bool CQuickTimeMaterial::IsLooping()
  343. {
  344. return m_bLoopMovie;
  345. }
  346. void CQuickTimeMaterial::SetPaused( bool bPauseState )
  347. {
  348. if ( !m_bMoviePlaying || m_bMoviePaused == bPauseState )
  349. {
  350. Assert( m_bMoviePlaying );
  351. return;
  352. }
  353. if ( bPauseState ) // Pausing the movie?
  354. {
  355. // Save off current time and set paused state
  356. m_MoviePauseTime = GetMovieTime( m_QTMovie, nullptr );
  357. StopMovie( m_QTMovie );
  358. }
  359. else // unpausing the movie
  360. {
  361. // Reset the movie to the paused time
  362. SetMovieTimeValue( m_QTMovie, m_MoviePauseTime );
  363. StartMovie( m_QTMovie );
  364. Assert( GetMoviesError() == noErr );
  365. }
  366. m_bMoviePaused = bPauseState;
  367. }
  368. bool CQuickTimeMaterial::IsPaused()
  369. {
  370. return ( m_bMoviePlaying ) ? m_bMoviePaused : false;
  371. }
  372. // Begins playback of the movie
  373. bool CQuickTimeMaterial::StartVideo()
  374. {
  375. if ( !m_bMovieInitialized )
  376. {
  377. Assert( false );
  378. SetResult( VideoResult::OPERATION_ALREADY_PERFORMED );
  379. return false;
  380. }
  381. // Start the movie playing at the first frame
  382. SetMovieTimeValue( m_QTMovie, m_MovieFirstFrameTime );
  383. Assert( GetMoviesError() == noErr );
  384. StartMovie( m_QTMovie );
  385. Assert( GetMoviesError() == noErr );
  386. // Transition to playing state
  387. m_bMovieInitialized = false;
  388. m_bMoviePlaying = true;
  389. // Deliberately set the next interesting time to the current time to
  390. // insure that the ::update() call causes the textures to be downloaded
  391. m_NextInterestingTimeToPlay = m_MovieFirstFrameTime;
  392. Update();
  393. return true;
  394. }
  395. // stops movie for good, frees resources, but retains texture & material of last frame rendered
  396. bool CQuickTimeMaterial::StopVideo()
  397. {
  398. if ( !m_bMoviePlaying )
  399. {
  400. SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE );
  401. return false;
  402. }
  403. StopMovie( m_QTMovie );
  404. m_bMoviePlaying = false;
  405. m_bMoviePaused = false;
  406. m_bMovieFinishedPlaying = true;
  407. // free resources
  408. CloseQTFile();
  409. SetResult( VideoResult::SUCCESS );
  410. return true;
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Purpose: Updates our scene
  414. // Output : true = movie playing ok, false = time to end movie
  415. // supposed to be: Returns true on a new frame of video being downloaded into the texture
  416. //-----------------------------------------------------------------------------
  417. bool CQuickTimeMaterial::Update( void )
  418. {
  419. AssertExitF( m_bMoviePlaying );
  420. OSType qTypes[1] = { VisualMediaCharacteristic };
  421. // are we paused? can't update if so...
  422. if ( m_bMoviePaused )
  423. {
  424. return true; // reuse the last frame
  425. }
  426. // Get current time in the movie
  427. TimeValue curMovieTime = GetMovieTime( m_QTMovie, nullptr );
  428. // Did we hit the end of the movie?
  429. if ( curMovieTime >= m_QTMovieDuration )
  430. {
  431. // If we're not looping, then report that we are done updating
  432. if ( m_bLoopMovie == false )
  433. {
  434. StopVideo();
  435. return false;
  436. }
  437. // Reset the movie to the start time
  438. SetMovieTimeValue( m_QTMovie, m_MovieFirstFrameTime );
  439. AssertExitF( GetMoviesError() == noErr );
  440. // Assure fall through to render a new frame
  441. m_NextInterestingTimeToPlay = m_MovieFirstFrameTime;
  442. }
  443. // Are we on the last frame of the movie? (but not past the end of any audio?)
  444. if ( m_NextInterestingTimeToPlay == NO_MORE_INTERESTING_TIMES )
  445. {
  446. return true; // reuse last frame
  447. }
  448. // Enough time passed to get to next frame?
  449. if ( curMovieTime < m_NextInterestingTimeToPlay )
  450. {
  451. // nope.. use the previous frame
  452. return true;
  453. }
  454. // move the movie along
  455. UpdateMovie( m_QTMovie );
  456. AssertExitF( GetMoviesError() == noErr );
  457. // Let QuickTime render the frame
  458. MoviesTask( m_QTMovie, 0L );
  459. AssertExitF( GetMoviesError() == noErr );
  460. // Get the next frame after the current time (the movie may have advanced a bit during UpdateMovie() and MovieTasks()
  461. GetMovieNextInterestingTime( m_QTMovie, nextTimeStep | nextTimeEdgeOK, 1, qTypes, GetMovieTime( m_QTMovie, nullptr ), fixed1, &m_NextInterestingTimeToPlay, nullptr );
  462. // hit the end of the movie?
  463. if ( GetMoviesError() == invalidTime || m_NextInterestingTimeToPlay == END_OF_QUICKTIME_MOVIE )
  464. {
  465. m_NextInterestingTimeToPlay = NO_MORE_INTERESTING_TIMES;
  466. }
  467. // Regenerate our texture, it'll grab from the GWorld Directly
  468. m_Texture->Download();
  469. SetResult( VideoResult::SUCCESS );
  470. return true;
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Returns the material
  474. //-----------------------------------------------------------------------------
  475. IMaterial *CQuickTimeMaterial::GetMaterial()
  476. {
  477. return m_Material;
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Returns the texcoord range
  481. //-----------------------------------------------------------------------------
  482. void CQuickTimeMaterial::GetVideoTexCoordRange( float *pMaxU, float *pMaxV )
  483. {
  484. AssertExit( pMaxU != nullptr && pMaxV != nullptr );
  485. if ( m_Texture == nullptr ) // no texture?
  486. {
  487. *pMaxU = *pMaxV = 1.0f;
  488. return;
  489. }
  490. *pMaxU = m_TexCordU;
  491. *pMaxV = m_TexCordV;
  492. }
  493. //-----------------------------------------------------------------------------
  494. // Returns the frame size of the QuickTime Video in pixels
  495. //-----------------------------------------------------------------------------
  496. void CQuickTimeMaterial::GetVideoImageSize( int *pWidth, int *pHeight )
  497. {
  498. Assert( pWidth != nullptr && pHeight != nullptr );
  499. *pWidth = m_VideoFrameWidth;
  500. *pHeight = m_VideoFrameHeight;
  501. }
  502. float CQuickTimeMaterial::GetVideoDuration()
  503. {
  504. return m_QTMovieDurationinSec;
  505. }
  506. int CQuickTimeMaterial::GetFrameCount()
  507. {
  508. return m_QTMovieFrameCount;
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Sets the frame for an QuickTime Material (use instead of SetTime)
  512. //-----------------------------------------------------------------------------
  513. bool CQuickTimeMaterial::SetFrame( int FrameNum )
  514. {
  515. if ( !m_bMoviePlaying )
  516. {
  517. Assert( false );
  518. SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE );
  519. return false;
  520. }
  521. float theTime = (float) FrameNum * m_QTMovieFrameRate.GetFPS();
  522. return SetTime( theTime );
  523. }
  524. int CQuickTimeMaterial::GetCurrentFrame()
  525. {
  526. AssertExitV( m_bMoviePlaying, -1 );
  527. TimeValue curTime = m_bMoviePaused ? m_MoviePauseTime : GetMovieTime( m_QTMovie, nullptr );
  528. return curTime / m_QTMovieFrameRate.GetUnitsPerFrame();
  529. }
  530. float CQuickTimeMaterial::GetCurrentVideoTime()
  531. {
  532. AssertExitV( m_bMoviePlaying, -1.0f );
  533. TimeValue curTime = m_bMoviePaused ? m_MoviePauseTime : GetMovieTime( m_QTMovie, nullptr );
  534. return curTime / m_QTMovieFrameRate.GetUnitsPerSecond();
  535. }
  536. bool CQuickTimeMaterial::SetTime( float flTime )
  537. {
  538. AssertExitF( m_bMoviePlaying );
  539. AssertExitF( flTime >= 0 && flTime < m_QTMovieDurationinSec );
  540. TimeValue newTime = (TimeValue) ( flTime * m_QTMovieFrameRate.GetUnitsPerSecond() + 0.5f) ;
  541. clamp( newTime, m_MovieFirstFrameTime, m_QTMovieDuration );
  542. // Are we paused?
  543. if ( m_bMoviePaused )
  544. {
  545. m_MoviePauseTime = newTime;
  546. return true;
  547. }
  548. TimeValue curMovieTime = GetMovieTime( m_QTMovie, nullptr );
  549. // Don't stop and reset movie if we are within 1 frame of the requested time
  550. if ( newTime <= curMovieTime - m_QTMovieFrameRate.GetUnitsPerFrame() || newTime >= curMovieTime + m_QTMovieFrameRate.GetUnitsPerFrame() )
  551. {
  552. // Reset the movie to the requested time
  553. StopMovie( m_QTMovie );
  554. SetMovieTimeValue( m_QTMovie, newTime );
  555. StartMovie( m_QTMovie );
  556. Assert( GetMoviesError() == noErr );
  557. }
  558. return true;
  559. }
  560. //-----------------------------------------------------------------------------
  561. // Initializes, shuts down the procedural texture
  562. //-----------------------------------------------------------------------------
  563. void CQuickTimeMaterial::CreateProceduralTexture( const char *pTextureName )
  564. {
  565. AssertIncRange( m_VideoFrameWidth, cMinVideoFrameWidth, cMaxVideoFrameWidth );
  566. AssertIncRange( m_VideoFrameHeight, cMinVideoFrameHeight, cMaxVideoFrameHeight );
  567. AssertStr( pTextureName );
  568. // Either make the texture the same dimensions as the video,
  569. // or choose power-of-two textures which are at least as big as the video
  570. bool actualSizeTexture = BITFLAGS_SET( m_PlaybackFlags, VideoPlaybackFlags::TEXTURES_ACTUAL_SIZE );
  571. int nWidth = ( actualSizeTexture ) ? ALIGN_VALUE( m_VideoFrameWidth, TEXTURE_SIZE_ALIGNMENT ) : ComputeGreaterPowerOfTwo( m_VideoFrameWidth );
  572. int nHeight = ( actualSizeTexture ) ? ALIGN_VALUE( m_VideoFrameHeight, TEXTURE_SIZE_ALIGNMENT ) : ComputeGreaterPowerOfTwo( m_VideoFrameHeight );
  573. // initialize the procedural texture as 32-it RGBA, w/o mipmaps
  574. m_Texture.InitProceduralTexture( pTextureName, "VideoCacheTextures", nWidth, nHeight,
  575. IMAGE_FORMAT_BGRA8888, TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT | TEXTUREFLAGS_NOMIP |
  576. TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_NOLOD );
  577. // Use this to get the updated frame from the remote connection
  578. m_Texture->SetTextureRegenerator( &m_TextureRegen /* , false */ );
  579. // compute the texcoords
  580. int nTextureWidth = m_Texture->GetActualWidth();
  581. int nTextureHeight = m_Texture->GetActualHeight();
  582. m_TexCordU = ( nTextureWidth > 0 ) ? (float) m_VideoFrameWidth / (float) nTextureWidth : 0.0f;
  583. m_TexCordV = ( nTextureHeight > 0 ) ? (float) m_VideoFrameHeight / (float) nTextureHeight : 0.0f;
  584. }
  585. void CQuickTimeMaterial::DestroyProceduralTexture()
  586. {
  587. if ( m_Texture != nullptr )
  588. {
  589. // DO NOT Call release on the Texture Regenerator, as it will destroy this object! bad bad bad
  590. // instead we tell it to assign a NULL regenerator and flag it to not call release
  591. m_Texture->SetTextureRegenerator( nullptr /*, false */ );
  592. // Texture, texture go away...
  593. m_Texture.Shutdown( true );
  594. }
  595. }
  596. //-----------------------------------------------------------------------------
  597. // Initializes, shuts down the procedural material
  598. //-----------------------------------------------------------------------------
  599. void CQuickTimeMaterial::CreateProceduralMaterial( const char *pMaterialName )
  600. {
  601. // create keyvalues if necessary
  602. KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
  603. {
  604. pVMTKeyValues->SetString( "$basetexture", m_Texture->GetName() );
  605. pVMTKeyValues->SetInt( "$nobasetexture", 1 );
  606. pVMTKeyValues->SetInt( "$nofog", 1 );
  607. pVMTKeyValues->SetInt( "$spriteorientation", 3 );
  608. pVMTKeyValues->SetInt( "$translucent", 1 );
  609. pVMTKeyValues->SetInt( "$nolod", 1 );
  610. pVMTKeyValues->SetInt( "$nomip", 1 );
  611. pVMTKeyValues->SetInt( "$gammacolorread", 0 );
  612. }
  613. // FIXME: gak, this is backwards. Why doesn't the material just see that it has a funky basetexture?
  614. m_Material.Init( pMaterialName, pVMTKeyValues );
  615. m_Material->Refresh();
  616. }
  617. void CQuickTimeMaterial::DestroyProceduralMaterial()
  618. {
  619. // Store the internal material pointer for later use
  620. IMaterial *pMaterial = m_Material;
  621. m_Material.Shutdown();
  622. materials->UncacheUnusedMaterials();
  623. // Now be sure to free that material because we don't want to reference it again later, we'll recreate it!
  624. if ( pMaterial != nullptr )
  625. {
  626. pMaterial->DeleteIfUnreferenced();
  627. }
  628. }
  629. //-----------------------------------------------------------------------------
  630. // Opens a movie file using quicktime
  631. //-----------------------------------------------------------------------------
  632. void CQuickTimeMaterial::OpenQTMovie( const char *theQTMovieFileName )
  633. {
  634. AssertExit( IS_NOT_EMPTY( theQTMovieFileName ) );
  635. // Set graphics port
  636. #if defined ( WIN32 )
  637. SetGWorld ( (CGrafPtr) GetNativeWindowPort( nil ), nil );
  638. #elif defined ( OSX )
  639. SetGWorld( nil, nil );
  640. #endif
  641. SetQTFileName( theQTMovieFileName );
  642. Handle MovieFileDataRef = nullptr;
  643. OSType MovieFileDataRefType = 0;
  644. CFStringRef imageStrRef = CFStringCreateWithCString ( NULL, theQTMovieFileName, 0 );
  645. AssertExitFunc( imageStrRef != nullptr, SetResult( VideoResult::SYSTEM_ERROR_OCCURED ) );
  646. OSErr status = QTNewDataReferenceFromFullPathCFString( imageStrRef, (QTPathStyle) kQTNativeDefaultPathStyle, 0, &MovieFileDataRef, &MovieFileDataRefType );
  647. AssertExitFunc( status == noErr, SetResult( VideoResult::FILE_ERROR_OCCURED ) );
  648. CFRelease( imageStrRef );
  649. status = NewMovieFromDataRef( &m_QTMovie, newMovieActive, nil, MovieFileDataRef, MovieFileDataRefType );
  650. SAFE_DISPOSE_HANDLE( MovieFileDataRef );
  651. if ( status != noErr )
  652. {
  653. Assert( false );
  654. Reset();
  655. SetResult( VideoResult::VIDEO_ERROR_OCCURED );
  656. return;
  657. }
  658. // disabling audio?
  659. if ( BITFLAGS_SET( m_PlaybackFlags, VideoPlaybackFlags::NO_AUDIO ) )
  660. {
  661. m_bHasAudio = false;
  662. }
  663. else
  664. {
  665. // does movie have audio?
  666. Track audioTrack = GetMovieIndTrackType( m_QTMovie, 1, SoundMediaType, movieTrackMediaType );
  667. m_bHasAudio = ( audioTrack != nullptr );
  668. }
  669. // Now we need to extract the time info from the QT Movie
  670. m_QTMovieTimeScale = GetMovieTimeScale( m_QTMovie );
  671. m_QTMovieDuration = GetMovieDuration( m_QTMovie );
  672. // compute movie duration
  673. m_QTMovieDurationinSec = float ( double( m_QTMovieDuration ) / double( m_QTMovieTimeScale ) );
  674. if ( !MovieGetStaticFrameRate( m_QTMovie, m_QTMovieFrameRate ) )
  675. {
  676. WarningAssert( "Couldn't Get Frame Rate" );
  677. }
  678. // and get an estimated frame count
  679. m_QTMovieFrameCount = m_QTMovieDuration / m_QTMovieTimeScale;
  680. if ( m_QTMovieFrameRate.GetUnitsPerSecond() == m_QTMovieTimeScale )
  681. {
  682. m_QTMovieFrameCount = m_QTMovieDuration / m_QTMovieFrameRate.GetUnitsPerFrame();
  683. }
  684. else
  685. {
  686. m_QTMovieFrameCount = (int) ( (float) m_QTMovieDurationinSec * m_QTMovieFrameRate.GetFPS() + 0.5f );
  687. }
  688. // what size do we set the output rect to?
  689. GetMovieNaturalBoundsRect(m_QTMovie, &m_QTMovieRect);
  690. m_VideoFrameWidth = m_QTMovieRect.right;
  691. m_VideoFrameHeight = m_QTMovieRect.bottom;
  692. // Sanity check...
  693. AssertExitFunc( m_QTMovieRect.top == 0 && m_QTMovieRect.left == 0 &&
  694. m_QTMovieRect.right >= cMinVideoFrameWidth && m_QTMovieRect.right <= cMaxVideoFrameWidth &&
  695. m_QTMovieRect.bottom >= cMinVideoFrameHeight && m_QTMovieRect.bottom <= cMaxVideoFrameHeight &&
  696. m_QTMovieRect.right % 4 == 0,
  697. SetResult( VideoResult::VIDEO_ERROR_OCCURED ) );
  698. // Setup the QuiuckTime Graphics World for the Movie
  699. status = QTNewGWorld( &m_MovieGWorld, k32BGRAPixelFormat, &m_QTMovieRect, nil, nil, 0 );
  700. AssertExit( status == noErr );
  701. // Setup the playback gamma according to the convar
  702. SetGWorldDecodeGamma( m_MovieGWorld, VideoPlaybackGamma::USE_GAMMA_CONVAR );
  703. // Assign the GWorld to this movie
  704. SetMovieGWorld( m_QTMovie, m_MovieGWorld, nil );
  705. // Setup Movie Audio, unless suppressed
  706. if ( !CreateMovieAudioContext( m_bHasAudio, m_QTMovie, &m_AudioContext, true, &m_CurrentVolume ) )
  707. {
  708. SetResult( VideoResult::AUDIO_ERROR_OCCURED );
  709. WarningAssert( "Couldn't Set Audio" );
  710. }
  711. // Get the time of the first frame
  712. OSType qTypes[1] = { VisualMediaCharacteristic };
  713. short qFlags = nextTimeStep | nextTimeEdgeOK; // use nextTimeStep instead of nextTimeMediaSample for MPEG 1-2 compatibility
  714. GetMovieNextInterestingTime( m_QTMovie, qFlags, 1, qTypes, (TimeValue) 0, fixed1, &m_MovieFirstFrameTime, NULL );
  715. AssertExitFunc( GetMoviesError() == noErr, SetResult( VideoResult::VIDEO_ERROR_OCCURED ) );
  716. // Preroll the movie
  717. if ( BITFLAGS_SET( m_PlaybackFlags, VideoPlaybackFlags::PRELOAD_VIDEO ) )
  718. {
  719. Fixed playRate = GetMoviePreferredRate( m_QTMovie );
  720. status = PrerollMovie( m_QTMovie, m_MovieFirstFrameTime, playRate );
  721. AssertExitFunc( status == noErr, SetResult( VideoResult::VIDEO_ERROR_OCCURED ) );
  722. }
  723. m_bMovieInitialized = true;
  724. }
  725. void CQuickTimeMaterial::CloseQTFile()
  726. {
  727. if ( m_QTMovie == nullptr )
  728. {
  729. return;
  730. }
  731. SAFE_RELEASE_AUDIOCONTEXT( m_AudioContext );
  732. SAFE_DISPOSE_GWORLD( m_MovieGWorld );
  733. SAFE_DISPOSE_MOVIE( m_QTMovie );
  734. SetQTFileName( nullptr );
  735. }