//====== Copyright 2010, Valve Corporation, All rights reserved. ============== // // Purpose: // //============================================================================= #include "filesystem.h" #include "tier1/strtools.h" #include "tier1/utllinkedlist.h" #include "tier1/keyvalues.h" #include "materialsystem/imaterial.h" #include "materialsystem/imaterialsystem.h" #include "materialsystem/materialsystemutil.h" #include "materialsystem/itexture.h" #include "vtf/vtf.h" #include "pixelwriter.h" #include "tier3/tier3.h" #include "platform.h" #include "avi/iquicktime.h" #include "quicktime.h" #if defined ( WIN32 ) #if defined ( QUICKTIME_VIDEO ) #include #include <../dx9sdk/include/dsound.h> #endif #endif #include "tier0/memdbgon.h" #define ZeroVar( var ) V_memset( &var, 0, sizeof( var) ) #define SAFE_DELETE( var ) if ( var != NULL ) { delete var; var = NULL; } #define SAFE_DELETE_ARRAY( var ) if ( var != NULL ) { delete[] var; var = NULL; } #ifdef DBGFLAG_ASSERT #define AssertExit( _exp ) Assert( _exp ) #define AssertExitF( _exp ) Assert( _exp ) #else #define AssertExit( _exp ) if ( !( _exp ) ) return; #define AssertExitF( _exp ) if ( !( _exp ) ) return false; #endif //----------------------------------------------------------------------------- // Singleton //----------------------------------------------------------------------------- static CQuickTime g_QUICKTIME; EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CQuickTime, IQuickTime, QUICKTIME_INTERFACE_VERSION, g_QUICKTIME ); //----------------------------------------------------------------------------- // Inherited from ITextureRegenerator //----------------------------------------------------------------------------- void CQuicktimeMaterialRGBTextureRegenerator::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ) { AssertExit( pVTFTexture != NULL ); // Error condition if ( ( pVTFTexture->FrameCount() > 1 ) || ( pVTFTexture->FaceCount() > 1 ) || ( pVTFTexture->MipCount() > 1 ) || ( pVTFTexture->Depth() > 1 ) ) { memset( pVTFTexture->ImageData(), 0xAA, pVTFTexture->ComputeTotalSize() ); return; } // do we not have a video to install, or a video buffer that is too big? if ( m_pQTMaterial->m_BitMapData == NULL /* || m_VideoFrameBufferSize < pVTFTexture->ComputeMipSize( 0 ) */ ) { memset( pVTFTexture->ImageData(), 0xCC, pVTFTexture->ComputeTotalSize() ); return; } // Need to verify we have compatible formats Assert( pVTFTexture->Format() == IMAGE_FORMAT_BGRA8888 ); Assert( pVTFTexture->RowSizeInBytes( 0 ) >= pVTFTexture->Width() * 4 ); Assert( pVTFTexture->Width() >= m_nSourceWidth ); Assert( pVTFTexture->Height() >= m_nSourceHeight ); // simplest of image copies, one line at a time BYTE *ImageData = pVTFTexture->ImageData(); BYTE *SrcData = (BYTE*) m_pQTMaterial->m_BitMapData; int dstStride = pVTFTexture->RowSizeInBytes( 0 ); int srcStride = m_nSourceWidth * 4; int rowSize = m_nSourceWidth * 4; // copy the rows of data for ( int y = 0; y < m_nSourceHeight; y++ ) { memcpy( ImageData, SrcData, rowSize); ImageData+= dstStride; SrcData+= srcStride; } } void CQuicktimeMaterialRGBTextureRegenerator::Release() { // we don't invoke the destructor here, we're not using the no-release extensions } //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- CQuickTimeMaterial::CQuickTimeMaterial() : m_pFileName( NULL ) #if defined ( QUICKTIME_VIDEO ) ,m_MovieGWorld( NULL ), m_QTMovie( NULL ), m_AudioContext( NULL ), m_BitMapData( NULL ) #endif { Reset(); } //----------------------------------------------------------------------------- // Destructor //----------------------------------------------------------------------------- CQuickTimeMaterial::~CQuickTimeMaterial() { Reset(); } void CQuickTimeMaterial::SetQTFileName( const char* theQTMovieFileName ) { SAFE_DELETE_ARRAY( m_pFileName ); if ( theQTMovieFileName != NULL ) { int sLen = V_strlen( theQTMovieFileName ); AssertMsg( sLen > 0 && sLen <= cMaxQTFileNameLen, "Bad Movie FileName" ); m_pFileName = new char[ sLen + 1 ]; V_strcpy( m_pFileName, theQTMovieFileName ); } } void CQuickTimeMaterial::Reset() { SetQTFileName( NULL ); ZeroVar( m_TextureName ); ZeroVar( m_MaterialName ); DestroyProceduralTexture(); DestroyProceduralMaterial(); SAFE_DELETE_ARRAY( m_BitMapData ); m_TexCordU = 0.0f; m_TexCordV = 0.0f; m_bActive = false; m_bLoopMovie = false; m_bMoviePlaying = false; m_MovieBeganPlayingTime = 0.0; m_MovieCurrentTime = 0.0; #if defined ( QUICKTIME_VIDEO ) if ( m_AudioContext != NULL ) { QTAudioContextRelease( m_AudioContext ); m_AudioContext = NULL; } if ( m_MovieGWorld != NULL ) { DisposeGWorld( m_MovieGWorld ); m_MovieGWorld = NULL; } if ( m_QTMovie != NULL ) { DisposeMovie( m_QTMovie ); m_QTMovie = NULL; } #endif } //----------------------------------------------------------------------------- // Initializes the material //----------------------------------------------------------------------------- bool CQuickTimeMaterial::Init( const char *pMaterialName, const char *pFileName, const char *pPathID ) { // Determine the full path name of the video file char pQTFileName[ MAX_PATH ]; char pFullQTFileName[ MAX_PATH ]; Q_snprintf( pQTFileName, sizeof( pQTFileName ), "%s", pFileName ); V_SetExtension( pQTFileName, ".mov", sizeof( pQTFileName ) ); if ( !g_pFullFileSystem->RelativePathToFullPath( pQTFileName, pPathID, pFullQTFileName, sizeof( pFullQTFileName ) ) ) { // A file by that name was not found Assert( 0 ); return false; } OpenQTMovie( pFullQTFileName ); if ( !m_bActive ) { // The file was unable to be opened Assert( 0 ); return false; } // Now we can properly setup out regenerators m_TextureRegen.SetParentMaterial( this, m_VideoFrameWidth, m_VideoFrameHeight); CreateProceduralTexture( pMaterialName ); CreateProceduralMaterial( pMaterialName ); return true; } void CQuickTimeMaterial::Shutdown( void ) { CloseQTFile(); DestroyProceduralMaterial(); DestroyProceduralTexture(); Reset(); } //----------------------------------------------------------------------------- // Purpose: Updates our scene // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CQuickTimeMaterial::Update( void ) { Assert( m_bActive ); #if defined ( QUICKTIME_VIDEO ) OSType qTypes[1] = { VisualMediaCharacteristic }; // is this our first frame? if ( !m_bMoviePlaying ) { TimeValue startTime = -1; short qFlags = nextTimeMediaSample + nextTimeEdgeOK; GetMovieNextInterestingTime( m_QTMovie, qFlags, 1, qTypes, (TimeValue) 0, fixed1, &startTime, NULL); Assert( GetMoviesError() == noErr ); GetMovieNextInterestingTime( m_QTMovie, nextTimeMediaSample, 1, qTypes, startTime, fixed1, &m_NextInterestingTimeToPlay, NULL); Assert( GetMoviesError() == noErr ); SetMovieTimeValue( m_QTMovie, startTime ); Assert( GetMoviesError() == noErr ); m_LastInterestingTimePlayed = startTime; m_MovieBeganPlayingTime = Plat_FloatTime(); m_bMoviePlaying = true; } else // we've drawn at least one frame before { // Get Current Time.. are we done playing the movie? double rightNow = Plat_FloatTime(); m_MovieCurrentTime = rightNow - m_MovieBeganPlayingTime; // did we hit the end of the movie? if ( m_MovieCurrentTime >= m_QTMovieDurationinSec || m_NextInterestingTimeToPlay == -2 ) { // If we're not looping, then report that we are done updating if ( m_bLoopMovie == false ) { return false; } // ok we're looping the movie, so.... // wrap around the current time while ( m_MovieCurrentTime >= m_QTMovieDurationinSec ) { m_MovieBeganPlayingTime+= m_QTMovieDurationinSec; m_MovieCurrentTime = rightNow - m_MovieBeganPlayingTime; } // the next frame is set the frame 0, so it should trigger wrapping to the beginning long currentMovieTime = ( long ) ( m_MovieCurrentTime * m_QTMovieTimeScale ); m_NextInterestingTimeToPlay = 0; // Reset the movie to the wrapped around time (probably should compute starttime instead of assuming 0) SetMovieTimeValue( m_QTMovie, currentMovieTime ); Assert( GetMoviesError() == noErr ); } // where are we in terms of QT media units? long currentMovieTime = ( long ) ( m_MovieCurrentTime * m_QTMovieTimeScale ); // Enough time passed to get to next frame if ( currentMovieTime < m_NextInterestingTimeToPlay ) { // nope.. use the previous frame return true; } TimeValue nextTimeAfter = -1; // do we need to skip any frames? while ( true ) { // look at the sample time after the one we past GetMovieNextInterestingTime( m_QTMovie, nextTimeMediaSample, 1, qTypes, m_NextInterestingTimeToPlay, fixed1, &nextTimeAfter, NULL); OSErr lastErr = GetMoviesError(); // hit the end of the movie? if ( lastErr == invalidTime ) { nextTimeAfter = -2; break; } Assert( lastErr == noErr ); // is there a later frame we should be showing? if ( nextTimeAfter <= currentMovieTime) { m_NextInterestingTimeToPlay = nextTimeAfter; nextTimeAfter = -1; } else { break; } } // SetMovieTimeValue( m_QTMovie, m_NextInterestingTimeToPlay ); Assert( GetMoviesError() == noErr ); m_LastInterestingTimePlayed = m_NextInterestingTimeToPlay; m_NextInterestingTimeToPlay = nextTimeAfter; } // move the movie along UpdateMovie( m_QTMovie ); Assert( GetMoviesError() == noErr ); MoviesTask( m_QTMovie, 10L ); Assert( GetMoviesError() == noErr ); #if defined (WIN32) HDC theHDC = (HDC) GetPortHDC( (GrafPtr) m_MovieGWorld ); HBITMAP theHBITMAP = (HBITMAP) GetPortHBITMAP( (GrafPtr) m_MovieGWorld ); // create the bitmapinfo header information BITMAP bmp; if ( !GetObject( theHBITMAP, sizeof(BITMAP), (LPSTR)&bmp) ) { Assert( false ); return false; } Assert( bmp.bmWidth == m_QTMovieRect.right ); Assert( bmp.bmBitsPixel == 32 ); BITMAPINFO tempInfo; V_memcpy( &tempInfo, &m_BitmapInfo, sizeof( tempInfo ) ); // Retrieve the pixel bits (no color table) if ( !GetDIBits( theHDC, theHBITMAP, 0, (WORD) bmp.bmHeight, m_BitMapData, &tempInfo, DIB_RGB_COLORS)) { AssertMsg( false, "writeBMP::GetDIB error" ); return false; } #elif defined ( OSX ) PixMapHandle thePixMap = GetGWorldPixMap( m_MovieGWorld ); if ( LockPixels( thePixMap ) ) { void *pPixels = GetPixBaseAddr( thePixMap ); long rowStride = GetPixRowBytes( thePixMap ); int rowBytes = m_VideoFrameWidth * 4; for (int y = 0; y < m_VideoFrameHeight; y++ ) { BYTE *src = (BYTE*) pPixels + ( y * rowStride ); BYTE *dst = (BYTE*) m_BitMapData + ( y * rowBytes ); memcpy( dst, src, rowBytes ); } UnlockPixels( thePixMap ); } #endif // Regenerate our texture m_Texture->Download(); #endif return true; } //----------------------------------------------------------------------------- // Checks to see if the video has a new frame ready to download into the // texture //----------------------------------------------------------------------------- bool CQuickTimeMaterial::ReadyForSwap( void ) { AssertExitF( m_bActive ); // Waiting to play the first frame? Hell yes we are ready if ( !m_bMoviePlaying ) return true; // Get Current Time.. are we done playing the movie? double CurrentTime = Plat_FloatTime() - m_MovieBeganPlayingTime; // did we hit the end of the movie? if ( CurrentTime >= m_QTMovieDurationinSec || m_NextInterestingTimeToPlay == -2 ) { // if we are looping, we have another frame, otherwise no return m_bLoopMovie; } // where are we in terms of QT media units? long currentMovieTime = ( long ) ( CurrentTime * m_QTMovieTimeScale ); // Enough time passed to get to next frame?? if ( currentMovieTime < m_NextInterestingTimeToPlay ) { // nope.. use the previous frame return false; } // we have a new frame we want then.. return true; } //----------------------------------------------------------------------------- // Returns the material //----------------------------------------------------------------------------- IMaterial *CQuickTimeMaterial::GetMaterial() { return m_Material; } //----------------------------------------------------------------------------- // Returns the texcoord range //----------------------------------------------------------------------------- void CQuickTimeMaterial::GetTexCoordRange( float *pMaxU, float *pMaxV ) { // no texture? if ( m_Texture == NULL ) { *pMaxU = *pMaxV = 1.0f; return; } int nTextureWidth = m_Texture->GetActualWidth(); int nTextureHeight = m_Texture->GetActualHeight(); *pMaxU = (float) m_VideoFrameWidth / (float) nTextureWidth; *pMaxV = (float) m_VideoFrameHeight / (float) nTextureHeight; } //----------------------------------------------------------------------------- // Returns the frame size of the QuickTime Video (stored in a subrect of the material itself) //----------------------------------------------------------------------------- void CQuickTimeMaterial::GetFrameSize( int *pWidth, int *pHeight ) { *pWidth = m_VideoFrameWidth; *pHeight = m_VideoFrameHeight; } //----------------------------------------------------------------------------- // Computes a power of two at least as big as the passed-in number //----------------------------------------------------------------------------- static inline int ComputeGreaterPowerOfTwo( int n ) { int i = 1; while ( i < n ) { i <<= 1; } return i; } //----------------------------------------------------------------------------- // Returns the frame rate of the Quicktime Video //----------------------------------------------------------------------------- int CQuickTimeMaterial::GetFrameRate( ) { #if defined ( QUICKTIME_VIDEO ) return m_QTMoveFrameRate; #else return 1; #endif } int CQuickTimeMaterial::GetFrameCount( ) { #if defined ( QUICKTIME_VIDEO ) return (int) ( m_QTMovieDurationinSec * m_QTMoveFrameRate ); #else return 1; #endif } //----------------------------------------------------------------------------- // Sets the frame for an QuickTime Material (use instead of SetTime) //----------------------------------------------------------------------------- void CQuickTimeMaterial::SetFrame( float flFrame ) { flFrame; AssertMsg( false, "method not implemented " ); } //----------------------------------------------------------------------------- // Sets the movie to loop continously instead of end, or not //----------------------------------------------------------------------------- void CQuickTimeMaterial::SetLooping( bool loop ) { m_bLoopMovie = loop; } //----------------------------------------------------------------------------- // Initializes, shuts down the procedural texture //----------------------------------------------------------------------------- void CQuickTimeMaterial::CreateProceduralTexture( const char *pTextureName ) { Assert( m_VideoFrameWidth >= cMinVideoFrameWidth && m_VideoFrameHeight >= cMinVideoFrameHeight && m_VideoFrameWidth <= cMaxVideoFrameWidth && m_VideoFrameHeight <= cMaxVideoFrameHeight); Assert( pTextureName ); // Choose power-of-two textures which are at least as big as the AVI int nWidth = ComputeGreaterPowerOfTwo( m_VideoFrameWidth ); int nHeight = ComputeGreaterPowerOfTwo( m_VideoFrameHeight ); // initialize the procedural texture as 32-it RGBA, w/o mipmaps m_Texture.InitProceduralTexture( pTextureName, "VideoCacheTextures", nWidth, nHeight, IMAGE_FORMAT_BGRA8888, TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_NOLOD ); // Use this to get the updated frame from the remote connection m_Texture->SetTextureRegenerator( &m_TextureRegen /* , false */ ); // compute the texcoords int nTextureWidth = m_Texture->GetActualWidth(); int nTextureHeight = m_Texture->GetActualHeight(); m_TexCordU = ( nTextureWidth > 0 ) ? (float) m_VideoFrameWidth / (float) nTextureWidth : 0.0f; m_TexCordV = ( nTextureHeight > 0 ) ? (float) m_VideoFrameHeight / (float) nTextureHeight : 0.0f; } void CQuickTimeMaterial::DestroyProceduralTexture() { if ( m_Texture != NULL ) { // DO NOT Call release on the Texture Regenerator, as it will destroy this object! bad bad bad // instead we tell it to assign a NULL regenerator and flag it to not call release m_Texture->SetTextureRegenerator( NULL /*, false */ ); // Texture, texture go away... m_Texture.Shutdown( true ); } } //----------------------------------------------------------------------------- // Initializes, shuts down the procedural material //----------------------------------------------------------------------------- void CQuickTimeMaterial::CreateProceduralMaterial( const char *pMaterialName ) { // create keyvalues if necessary KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); { pVMTKeyValues->SetString( "$basetexture", m_Texture->GetName() ); pVMTKeyValues->SetInt( "$nofog", 1 ); pVMTKeyValues->SetInt( "$spriteorientation", 3 ); pVMTKeyValues->SetInt( "$translucent", 1 ); pVMTKeyValues->SetInt( "$nolod", 1 ); pVMTKeyValues->SetInt( "$nomip", 1 ); pVMTKeyValues->SetInt( "$gammacolorread", 0 ); } // FIXME: gak, this is backwards. Why doesn't the material just see that it has a funky basetexture? m_Material.Init( pMaterialName, pVMTKeyValues ); m_Material->Refresh(); } void CQuickTimeMaterial::DestroyProceduralMaterial() { // Store the internal material pointer for later use IMaterial *pMaterial = m_Material; m_Material.Shutdown(); materials->UncacheUnusedMaterials(); // Now be sure to free that material because we don't want to reference it again later, we'll recreate it! if ( pMaterial != NULL ) { pMaterial->DeleteIfUnreferenced(); } } //----------------------------------------------------------------------------- // Opens a movie file using quicktime //----------------------------------------------------------------------------- void CQuickTimeMaterial::OpenQTMovie( const char* theQTMovieFileName ) { AssertExit( theQTMovieFileName != NULL ); #if defined ( QUICKTIME_VIDEO ) short theFile = 0; FSSpec sfFile; char fullPath[256]; OSErr status = 0; // Set graphics port #if defined ( WIN32 ) SetGWorld ( (CGrafPtr) GetNativeWindowPort( nil ), nil ); #elif defined ( OSX ) SetGWorld( nil, nil ); #endif SetQTFileName( theQTMovieFileName ); #if defined ( OSX ) FSRef dirRef; Boolean isDir; status = FSPathMakeRef( (UInt8 *)theQTMovieFileName, &dirRef, &isDir ); if ( status == noErr ) { status = FSGetCatalogInfo( &dirRef, kFSCatInfoNone, NULL, NULL, &sfFile, NULL ); Assert( status == noErr ); } if ( status != noErr ) { Reset(); return; } #elif defined ( WIN32 ) strcpy ( fullPath, theQTMovieFileName); // Copy full pathname c2pstr ( fullPath ); // Convert to Pascal string status = FSMakeFSSpec( 0, 0L, (const unsigned char *)&fullPath[0], &sfFile ); // Make file-system specification record AssertExit( status == noErr ); #endif status = OpenMovieFile( &sfFile, &theFile, fsRdPerm) ; // Open movie file Assert( status == noErr ); if ( status != noErr ) { CloseMovieFile( theFile ); Reset(); return; } status = NewMovieFromFile ( &m_QTMovie, theFile, nil, nil, newMovieActive, nil); // Get movie from file Assert( status == noErr ); if ( status != noErr ) { CloseMovieFile( theFile ); Reset(); return; } status = CloseMovieFile (theFile); // Close movie file AssertExit( status == noErr ); // Now we need to extract the time info from the QT Movie // Duration scale = 600 per second... m_QTMovieTimeScale = GetMovieTimeScale( m_QTMovie ); m_QTMovieDuration = GetMovieDuration( m_QTMovie ); m_QTMovieDurationinSec = float ( m_QTMovieDuration ) / float ( m_QTMovieTimeScale ); Fixed movieRate = GetMoviePreferredRate( m_QTMovie ); m_QTMoveFrameRate = Fix2Long( movieRate ); // what size do we set the output rect to? GetMovieNaturalBoundsRect(m_QTMovie, &m_QTMovieRect); m_VideoFrameWidth = m_QTMovieRect.right; m_VideoFrameHeight = m_QTMovieRect.bottom; // Sanity check... AssertExit( m_QTMovieRect.top == 0 && m_QTMovieRect.left == 0 && m_QTMovieRect.right >= 64 && m_QTMovieRect.right <= 1920 && m_QTMovieRect.bottom >= 48 && m_QTMovieRect.bottom <= 1200 && m_QTMovieRect.right % 4 == 0 ); // Setup a bitmap to store frames... // compute image buffer size m_BitMapDataSize = 4 * m_QTMovieRect.right * m_QTMovieRect.bottom; #if defined ( WIN32 ) // Initialize bitmap info ZeroVar( m_BitmapInfo ); m_BitmapInfo.bmiHeader.biSize = sizeof( m_BitmapInfo.bmiHeader ); m_BitmapInfo.bmiHeader.biWidth = (LONG) m_QTMovieRect.right; m_BitmapInfo.bmiHeader.biHeight = -1 * (LONG) m_QTMovieRect.bottom; m_BitmapInfo.bmiHeader.biPlanes = 1; m_BitmapInfo.bmiHeader.biBitCount = 32; m_BitmapInfo.bmiHeader.biCompression = 0; /* BI_RGB */ m_BitmapInfo.bmiHeader.biSizeImage = m_BitMapDataSize; // the rest of the fields should be 0 #endif // create buffer to hold a single frame m_BitMapData = new byte[ m_BitMapDataSize ]; // Setup the QuiuckTime Graphics World for the Movie status = QTNewGWorld( &m_MovieGWorld, k32BGRAPixelFormat, &m_QTMovieRect, nil, nil, 0 ); AssertExit( status == noErr ); // perform any needed gamma correction // kQTUsePlatformDefaultGammaLevel = 0, /* When decompressing into this PixMap, gamma-correct to the platform's standard gamma. */ // kQTUseSourceGammaLevel = -1L, /* When decompressing into this PixMap, don't perform gamma-correction. */ // kQTCCIR601VideoGammaLevel = 0x00023333 /* 2.2, standard television video gamma.*/ // Fixed cGamma1_8 = 0x0001CCCC; // Gamma 1.8 // Fixed cGamma2_5 = 0x00028000; // Gamma 2.5 // // On OSX it appears we need to set a gamma of 1.0 or 0x0001000 - the values are interpreted differently? #if defined ( OSX ) Fixed decodeGamma = 0x00012000; #elif defined ( WIN32 ) Fixed decodeGamma = 0x00023333; #endif // Get the pix map for the GWorld and adjust the gamma correction on it PixMapHandle thePixMap = GetGWorldPixMap( m_MovieGWorld ); OSErr Status = QTSetPixMapHandleGammaLevel( thePixMap, decodeGamma ); AssertExit( Status == noErr ); Status = QTSetPixMapHandleRequestedGammaLevel( thePixMap, decodeGamma ); AssertExit( Status == noErr ); SetMovieGWorld( m_QTMovie, m_MovieGWorld, nil ); #if defined ( WIN32 ) WCHAR strGUID[39]; int numBytes = StringFromGUID2( DSDEVID_DefaultPlayback, (LPOLESTR) strGUID, 39); // CLSID_DirectSound is not what you want here // create the audio context CFStringRef deviceNameStrRef = NULL; deviceNameStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault, (const UniChar*) strGUID, (CFIndex) (numBytes -1) ); OSStatus result = QTAudioContextCreateForAudioDevice( NULL, deviceNameStrRef, NULL, &m_AudioContext ); #elif defined ( OSX ) OSStatus result = QTAudioContextCreateForAudioDevice( NULL, NULL, NULL, &m_AudioContext ); #endif AssertExit( result == noErr ); // Set the audio context result = SetMovieAudioContext( m_QTMovie, m_AudioContext ); AssertExit( result == noErr ); // Set the volume ConVarRef volumeConVar( "volume" ); float sysVolume = 1.0f; if ( volumeConVar.IsValid() ) sysVolume = volumeConVar.GetFloat(); clamp( sysVolume, 0.0f, 1.0f); short movieVolume = (short) ( sysVolume * 256.0 ); SetMovieVolume( m_QTMovie, movieVolume ); // Start movie playback (get the sound rolling) StartMovie( m_QTMovie ); m_bActive = true; #if defined( WIN32 ) if ( deviceNameStrRef ) { CFRelease( deviceNameStrRef ); } #endif #endif } void CQuickTimeMaterial::CloseQTFile() { #if defined ( QUICKTIME_VIDEO ) StopMovie( m_QTMovie ); SAFE_DELETE_ARRAY( m_BitMapData ); if ( m_AudioContext != NULL ) { QTAudioContextRelease( m_AudioContext ); m_AudioContext = NULL; } if ( m_MovieGWorld != NULL ) { DisposeGWorld( m_MovieGWorld ); m_MovieGWorld = NULL; } if ( m_QTMovie ) { DisposeMovie( m_QTMovie ); m_QTMovie = NULL; } SetQTFileName( NULL ); #endif } //----------------------------------------------------------------------------- // Constructor/destructor //----------------------------------------------------------------------------- CQuickTime::CQuickTime() { m_bQTInitialized = false; } // ---------------------------------------------------------------------------- CQuickTime::~CQuickTime() { // Make sure we shut down quicktime ShutdownQuicktime(); } //----------------------------------------------------------------------------- // Connect/disconnect //----------------------------------------------------------------------------- bool CQuickTime::Connect( CreateInterfaceFn factory ) { ConnectTier1Libraries( &factory, 1 ); ConnectTier2Libraries( &factory, 1 ); if ( !( g_pFullFileSystem && materials ) ) { Msg( "Quicktime failed to connect to a required system\n" ); } return ( g_pFullFileSystem && materials ); } //----------------------------------------------------------------------------- // Connect/disconnect //----------------------------------------------------------------------------- void CQuickTime::Disconnect( void ) { // Make sure we shut down quicktime ShutdownQuicktime(); } //----------------------------------------------------------------------------- // Query Interface //----------------------------------------------------------------------------- void *CQuickTime::QueryInterface( const char *pInterfaceName ) { if ( Q_strncmp( pInterfaceName, QUICKTIME_INTERFACE_VERSION, Q_strlen(QUICKTIME_INTERFACE_VERSION) + 1) == 0 ) { return (IQuickTime*)this; } return NULL; } //----------------------------------------------------------------------------- // Init/shutdown //----------------------------------------------------------------------------- InitReturnVal_t CQuickTime::Init() { return SetupQuicktime() ? INIT_OK : INIT_FAILED; } //----------------------------------------------------------------------------- void CQuickTime::Shutdown() { ShutdownQuicktime(); } //----------------------------------------------------------------------------- // Create/destroy an QuickTime material //----------------------------------------------------------------------------- QUICKTIMEMaterial_t CQuickTime::CreateMaterial( const char *pMaterialName, const char *pFileName, const char *pPathID, int flags ) { if ( ! m_bQTInitialized ) { return QUICKTIMEMATERIAL_INVALID; } QUICKTIMEMaterial_t h = m_QTMaterials.AddToTail(); m_QTMaterials[h] = new CQuickTimeMaterial; if ( m_QTMaterials[h]->Init( pMaterialName, pFileName, pPathID ) == false ) { delete m_QTMaterials[h]; m_QTMaterials.Remove( h ); return QUICKTIMEMATERIAL_INVALID; } m_QTMaterials[h]->SetLooping( ( flags & QUICKTIME_LOOP_MOVIE ) == QUICKTIME_LOOP_MOVIE ); return h; } // ---------------------------------------------------------------------------- void CQuickTime::DestroyMaterial( QUICKTIMEMaterial_t h ) { if ( h != QUICKTIMEMATERIAL_INVALID ) { m_QTMaterials[h]->Shutdown(); delete m_QTMaterials[h]; m_QTMaterials.Remove( h ); } } //----------------------------------------------------------------------------- // Update the QuickTime Video Material // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CQuickTime::Update( QUICKTIMEMaterial_t hMaterial ) { return ( hMaterial == QUICKTIMEMATERIAL_INVALID ) ? false : m_QTMaterials[hMaterial]->Update(); } //----------------------------------------------------------------------------- // Determine if a new frame of the movie is ready for display //----------------------------------------------------------------------------- bool CQuickTime::ReadyForSwap( QUICKTIMEMaterial_t hMaterial ) { return ( hMaterial == QUICKTIMEMATERIAL_INVALID ) ? false : m_QTMaterials[hMaterial]->ReadyForSwap(); } //----------------------------------------------------------------------------- // Gets the IMaterial associated with an QuickTime video material //----------------------------------------------------------------------------- IMaterial *CQuickTime::GetMaterial( QUICKTIMEMaterial_t h ) { return ( h != QUICKTIMEMATERIAL_INVALID ) ? m_QTMaterials[h]->GetMaterial() : NULL; } //----------------------------------------------------------------------------- // Returns the max texture coordinate of the QuickTime Video on the texture //----------------------------------------------------------------------------- void CQuickTime::GetTexCoordRange( QUICKTIMEMaterial_t h, float *pMaxU, float *pMaxV ) { if ( h != QUICKTIMEMATERIAL_INVALID ) { m_QTMaterials[h]->GetTexCoordRange( pMaxU, pMaxV ); } else { *pMaxU = *pMaxV = 1.0f; } } //----------------------------------------------------------------------------- // Returns the frame size of the Quicktime Video (is a subrect of the material itself) //----------------------------------------------------------------------------- void CQuickTime::GetFrameSize( QUICKTIMEMaterial_t h, int *pWidth, int *pHeight ) { if ( h != QUICKTIMEMATERIAL_INVALID ) { m_QTMaterials[h]->GetFrameSize( pWidth, pHeight ); } else { *pWidth = *pHeight = 1; } } //----------------------------------------------------------------------------- // Returns the frame size of the QuickTime Video (is a subrect of the material itself) //----------------------------------------------------------------------------- int CQuickTime::GetFrameRate( QUICKTIMEMaterial_t h ) { return ( h == QUICKTIMEMATERIAL_INVALID ) ? -1 : m_QTMaterials[h]->GetFrameRate(); } //----------------------------------------------------------------------------- // Sets the frame for an Quicktime Video material (use instead of SetTime) //----------------------------------------------------------------------------- void CQuickTime::SetFrame( QUICKTIMEMaterial_t h, float flFrame ) { if ( h != QUICKTIMEMATERIAL_INVALID ) { m_QTMaterials[h]->SetFrame( flFrame ); } } //----------------------------------------------------------------------------- // Returns the frame rate of the Quicktime Video //----------------------------------------------------------------------------- int CQuickTime::GetFrameCount( QUICKTIMEMaterial_t h ) { return ( h == QUICKTIMEMATERIAL_INVALID ) ? -1 : m_QTMaterials[h]->GetFrameCount(); } //----------------------------------------------------------------------------- // Hooks up the houtput sound device //----------------------------------------------------------------------------- bool CQuickTime::SetSoundDevice( void *pDevice ) { pDevice; return true; } // ---------------------------------------------------------------------------- // functions to initialize and shut down QuickTime services // ---------------------------------------------------------------------------- bool CQuickTime::SetupQuicktime() { m_bQTInitialized = false; #if defined ( QUICKTIME_VIDEO ) #if defined ( WIN32 ) OSErr status = InitializeQTML( 0 ); // if -2903 then quicktime not installed on this system if ( status != noErr ) { if ( status == qtmlDllLoadErr ) { //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" ); Assert( 0 ); } return false; } // Make sure we have version 7.04 or greater of quicktime long version = 0; status = Gestalt(gestaltQuickTime, &version); if ( (status != noErr) || ( version < 0x07048000) ) { TerminateQTML(); return false; } #endif OSErr status2 = EnterMovies(); // Initialize QuickTime Movie Toolbox if ( status2 != noErr ) { Assert( 0 ); #if defined ( WIN32 ) TerminateQTML(); #endif return false; } m_bQTInitialized = true; #endif return m_bQTInitialized; } // ---------------------------------------------------------------------------- void CQuickTime::ShutdownQuicktime() { if ( m_bQTInitialized ) { #if defined ( QUICKTIME_VIDEO ) ExitMovies(); // Terminate QuickTime #if defined ( WIN32 ) TerminateQTML(); // Terminate QTML #endif #endif m_bQTInitialized = false; } }