From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- video/webm_material.cpp | 962 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 962 insertions(+) create mode 100644 video/webm_material.cpp (limited to 'video/webm_material.cpp') diff --git a/video/webm_material.cpp b/video/webm_material.cpp new file mode 100644 index 0000000..75d39bf --- /dev/null +++ b/video/webm_material.cpp @@ -0,0 +1,962 @@ +//========= Copyright 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 "quicktime_material.h" + +#if defined ( WIN32 ) + #include + #include <../dx9sdk/include/dsound.h> +#endif + +#include "tier0/memdbgon.h" + + +// =========================================================================== +// CQuicktimeMaterialRGBTextureRegenerator - Inherited from ITextureRegenerator +// Copies and converts the buffer bits to texture bits +// Currently only supports 32-bit BGRA +// =========================================================================== +CQuicktimeMaterialRGBTextureRegenerator::CQuicktimeMaterialRGBTextureRegenerator() : + m_SrcGWorld( nullptr ), + m_nSourceWidth( 0 ), + m_nSourceHeight( 0 ) +{ +} + + +CQuicktimeMaterialRGBTextureRegenerator::~CQuicktimeMaterialRGBTextureRegenerator() +{ + // nothing to do +} + + +void CQuicktimeMaterialRGBTextureRegenerator::SetSourceGWorld( GWorldPtr theGWorld, int nWidth, int nHeight ) +{ + m_SrcGWorld = theGWorld; + m_nSourceWidth = nWidth; + m_nSourceHeight = nHeight; +} + + +void CQuicktimeMaterialRGBTextureRegenerator::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ) +{ + AssertExit( pVTFTexture != nullptr ); + + // Error condition, should only have 1 frame, 1 face, 1 mip level + if ( ( pVTFTexture->FrameCount() > 1 ) || ( pVTFTexture->FaceCount() > 1 ) || ( pVTFTexture->MipCount() > 1 ) || ( pVTFTexture->Depth() > 1 ) ) + { + WarningAssert( "Texture Properties Incorrect "); + memset( pVTFTexture->ImageData(), 0xAA, pVTFTexture->ComputeTotalSize() ); + return; + } + + // Make sure we have a valid video image source + if ( m_SrcGWorld == nullptr ) + { + WarningAssert( "Video texture source not set" ); + memset( pVTFTexture->ImageData(), 0xCC, pVTFTexture->ComputeTotalSize() ); + return; + } + + // Verify the destination texture is set up correctly + Assert( pVTFTexture->Format() == IMAGE_FORMAT_BGRA8888 ); + Assert( pVTFTexture->RowSizeInBytes( 0 ) >= pVTFTexture->Width() * 4 ); + Assert( pVTFTexture->Width() >= m_nSourceWidth ); + Assert( pVTFTexture->Height() >= m_nSourceHeight ); + + // Copy directly from the Quicktime GWorld + PixMapHandle thePixMap = GetGWorldPixMap( m_SrcGWorld ); + + if ( LockPixels( thePixMap ) ) + { + BYTE *pImageData = pVTFTexture->ImageData(); + int dstStride = pVTFTexture->RowSizeInBytes( 0 ); + BYTE *pSrcData = (BYTE*) GetPixBaseAddr( thePixMap ); + long srcStride = QTGetPixMapHandleRowBytes( thePixMap ); + int rowSize = m_nSourceWidth * 4; + + for (int y = 0; y < m_nSourceHeight; y++ ) + { + memcpy( pImageData, pSrcData, rowSize ); + pImageData+= dstStride; + pSrcData+= srcStride; + } + + UnlockPixels( thePixMap ); + } + else + { + WarningAssert( "LockPixels Failed" ); + } +} + + +void CQuicktimeMaterialRGBTextureRegenerator::Release() +{ + // we don't invoke the destructor here, we're not using the no-release extensions +} + + + +// =========================================================================== +// CQuickTimeMaterial class - creates a material, opens a QuickTime movie +// and plays the movie onto the material +// =========================================================================== + +//----------------------------------------------------------------------------- +// CQuickTimeMaterial Constructor +//----------------------------------------------------------------------------- +CQuickTimeMaterial::CQuickTimeMaterial() : + m_pFileName( nullptr ), + m_MovieGWorld( nullptr ), + m_QTMovie( nullptr ), + m_AudioContext( nullptr ), + m_bInitCalled( false ) +{ + Reset(); +} + + +//----------------------------------------------------------------------------- +// CQuickTimeMaterial Destructor +//----------------------------------------------------------------------------- +CQuickTimeMaterial::~CQuickTimeMaterial() +{ + Reset(); +} + + +void CQuickTimeMaterial::Reset() +{ + SetQTFileName( nullptr ); + + DestroyProceduralTexture(); + DestroyProceduralMaterial(); + + m_TexCordU = 0.0f; + m_TexCordV = 0.0f; + + m_VideoFrameWidth = 0; + m_VideoFrameHeight = 0; + + m_PlaybackFlags = VideoPlaybackFlags::NO_PLAYBACK_OPTIONS; + + m_bMovieInitialized = false; + m_bMoviePlaying = false; + m_bMovieFinishedPlaying = false; + m_bMoviePaused = false; + m_bLoopMovie = false; + + m_bHasAudio = false; + m_bMuted = false; + + m_CurrentVolume = 0.0f; + + m_QTMovieTimeScale = 0; + m_QTMovieDuration = 0; + m_QTMovieDurationinSec = 0.0f; + m_QTMovieFrameRate.SetFPS( 0, false ); + + SAFE_RELEASE_AUDIOCONTEXT( m_AudioContext ); + SAFE_DISPOSE_GWORLD( m_MovieGWorld ); + SAFE_DISPOSE_MOVIE( m_QTMovie ); + + m_LastResult = VideoResult::SUCCESS; +} + + +void CQuickTimeMaterial::SetQTFileName( const char *theQTMovieFileName ) +{ + SAFE_DELETE_ARRAY( m_pFileName ); + + if ( theQTMovieFileName != nullptr ) + { + AssertMsg( V_strlen( theQTMovieFileName ) <= MAX_QT_FILENAME_LEN, "Bad Quicktime Movie Filename" ); + m_pFileName = COPY_STRING( theQTMovieFileName ); + } + +} + + +VideoResult_t CQuickTimeMaterial::SetResult( VideoResult_t status ) +{ + m_LastResult = status; + return status; +} + + +//----------------------------------------------------------------------------- +// Video information functions +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Returns the resolved filename of the video, as it might differ from +// what the user supplied, (also with absolute path) +//----------------------------------------------------------------------------- +const char *CQuickTimeMaterial::GetVideoFileName() +{ + return m_pFileName; +} + + +VideoFrameRate_t &CQuickTimeMaterial::GetVideoFrameRate() +{ + return m_QTMovieFrameRate; +} + + +VideoResult_t CQuickTimeMaterial::GetLastResult() +{ + return m_LastResult; +} + + +//----------------------------------------------------------------------------- +// Audio Functions +//----------------------------------------------------------------------------- +bool CQuickTimeMaterial::HasAudio() +{ + return m_bHasAudio; +} + + +bool CQuickTimeMaterial::SetVolume( float fVolume ) +{ + clamp( fVolume, 0.0f, 1.0f ); + + m_CurrentVolume = fVolume; + + if ( m_AudioContext != nullptr && m_bHasAudio ) + { + short movieVolume = (short) ( m_CurrentVolume * 256.0f ); + + SetMovieVolume( m_QTMovie, movieVolume ); + + SetResult( VideoResult::SUCCESS ); + return true; + } + + SetResult( VideoResult::AUDIO_ERROR_OCCURED ); + return false; +} + + +float CQuickTimeMaterial::GetVolume() +{ + return m_CurrentVolume; +} + + +void CQuickTimeMaterial::SetMuted( bool bMuteState ) +{ + AssertExitFunc( m_bMoviePlaying, SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE) ); + + SetResult( VideoResult::SUCCESS ); + + if ( bMuteState == m_bMuted ) // no change? + { + return; + } + + m_bMuted = bMuteState; + + if ( m_bHasAudio ) + { + OSStatus result = SetMovieAudioMute( m_QTMovie, m_bMuted, 0 ); + AssertExitFunc( result == noErr, SetResult( VideoResult::AUDIO_ERROR_OCCURED) ); + } + + SetResult( VideoResult::SUCCESS ); +} + + +bool CQuickTimeMaterial::IsMuted() +{ + return m_bMuted; +} + + +VideoResult_t CQuickTimeMaterial::SoundDeviceCommand( VideoSoundDeviceOperation_t operation, void *pDevice, void *pData ) +{ + AssertExitV( m_bMovieInitialized || m_bMoviePlaying, VideoResult::OPERATION_OUT_OF_SEQUENCE ); + + switch( operation ) + { + // On win32, we try and create an audio context from a GUID + case VideoSoundDeviceOperation::SET_DIRECT_SOUND_DEVICE: + { +#if defined ( WIN32 ) + SAFE_RELEASE_AUDIOCONTEXT( m_AudioContext ); + return ( CreateMovieAudioContext( m_bHasAudio, m_QTMovie, &m_AudioContext ) ? SetResult( VideoResult::SUCCESS ) : SetResult( VideoResult::AUDIO_ERROR_OCCURED ) ); +#else + // On any other OS, we don't support this operation + return SetResult( VideoResult::OPERATION_NOT_SUPPORTED ); +#endif + } + case VideoSoundDeviceOperation::SET_SOUND_MANAGER_DEVICE: + { +#if defined ( OSX ) + SAFE_RELEASE_AUDIOCONTEXT( m_AudioContext ); + return ( CreateMovieAudioContext( m_bHasAudio, m_QTMovie, &m_AudioContext ) ? SetResult( VideoResult::SUCCESS ) : SetResult( VideoResult::AUDIO_ERROR_OCCURED ) ); +#else + // On any other OS, we don't support this operation + return SetResult( VideoResult::OPERATION_NOT_SUPPORTED ); +#endif + } + + case VideoSoundDeviceOperation::SET_LIB_AUDIO_DEVICE: + case VideoSoundDeviceOperation::HOOK_X_AUDIO: + case VideoSoundDeviceOperation::SET_MILES_SOUND_DEVICE: + { + return SetResult( VideoResult::OPERATION_NOT_SUPPORTED ); + } + default: + { + return SetResult( VideoResult::BAD_INPUT_PARAMETERS ); + } + } + +} + + +//----------------------------------------------------------------------------- +// Initializes the video material +//----------------------------------------------------------------------------- +bool CQuickTimeMaterial::Init( const char *pMaterialName, const char *pFileName, VideoPlaybackFlags_t flags ) +{ + SetResult( VideoResult::BAD_INPUT_PARAMETERS ); + AssertExitF( IS_NOT_EMPTY( pFileName ) ); + AssertExitF( m_bInitCalled == false ); + + m_PlaybackFlags = flags; + + OpenQTMovie( pFileName ); // Open up the Quicktime file + + if ( !m_bMovieInitialized ) + { + return false; // Something bad happened when we went to open + } + + // Now we can properly setup our regenerators + m_TextureRegen.SetSourceGWorld( m_MovieGWorld, m_VideoFrameWidth, m_VideoFrameHeight ); + + CreateProceduralTexture( pMaterialName ); + CreateProceduralMaterial( pMaterialName ); + + // Start movie playback + if ( !BITFLAGS_SET( m_PlaybackFlags, VideoPlaybackFlags::DONT_AUTO_START_VIDEO ) ) + { + StartVideo(); + } + + m_bInitCalled = true; // Look, if you only got one shot... + + return true; +} + + +void CQuickTimeMaterial::Shutdown( void ) +{ + StopVideo(); + Reset(); +} + + +//----------------------------------------------------------------------------- +// Video playback state functions +//----------------------------------------------------------------------------- +bool CQuickTimeMaterial::IsVideoReadyToPlay() +{ + return m_bMovieInitialized; +} + + +bool CQuickTimeMaterial::IsVideoPlaying() +{ + return m_bMoviePlaying; +} + + +//----------------------------------------------------------------------------- +// Checks to see if the video has a new frame ready to be rendered and +// downloaded into the texture and eventually display +//----------------------------------------------------------------------------- +bool CQuickTimeMaterial::IsNewFrameReady( void ) +{ + // Are we waiting to start playing the first frame? if so, tell them we are ready! + if ( m_bMovieInitialized == true ) + { + return true; + } + + // We better be playing the movie + AssertExitF( m_bMoviePlaying ); + + // paused? + if ( m_bMoviePaused ) + { + return false; + } + + TimeValue curMovieTime = GetMovieTime( m_QTMovie, nullptr ); + + if ( curMovieTime >= m_QTMovieDuration || m_NextInterestingTimeToPlay == NO_MORE_INTERESTING_TIMES ) + { + // if we are looping, we have another frame, otherwise no + return m_bLoopMovie; + } + + // Enough time passed to get to next frame?? + if ( curMovieTime < m_NextInterestingTimeToPlay ) + { + // nope.. use the previous frame + return false; + } + + // we have a new frame we want then.. + return true; +} + + +bool CQuickTimeMaterial::IsFinishedPlaying() +{ + return m_bMovieFinishedPlaying; +} + + +void CQuickTimeMaterial::SetLooping( bool bLoopVideo ) +{ + m_bLoopMovie = bLoopVideo; +} + + +bool CQuickTimeMaterial::IsLooping() +{ + return m_bLoopMovie; +} + + +void CQuickTimeMaterial::SetPaused( bool bPauseState ) +{ + if ( !m_bMoviePlaying || m_bMoviePaused == bPauseState ) + { + Assert( m_bMoviePlaying ); + return; + } + + if ( bPauseState ) // Pausing the movie? + { + // Save off current time and set paused state + m_MoviePauseTime = GetMovieTime( m_QTMovie, nullptr ); + StopMovie( m_QTMovie ); + } + else // unpausing the movie + { + // Reset the movie to the paused time + SetMovieTimeValue( m_QTMovie, m_MoviePauseTime ); + StartMovie( m_QTMovie ); + Assert( GetMoviesError() == noErr ); + } + + m_bMoviePaused = bPauseState; +} + + +bool CQuickTimeMaterial::IsPaused() +{ + return ( m_bMoviePlaying ) ? m_bMoviePaused : false; +} + + +// Begins playback of the movie +bool CQuickTimeMaterial::StartVideo() +{ + if ( !m_bMovieInitialized ) + { + Assert( false ); + SetResult( VideoResult::OPERATION_ALREADY_PERFORMED ); + return false; + } + + // Start the movie playing at the first frame + SetMovieTimeValue( m_QTMovie, m_MovieFirstFrameTime ); + Assert( GetMoviesError() == noErr ); + + StartMovie( m_QTMovie ); + Assert( GetMoviesError() == noErr ); + + // Transition to playing state + m_bMovieInitialized = false; + m_bMoviePlaying = true; + + // Deliberately set the next interesting time to the current time to + // insure that the ::update() call causes the textures to be downloaded + m_NextInterestingTimeToPlay = m_MovieFirstFrameTime; + Update(); + + return true; +} + + +// stops movie for good, frees resources, but retains texture & material of last frame rendered +bool CQuickTimeMaterial::StopVideo() +{ + if ( !m_bMoviePlaying ) + { + SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE ); + return false; + } + + StopMovie( m_QTMovie ); + + m_bMoviePlaying = false; + m_bMoviePaused = false; + m_bMovieFinishedPlaying = true; + + // free resources + CloseQTFile(); + + SetResult( VideoResult::SUCCESS ); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Updates our scene +// Output : true = movie playing ok, false = time to end movie +// supposed to be: Returns true on a new frame of video being downloaded into the texture +//----------------------------------------------------------------------------- +bool CQuickTimeMaterial::Update( void ) +{ + AssertExitF( m_bMoviePlaying ); + + OSType qTypes[1] = { VisualMediaCharacteristic }; + + // are we paused? can't update if so... + if ( m_bMoviePaused ) + { + return true; // reuse the last frame + } + + // Get current time in the movie + TimeValue curMovieTime = GetMovieTime( m_QTMovie, nullptr ); + + // Did we hit the end of the movie? + if ( curMovieTime >= m_QTMovieDuration ) + { + // If we're not looping, then report that we are done updating + if ( m_bLoopMovie == false ) + { + StopVideo(); + return false; + } + + // Reset the movie to the start time + SetMovieTimeValue( m_QTMovie, m_MovieFirstFrameTime ); + AssertExitF( GetMoviesError() == noErr ); + + // Assure fall through to render a new frame + m_NextInterestingTimeToPlay = m_MovieFirstFrameTime; + } + + // Are we on the last frame of the movie? (but not past the end of any audio?) + if ( m_NextInterestingTimeToPlay == NO_MORE_INTERESTING_TIMES ) + { + return true; // reuse last frame + } + + // Enough time passed to get to next frame? + if ( curMovieTime < m_NextInterestingTimeToPlay ) + { + // nope.. use the previous frame + return true; + } + + // move the movie along + UpdateMovie( m_QTMovie ); + AssertExitF( GetMoviesError() == noErr ); + + // Let QuickTime render the frame + MoviesTask( m_QTMovie, 0L ); + AssertExitF( GetMoviesError() == noErr ); + + // Get the next frame after the current time (the movie may have advanced a bit during UpdateMovie() and MovieTasks() + GetMovieNextInterestingTime( m_QTMovie, nextTimeStep | nextTimeEdgeOK, 1, qTypes, GetMovieTime( m_QTMovie, nullptr ), fixed1, &m_NextInterestingTimeToPlay, nullptr ); + + // hit the end of the movie? + if ( GetMoviesError() == invalidTime || m_NextInterestingTimeToPlay == END_OF_QUICKTIME_MOVIE ) + { + m_NextInterestingTimeToPlay = NO_MORE_INTERESTING_TIMES; + } + + // Regenerate our texture, it'll grab from the GWorld Directly + m_Texture->Download(); + + SetResult( VideoResult::SUCCESS ); + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the material +//----------------------------------------------------------------------------- +IMaterial *CQuickTimeMaterial::GetMaterial() +{ + return m_Material; +} + + +//----------------------------------------------------------------------------- +// Returns the texcoord range +//----------------------------------------------------------------------------- +void CQuickTimeMaterial::GetVideoTexCoordRange( float *pMaxU, float *pMaxV ) +{ + AssertExit( pMaxU != nullptr && pMaxV != nullptr ); + + if ( m_Texture == nullptr ) // no texture? + { + *pMaxU = *pMaxV = 1.0f; + return; + } + + *pMaxU = m_TexCordU; + *pMaxV = m_TexCordV; +} + + +//----------------------------------------------------------------------------- +// Returns the frame size of the QuickTime Video in pixels +//----------------------------------------------------------------------------- +void CQuickTimeMaterial::GetVideoImageSize( int *pWidth, int *pHeight ) +{ + Assert( pWidth != nullptr && pHeight != nullptr ); + + *pWidth = m_VideoFrameWidth; + *pHeight = m_VideoFrameHeight; +} + + +float CQuickTimeMaterial::GetVideoDuration() +{ + return m_QTMovieDurationinSec; +} + + +int CQuickTimeMaterial::GetFrameCount() +{ + return m_QTMovieFrameCount; +} + + +//----------------------------------------------------------------------------- +// Sets the frame for an QuickTime Material (use instead of SetTime) +//----------------------------------------------------------------------------- +bool CQuickTimeMaterial::SetFrame( int FrameNum ) +{ + if ( !m_bMoviePlaying ) + { + Assert( false ); + SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE ); + return false; + } + + float theTime = (float) FrameNum * m_QTMovieFrameRate.GetFPS(); + return SetTime( theTime ); +} + + +int CQuickTimeMaterial::GetCurrentFrame() +{ + AssertExitV( m_bMoviePlaying, -1 ); + + TimeValue curTime = m_bMoviePaused ? m_MoviePauseTime : GetMovieTime( m_QTMovie, nullptr ); + + return curTime / m_QTMovieFrameRate.GetUnitsPerFrame(); +} + + +float CQuickTimeMaterial::GetCurrentVideoTime() +{ + AssertExitV( m_bMoviePlaying, -1.0f ); + + TimeValue curTime = m_bMoviePaused ? m_MoviePauseTime : GetMovieTime( m_QTMovie, nullptr ); + + return curTime / m_QTMovieFrameRate.GetUnitsPerSecond(); +} + + +bool CQuickTimeMaterial::SetTime( float flTime ) +{ + AssertExitF( m_bMoviePlaying ); + AssertExitF( flTime >= 0 && flTime < m_QTMovieDurationinSec ); + + TimeValue newTime = (TimeValue) ( flTime * m_QTMovieFrameRate.GetUnitsPerSecond() + 0.5f) ; + + clamp( newTime, m_MovieFirstFrameTime, m_QTMovieDuration ); + + // Are we paused? + if ( m_bMoviePaused ) + { + m_MoviePauseTime = newTime; + return true; + } + + TimeValue curMovieTime = GetMovieTime( m_QTMovie, nullptr ); + + // Don't stop and reset movie if we are within 1 frame of the requested time + if ( newTime <= curMovieTime - m_QTMovieFrameRate.GetUnitsPerFrame() || newTime >= curMovieTime + m_QTMovieFrameRate.GetUnitsPerFrame() ) + { + // Reset the movie to the requested time + StopMovie( m_QTMovie ); + SetMovieTimeValue( m_QTMovie, newTime ); + StartMovie( m_QTMovie ); + + Assert( GetMoviesError() == noErr ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Initializes, shuts down the procedural texture +//----------------------------------------------------------------------------- +void CQuickTimeMaterial::CreateProceduralTexture( const char *pTextureName ) +{ + AssertIncRange( m_VideoFrameWidth, cMinVideoFrameWidth, cMaxVideoFrameWidth ); + AssertIncRange( m_VideoFrameHeight, cMinVideoFrameHeight, cMaxVideoFrameHeight ); + AssertStr( pTextureName ); + + // Either make the texture the same dimensions as the video, + // or choose power-of-two textures which are at least as big as the video + bool actualSizeTexture = BITFLAGS_SET( m_PlaybackFlags, VideoPlaybackFlags::TEXTURES_ACTUAL_SIZE ); + + int nWidth = ( actualSizeTexture ) ? ALIGN_VALUE( m_VideoFrameWidth, TEXTURE_SIZE_ALIGNMENT ) : ComputeGreaterPowerOfTwo( m_VideoFrameWidth ); + int nHeight = ( actualSizeTexture ) ? ALIGN_VALUE( m_VideoFrameHeight, TEXTURE_SIZE_ALIGNMENT ) : 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 != nullptr ) + { + // 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( nullptr /*, 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( "$nobasetexture", 1 ); + 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 != nullptr ) + { + pMaterial->DeleteIfUnreferenced(); + } +} + + + +//----------------------------------------------------------------------------- +// Opens a movie file using quicktime +//----------------------------------------------------------------------------- +void CQuickTimeMaterial::OpenQTMovie( const char *theQTMovieFileName ) +{ + AssertExit( IS_NOT_EMPTY( theQTMovieFileName ) ); + + // Set graphics port +#if defined ( WIN32 ) + SetGWorld ( (CGrafPtr) GetNativeWindowPort( nil ), nil ); +#elif defined ( OSX ) + SetGWorld( nil, nil ); +#endif + + SetQTFileName( theQTMovieFileName ); + + Handle MovieFileDataRef = nullptr; + OSType MovieFileDataRefType = 0; + + CFStringRef imageStrRef = CFStringCreateWithCString ( NULL, theQTMovieFileName, 0 ); + AssertExitFunc( imageStrRef != nullptr, SetResult( VideoResult::SYSTEM_ERROR_OCCURED ) ); + + OSErr status = QTNewDataReferenceFromFullPathCFString( imageStrRef, (QTPathStyle) kQTNativeDefaultPathStyle, 0, &MovieFileDataRef, &MovieFileDataRefType ); + AssertExitFunc( status == noErr, SetResult( VideoResult::FILE_ERROR_OCCURED ) ); + + CFRelease( imageStrRef ); + + status = NewMovieFromDataRef( &m_QTMovie, newMovieActive, nil, MovieFileDataRef, MovieFileDataRefType ); + SAFE_DISPOSE_HANDLE( MovieFileDataRef ); + + if ( status != noErr ) + { + Assert( false ); + Reset(); + SetResult( VideoResult::VIDEO_ERROR_OCCURED ); + return; + } + + // disabling audio? + if ( BITFLAGS_SET( m_PlaybackFlags, VideoPlaybackFlags::NO_AUDIO ) ) + { + m_bHasAudio = false; + } + else + { + // does movie have audio? + Track audioTrack = GetMovieIndTrackType( m_QTMovie, 1, SoundMediaType, movieTrackMediaType ); + m_bHasAudio = ( audioTrack != nullptr ); + } + + // Now we need to extract the time info from the QT Movie + m_QTMovieTimeScale = GetMovieTimeScale( m_QTMovie ); + m_QTMovieDuration = GetMovieDuration( m_QTMovie ); + + // compute movie duration + m_QTMovieDurationinSec = float ( double( m_QTMovieDuration ) / double( m_QTMovieTimeScale ) ); + if ( !MovieGetStaticFrameRate( m_QTMovie, m_QTMovieFrameRate ) ) + { + WarningAssert( "Couldn't Get Frame Rate" ); + } + + // and get an estimated frame count + m_QTMovieFrameCount = m_QTMovieDuration / m_QTMovieTimeScale; + + if ( m_QTMovieFrameRate.GetUnitsPerSecond() == m_QTMovieTimeScale ) + { + m_QTMovieFrameCount = m_QTMovieDuration / m_QTMovieFrameRate.GetUnitsPerFrame(); + } + else + { + m_QTMovieFrameCount = (int) ( (float) m_QTMovieDurationinSec * m_QTMovieFrameRate.GetFPS() + 0.5f ); + } + + // 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... + AssertExitFunc( m_QTMovieRect.top == 0 && m_QTMovieRect.left == 0 && + m_QTMovieRect.right >= cMinVideoFrameWidth && m_QTMovieRect.right <= cMaxVideoFrameWidth && + m_QTMovieRect.bottom >= cMinVideoFrameHeight && m_QTMovieRect.bottom <= cMaxVideoFrameHeight && + m_QTMovieRect.right % 4 == 0, + SetResult( VideoResult::VIDEO_ERROR_OCCURED ) ); + + // Setup the QuiuckTime Graphics World for the Movie + status = QTNewGWorld( &m_MovieGWorld, k32BGRAPixelFormat, &m_QTMovieRect, nil, nil, 0 ); + AssertExit( status == noErr ); + + // Setup the playback gamma according to the convar + SetGWorldDecodeGamma( m_MovieGWorld, VideoPlaybackGamma::USE_GAMMA_CONVAR ); + + // Assign the GWorld to this movie + SetMovieGWorld( m_QTMovie, m_MovieGWorld, nil ); + + // Setup Movie Audio, unless suppressed + if ( !CreateMovieAudioContext( m_bHasAudio, m_QTMovie, &m_AudioContext, true, &m_CurrentVolume ) ) + { + SetResult( VideoResult::AUDIO_ERROR_OCCURED ); + WarningAssert( "Couldn't Set Audio" ); + } + + // Get the time of the first frame + OSType qTypes[1] = { VisualMediaCharacteristic }; + short qFlags = nextTimeStep | nextTimeEdgeOK; // use nextTimeStep instead of nextTimeMediaSample for MPEG 1-2 compatibility + + GetMovieNextInterestingTime( m_QTMovie, qFlags, 1, qTypes, (TimeValue) 0, fixed1, &m_MovieFirstFrameTime, NULL ); + AssertExitFunc( GetMoviesError() == noErr, SetResult( VideoResult::VIDEO_ERROR_OCCURED ) ); + + // Preroll the movie + if ( BITFLAGS_SET( m_PlaybackFlags, VideoPlaybackFlags::PRELOAD_VIDEO ) ) + { + Fixed playRate = GetMoviePreferredRate( m_QTMovie ); + status = PrerollMovie( m_QTMovie, m_MovieFirstFrameTime, playRate ); + AssertExitFunc( status == noErr, SetResult( VideoResult::VIDEO_ERROR_OCCURED ) ); + } + + m_bMovieInitialized = true; +} + + +void CQuickTimeMaterial::CloseQTFile() +{ + if ( m_QTMovie == nullptr ) + { + return; + } + + SAFE_RELEASE_AUDIOCONTEXT( m_AudioContext ); + SAFE_DISPOSE_GWORLD( m_MovieGWorld ); + SAFE_DISPOSE_MOVIE( m_QTMovie ); + + SetQTFileName( nullptr ); +} + + -- cgit v1.2.3