1
This commit is contained in:
100
video/quicktime_common.h
Normal file
100
video/quicktime_common.h
Normal file
@ -0,0 +1,100 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// File: quicktime_common.h
|
||||
//
|
||||
// QuickTime limits and constants shared among all QuickTime functions
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#ifndef QUICKTIME_COMMON_H
|
||||
#define QUICKTIME_COMMON_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef OSX
|
||||
// The OSX 10.7 SDK dropped support for the functions below, so we manually pull them in
|
||||
#include <dlfcn.h>
|
||||
typedef PixMapHandle
|
||||
(*PFNGetGWorldPixMap)(GWorldPtr offscreenGWorld);
|
||||
typedef Ptr
|
||||
(*PFNGetPixBaseAddr)(PixMapHandle pm);
|
||||
typedef Boolean
|
||||
(*PFNLockPixels)(PixMapHandle pm);
|
||||
typedef void
|
||||
(*PFNUnlockPixels)(PixMapHandle pm);
|
||||
typedef void
|
||||
(*PFNDisposeGWorld)(GWorldPtr offscreenGWorld);
|
||||
typedef void
|
||||
(*PFNSetGWorld)(CGrafPtr port,GDHandle gdh);
|
||||
typedef SInt32
|
||||
(*PFNGetPixRowBytes)(PixMapHandle pm);
|
||||
|
||||
|
||||
extern PFNGetGWorldPixMap GetGWorldPixMap;
|
||||
extern PFNGetPixBaseAddr GetPixBaseAddr;
|
||||
extern PFNLockPixels LockPixels;
|
||||
extern PFNUnlockPixels UnlockPixels;
|
||||
extern PFNDisposeGWorld DisposeGWorld;
|
||||
extern PFNSetGWorld SetGWorld;
|
||||
extern PFNGetPixRowBytes GetPixRowBytes;
|
||||
#endif
|
||||
|
||||
|
||||
// constant that define the bounds of various inputs
|
||||
static const int cMinVideoFrameWidth = 16;
|
||||
static const int cMinVideoFrameHeight = 16;
|
||||
static const int cMaxVideoFrameWidth = 2 * 2048;
|
||||
static const int cMaxVideoFrameHeight = 2 * 2048;
|
||||
|
||||
static const int cMinFPS = 1;
|
||||
static const int cMaxFPS = 600;
|
||||
|
||||
static const float cMinDuration = 0.016666666f; // 1/60th second
|
||||
static const float cMaxDuration = 3600.0f; // 1 Hour
|
||||
|
||||
static const int cMinSampleRate = 11025; // 1/4 CD sample rate
|
||||
static const int cMaxSampleRate = 88200; // 2x CD rate
|
||||
|
||||
#define NO_MORE_INTERESTING_TIMES -2
|
||||
#define END_OF_QUICKTIME_MOVIE -1
|
||||
|
||||
// ===========================================================================
|
||||
// Macros & Utility functions
|
||||
// ===========================================================================
|
||||
|
||||
#define SAFE_DISPOSE_HANDLE( _handle ) if ( _handle != nullptr ) { DisposeHandle( (Handle) _handle ); _handle = nullptr; }
|
||||
#define SAFE_DISPOSE_GWORLD( _gworld ) if ( _gworld != nullptr ) { DisposeGWorld( _gworld ); _gworld = nullptr; }
|
||||
#define SAFE_DISPOSE_MOVIE( _movie ) if ( _movie != nullptr ) { DisposeMovie( _movie ); ThreadSleep(10); Assert( GetMoviesError() == noErr ); _movie = nullptr; }
|
||||
#define SAFE_RELEASE_AUDIOCONTEXT( _cxt ) if ( _cxt != nullptr ) { QTAudioContextRelease( _cxt ); _cxt = nullptr; }
|
||||
#define SAFE_RELEASE_CFREF( _ref ) if ( _ref != nullptr ) { CFRelease( _ref ); _ref = nullptr; }
|
||||
// Utility functions
|
||||
|
||||
extern char *COPY_STRING( const char *pString );
|
||||
|
||||
bool MovieGetStaticFrameRate( Movie inMovie, VideoFrameRate_t &theFrameRate );
|
||||
|
||||
bool SetGWorldDecodeGamma( CGrafPtr theGWorld, VideoPlaybackGamma_t gamma );
|
||||
|
||||
bool CreateMovieAudioContext( bool enableAudio, Movie inMovie, QTAudioContextRef *pAudioConectext, bool setVolume = false, float *pCurrentVolume = nullptr );
|
||||
|
||||
VideoPlaybackGamma_t GetSystemPlaybackGamma();
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
#endif // QUICKTIME_COMMON_H
|
962
video/quicktime_material.cpp
Normal file
962
video/quicktime_material.cpp
Normal file
@ -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 <WinDef.h>
|
||||
#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 );
|
||||
}
|
||||
|
||||
|
221
video/quicktime_material.h
Normal file
221
video/quicktime_material.h
Normal file
@ -0,0 +1,221 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#ifndef QUICKTIME_MATERIAL_H
|
||||
#define QUICKTIME_MATERIAL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class IFileSystem;
|
||||
class IMaterialSystem;
|
||||
class CQuickTimeMaterial;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Global interfaces - you already did the needed includes, right?
|
||||
//-----------------------------------------------------------------------------
|
||||
extern IFileSystem *g_pFileSystem;
|
||||
extern IMaterialSystem *materials;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Quicktime includes
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined ( OSX )
|
||||
#include <quicktime/QTML.h>
|
||||
#include <quicktime/Movies.h>
|
||||
#include <quicktime/MediaHandlers.h>
|
||||
#elif defined ( WIN32 )
|
||||
#include <QTML.h>
|
||||
#include <Movies.h>
|
||||
#include <windows.h>
|
||||
#include <MediaHandlers.h>
|
||||
#elif
|
||||
#error "Quicktime not supported on this target platform"
|
||||
#endif
|
||||
|
||||
|
||||
#include "video/ivideoservices.h"
|
||||
|
||||
#include "video_macros.h"
|
||||
#include "quicktime_common.h"
|
||||
|
||||
#include "materialsystem/itexture.h"
|
||||
#include "materialsystem/imaterialsystem.h"
|
||||
#include "materialsystem/MaterialSystemUtil.h"
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Texture regenerator - callback to get new movie pixels into the texture
|
||||
// -----------------------------------------------------------------------------
|
||||
class CQuicktimeMaterialRGBTextureRegenerator : public ITextureRegenerator
|
||||
{
|
||||
public:
|
||||
CQuicktimeMaterialRGBTextureRegenerator();
|
||||
~CQuicktimeMaterialRGBTextureRegenerator();
|
||||
|
||||
void SetSourceGWorld( GWorldPtr theGWorld, int nWidth, int nHeight );
|
||||
|
||||
// Inherited from ITextureRegenerator
|
||||
virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect );
|
||||
virtual void Release();
|
||||
|
||||
private:
|
||||
GWorldPtr m_SrcGWorld;
|
||||
int m_nSourceWidth;
|
||||
int m_nSourceHeight;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Class used to play a QuickTime video onto a texture
|
||||
// -----------------------------------------------------------------------------
|
||||
class CQuickTimeMaterial : public IVideoMaterial
|
||||
{
|
||||
public:
|
||||
CQuickTimeMaterial();
|
||||
~CQuickTimeMaterial();
|
||||
|
||||
static const int MAX_QT_FILENAME_LEN = 255;
|
||||
static const int MAX_MATERIAL_NAME_LEN = 255;
|
||||
static const int TEXTURE_SIZE_ALIGNMENT = 8;
|
||||
|
||||
// Initializes, shuts down the material
|
||||
bool Init( const char *pMaterialName, const char *pFileName, VideoPlaybackFlags_t flags );
|
||||
void Shutdown();
|
||||
|
||||
// Video information functions
|
||||
virtual const char *GetVideoFileName(); // Gets the file name of the video this material is playing
|
||||
virtual VideoResult_t GetLastResult(); // Gets detailed info on the last operation
|
||||
|
||||
virtual VideoFrameRate_t &GetVideoFrameRate(); // Returns the frame rate of the associated video in FPS
|
||||
|
||||
// Audio Functions
|
||||
virtual bool HasAudio(); // Query if the video has an audio track
|
||||
|
||||
virtual bool SetVolume( float fVolume ); // Adjust the playback volume
|
||||
virtual float GetVolume(); // Query the current volume
|
||||
virtual void SetMuted( bool bMuteState ); // Mute/UnMutes the audio playback
|
||||
virtual bool IsMuted(); // Query muted status
|
||||
|
||||
virtual VideoResult_t SoundDeviceCommand( VideoSoundDeviceOperation_t operation, void *pDevice = nullptr, void *pData = nullptr ); // Assign Sound Device for this Video Material
|
||||
|
||||
// Video playback state functions
|
||||
virtual bool IsVideoReadyToPlay(); // Queries if the video material was initialized successfully and is ready for playback, but not playing or finished
|
||||
virtual bool IsVideoPlaying(); // Is the video currently playing (and needs update calls, etc)
|
||||
virtual bool IsNewFrameReady(); // Do we have a new frame to get & display?
|
||||
virtual bool IsFinishedPlaying(); // Have we reached the end of the movie
|
||||
|
||||
virtual bool StartVideo(); // Starts the video playing
|
||||
virtual bool StopVideo(); // Terminates the video playing
|
||||
|
||||
virtual void SetLooping( bool bLoopVideo ); // Sets the video to loop (or not)
|
||||
virtual bool IsLooping(); // Queries if the video is looping
|
||||
|
||||
virtual void SetPaused( bool bPauseState ); // Pauses or Unpauses video playback
|
||||
virtual bool IsPaused(); // Queries if the video is paused
|
||||
|
||||
// Position in playback functions
|
||||
virtual float GetVideoDuration(); // Returns the duration of the associated video in seconds
|
||||
virtual int GetFrameCount(); // Returns the total number of (unique) frames in the video
|
||||
|
||||
virtual bool SetFrame( int FrameNum ); // Sets the current frame # in the video to play next
|
||||
virtual int GetCurrentFrame(); // Gets the current frame # for the video playback, 0 Based
|
||||
|
||||
virtual bool SetTime( float flTime ); // Sets the video playback to specified time (in seconds)
|
||||
virtual float GetCurrentVideoTime(); // Gets the current time in the video playback
|
||||
|
||||
// Update function
|
||||
virtual bool Update(); // Updates the video frame to reflect the time passed, true = new frame available
|
||||
|
||||
// Material / Texture Info functions
|
||||
virtual IMaterial *GetMaterial(); // Gets the IMaterial associated with an video material
|
||||
|
||||
virtual void GetVideoTexCoordRange( float *pMaxU, float *pMaxV ) ; // Returns the max texture coordinate of the video portion of the material surface ( 0.0, 0.0 to U, V )
|
||||
virtual void GetVideoImageSize( int *pWidth, int *pHeight ); // Returns the frame size of the Video Image Frame in pixels ( the stored in a subrect of the material itself)
|
||||
|
||||
|
||||
|
||||
private:
|
||||
friend class CQuicktimeMaterialRGBTextureRegenerator;
|
||||
|
||||
void Reset(); // clears internal state
|
||||
void SetQTFileName( const char *theQTMovieFileName );
|
||||
VideoResult_t SetResult( VideoResult_t status );
|
||||
|
||||
// Initializes, shuts down the video stream
|
||||
void OpenQTMovie( const char *theQTMovieFileName );
|
||||
void CloseQTFile();
|
||||
|
||||
// Initializes, shuts down the procedural texture
|
||||
void CreateProceduralTexture( const char *pTextureName );
|
||||
void DestroyProceduralTexture();
|
||||
|
||||
// Initializes, shuts down the procedural material
|
||||
void CreateProceduralMaterial( const char *pMaterialName );
|
||||
void DestroyProceduralMaterial();
|
||||
|
||||
CQuicktimeMaterialRGBTextureRegenerator m_TextureRegen;
|
||||
|
||||
VideoResult_t m_LastResult;
|
||||
|
||||
CMaterialReference m_Material; // Ref to Material used for rendering the video frame
|
||||
CTextureReference m_Texture; // Ref to the renderable texture which contains the most recent video frame (in a sub-rect)
|
||||
|
||||
float m_TexCordU; // Max U texture coordinate of the texture sub-rect which holds the video frame
|
||||
float m_TexCordV; // Max V texture coordinate of the texture sub-rect which holds the video frame
|
||||
|
||||
int m_VideoFrameWidth; // Size of the movie frame in pixels
|
||||
int m_VideoFrameHeight;
|
||||
|
||||
char *m_pFileName; // resolved filename of the movie being played
|
||||
VideoPlaybackFlags_t m_PlaybackFlags; // option flags user supplied
|
||||
|
||||
bool m_bInitCalled;
|
||||
bool m_bMovieInitialized;
|
||||
bool m_bMoviePlaying;
|
||||
bool m_bMovieFinishedPlaying;
|
||||
bool m_bMoviePaused;
|
||||
bool m_bLoopMovie;
|
||||
|
||||
bool m_bHasAudio;
|
||||
bool m_bMuted;
|
||||
|
||||
float m_CurrentVolume;
|
||||
|
||||
// QuickTime Stuff
|
||||
Movie m_QTMovie;
|
||||
|
||||
TimeScale m_QTMovieTimeScale; // Units per second
|
||||
TimeValue m_QTMovieDuration; // movie duration in TimeScale Units Per Second
|
||||
float m_QTMovieDurationinSec; // movie duration in seconds
|
||||
VideoFrameRate_t m_QTMovieFrameRate; // Frame Rate of movie
|
||||
int m_QTMovieFrameCount;
|
||||
|
||||
Rect m_QTMovieRect;
|
||||
GWorldPtr m_MovieGWorld;
|
||||
|
||||
QTAudioContextRef m_AudioContext;
|
||||
|
||||
TimeValue m_MovieFirstFrameTime;
|
||||
TimeValue m_NextInterestingTimeToPlay;
|
||||
TimeValue m_MoviePauseTime;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // QUICKTIME_MATERIAL_H
|
2146
video/quicktime_recorder.cpp
Normal file
2146
video/quicktime_recorder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
263
video/quicktime_recorder.h
Normal file
263
video/quicktime_recorder.h
Normal file
@ -0,0 +1,263 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#ifndef QUICKTIME_RECORDER_H
|
||||
#define QUICKTIME_RECORDER_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
#if defined( OSX )
|
||||
#include <quicktime/QTML.h>
|
||||
#include <quicktime/Movies.h>
|
||||
#include <quicktime/QuickTimeComponents.h>
|
||||
#elif defined( WIN32 )
|
||||
#include "QTML.h"
|
||||
#include "Movies.h"
|
||||
#include "QuickTimeComponents.h"
|
||||
#else
|
||||
#error "Quicktime encoding is not supported on this platform"
|
||||
#endif
|
||||
|
||||
#include "video/ivideoservices.h"
|
||||
|
||||
#include "video_macros.h"
|
||||
#include "quicktime_common.h"
|
||||
|
||||
|
||||
// comment out to prevent logging of creation data
|
||||
//#define LOG_ENCODER_OPERATIONS
|
||||
//#define LOG_ENCODER_AUDIO_OPERATIONS
|
||||
|
||||
// comment out to log images of frames
|
||||
//#define LOG_FRAMES_TO_TGA
|
||||
//#define LOG_FRAMES_TO_TGA_INTERVAL 16
|
||||
|
||||
#if defined( LOG_ENCODER_OPERATIONS ) || defined( LOG_ENCODER_AUDIO_OPERATIONS ) || defined ( LOG_FRAMES_TO_TGA ) || defined ( ENABLE_EXTERNAL_ENCODER_LOGGING )
|
||||
#include <filesystem.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// CQTVideoFileComposer
|
||||
// Class to manages to creation of a video file from a series of
|
||||
// supplied bitmap images.
|
||||
//
|
||||
// At this time, the time interval between frames must be the same for all
|
||||
// frames of the movie
|
||||
// ------------------------------------------------------------------------
|
||||
class CQTVideoFileComposer
|
||||
{
|
||||
public:
|
||||
static const CodecType DEFAULT_CODEC = kH264CodecType;
|
||||
static const CodecQ DEFAULT_ENCODE_QUALITY = codecNormalQuality;
|
||||
static const Fixed DEFAULT_GAMMA = kQTUseSourceGammaLevel;
|
||||
|
||||
static const int MIN_AUDIO_SAMPLE_GROUP_SIZE = 64;
|
||||
static const int MAX_AUDIO_GROUP_SIZE_IN_SEC = 4;
|
||||
|
||||
CQTVideoFileComposer();
|
||||
~CQTVideoFileComposer();
|
||||
|
||||
bool CreateNewMovie( const char *fileName, bool hasAudio );
|
||||
|
||||
bool SetMovieVideoParameters( int width, int height, VideoFrameRate_t movieFPS, VideoEncodeCodec_t desiredCodec, int encodeQuality, VideoEncodeGamma_t gamma );
|
||||
bool SetMovieSourceImageParameters( int srcWidth, int srcHeight, VideoEncodeSourceFormat_t srcImageFormat );
|
||||
bool SetMovieSourceAudioParameters( AudioEncodeSourceFormat_t srcAudioFormat = AudioEncodeSourceFormat::AUDIO_NONE, int audioSampleRate = 0, AudioEncodeOptions_t audioOptions = AudioEncodeOptions::NO_AUDIO_OPTIONS, int audioSampleGroupSize = 0 );
|
||||
|
||||
bool AppendVideoFrameToMedia( void *ImageBuffer, int strideAdjustBytes );
|
||||
bool AppendAudioSamplesToMedia( void *soundBuffer, size_t bufferSize );
|
||||
|
||||
bool AbortMovie();
|
||||
bool FinishMovie( bool SaveMovieToDisk = true );
|
||||
|
||||
size_t GetFrameBufferSize() { return m_SrcImageSize; }
|
||||
|
||||
bool CheckFilename( const char *filename );
|
||||
|
||||
void SetResult( VideoResult_t status );
|
||||
VideoResult_t GetResult();
|
||||
bool IsReadyToRecord();
|
||||
|
||||
int GetFrameCount() { return m_nFramesAdded; }
|
||||
int GetSampleCount() { return m_nSamplesAdded; }
|
||||
|
||||
VideoFrameRate_t GetFPS() { return m_MovieRecordFPS; }
|
||||
int GetSampleRate() { return m_AudioSourceFrequency; }
|
||||
|
||||
bool HasAudio() { return m_bHasAudioTrack; }
|
||||
|
||||
#ifdef ENABLE_EXTERNAL_ENCODER_LOGGING
|
||||
bool LogMessage( const char *msg );
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum AudioGrouping_t
|
||||
{
|
||||
AG_NONE = 0,
|
||||
AG_FIXED_SIZE,
|
||||
AG_PER_FRAME
|
||||
};
|
||||
|
||||
// disable copy constructor and copy assignment
|
||||
CQTVideoFileComposer( CQTVideoFileComposer &rhs );
|
||||
CQTVideoFileComposer& operator= ( CQTVideoFileComposer &rhs );
|
||||
|
||||
bool CheckForReadyness();
|
||||
bool BeginMovieCreation();
|
||||
bool EndMovieCreation( bool saveMovieData );
|
||||
|
||||
bool SyncAndFlushAudio();
|
||||
int GetAudioSampleCountThruFrame( int frameNo );
|
||||
|
||||
VideoResult_t m_LastResult;
|
||||
|
||||
// Current State of Movie Creation;
|
||||
bool m_bMovieCreated;
|
||||
bool m_bHasAudioTrack;
|
||||
|
||||
bool m_bMovieConfigured;
|
||||
bool m_bSourceImagesConfigured;
|
||||
bool m_bSourceAudioConfigured;
|
||||
|
||||
bool m_bComposingMovie;
|
||||
bool m_bMovieCompleted;
|
||||
|
||||
int m_nFramesAdded;
|
||||
|
||||
int m_nAudioFramesAdded;
|
||||
int m_nSamplesAdded;
|
||||
int m_nSamplesAddedToMedia;
|
||||
|
||||
// parameters of the movie to create;
|
||||
int m_MovieFrameWidth;
|
||||
int m_MovieFrameHeight;
|
||||
|
||||
VideoFrameRate_t m_MovieRecordFPS;
|
||||
TimeScale m_MovieTimeScale;
|
||||
TimeValue m_DurationPerFrame;
|
||||
|
||||
// Audio recording options
|
||||
AudioEncodeOptions_t m_AudioOptions; // Option flags specifed by user
|
||||
AudioGrouping_t m_SampleGrouping; // Mode to group samples
|
||||
int m_nAudioSampleGroupSize; // number of samples to collect per sample group
|
||||
|
||||
int m_AudioSourceFrequency; // Source frequency of the supplied audio
|
||||
int m_AudioBytesPerSample;
|
||||
|
||||
bool m_bBufferSourceAudio;
|
||||
bool m_bLimitAudioDurationToVideo;
|
||||
|
||||
byte *m_srcAudioBuffer; // buffer to hold audio samples
|
||||
size_t m_srcAudioBufferSize;
|
||||
size_t m_srcAudioBufferCurrentSize;
|
||||
|
||||
int m_AudioSampleFrameCounter;
|
||||
|
||||
char *m_FileName;
|
||||
|
||||
int m_SrcImageWidth;
|
||||
int m_SrcImageHeight;
|
||||
size_t m_SrcImageSize;
|
||||
int m_ScrImageMaxCompressedSize;
|
||||
byte *m_SrcImageBuffer;
|
||||
Handle m_SrcImageCompressedBuffer;
|
||||
OSType m_SrcPixelFormat;
|
||||
int m_SrcBytesPerPixel;
|
||||
|
||||
OSType m_GWorldPixelFormat;
|
||||
int m_GWorldBytesPerPixel;
|
||||
int m_GWorldImageWidth;
|
||||
int m_GWorldImageHeight;
|
||||
|
||||
Handle m_srcSoundDescription;
|
||||
|
||||
|
||||
// parameters used by QuickTime
|
||||
CodecQ m_EncodeQuality;
|
||||
CodecType m_VideoCodecToUse;
|
||||
Fixed m_EncodeGamma;
|
||||
|
||||
Rect m_GWorldRect;
|
||||
GWorldPtr m_theSrcGWorld;
|
||||
|
||||
// short m_ResRefNum; // QuickTime Movie Resource Ref number
|
||||
|
||||
Handle m_MovieFileDataRef;
|
||||
OSType m_MovieFileDataRefType;
|
||||
DataHandler m_MovieFileDataHandler;
|
||||
|
||||
|
||||
Movie m_theMovie;
|
||||
Track m_theVideoTrack;
|
||||
Track m_theAudioTrack;
|
||||
Media m_theVideoMedia;
|
||||
Media m_theAudioMedia;
|
||||
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
FileHandle_t m_LogFile;
|
||||
void LogMsg( PRINTF_FORMAT_STRING const char* pMsg, ... );
|
||||
#endif
|
||||
|
||||
#ifdef LOG_FRAMES_TO_TGA
|
||||
char m_TGAFileBase[MAX_PATH];
|
||||
#endif
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CQuickTimeVideoRecorder : public IVideoRecorder
|
||||
{
|
||||
public:
|
||||
CQuickTimeVideoRecorder();
|
||||
~CQuickTimeVideoRecorder();
|
||||
|
||||
virtual bool EstimateMovieFileSize( size_t *pEstSize, int movieWidth, int movieHeight, VideoFrameRate_t movieFps, float movieDuration, VideoEncodeCodec_t theCodec, int videoQuality, AudioEncodeSourceFormat_t srcAudioFormat = AudioEncodeSourceFormat::AUDIO_NONE, int audioSampleRate = 0 );
|
||||
|
||||
virtual bool CreateNewMovieFile( const char *pFilename, bool hasAudioTrack = false );
|
||||
|
||||
virtual bool SetMovieVideoParameters( VideoEncodeCodec_t theCodec, int videoQuality, int movieFrameWidth, int movieFrameHeight, VideoFrameRate_t movieFPS, VideoEncodeGamma_t gamma = VideoEncodeGamma::NO_GAMMA_ADJUST );
|
||||
virtual bool SetMovieSourceImageParameters( VideoEncodeSourceFormat_t srcImageFormat, int imgWidth, int imgHeight );
|
||||
virtual bool SetMovieSourceAudioParameters( AudioEncodeSourceFormat_t srcAudioFormat = AudioEncodeSourceFormat::AUDIO_NONE, int audioSampleRate = 0, AudioEncodeOptions_t audioOptions = AudioEncodeOptions::NO_AUDIO_OPTIONS, int audioSampleGroupSize = 0 );
|
||||
|
||||
virtual bool IsReadyToRecord();
|
||||
virtual VideoResult_t GetLastResult();
|
||||
|
||||
virtual bool AppendVideoFrame( void *pFrameBuffer, int nStrideAdjustBytes = 0 );
|
||||
virtual bool AppendAudioSamples( void *pSampleBuffer, size_t sampleSize );
|
||||
|
||||
virtual int GetFrameCount();
|
||||
virtual int GetSampleCount();
|
||||
virtual int GetSampleRate();
|
||||
virtual VideoFrameRate_t GetFPS();
|
||||
|
||||
virtual bool AbortMovie();
|
||||
virtual bool FinishMovie( bool SaveMovieToDisk = true );
|
||||
|
||||
#ifdef ENABLE_EXTERNAL_ENCODER_LOGGING
|
||||
virtual bool LogMessage( const char *msg );
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
void SetResult( VideoResult_t resultCode );
|
||||
|
||||
float GetDataRate( int quality, int width, int height );
|
||||
|
||||
CQTVideoFileComposer *m_pEncoder;
|
||||
VideoResult_t m_LastResult;
|
||||
bool m_bHasAudio;
|
||||
bool m_bMovieFinished;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // QUICKTIME_RECORDER_H
|
1096
video/quicktime_video.cpp
Normal file
1096
video/quicktime_video.cpp
Normal file
File diff suppressed because it is too large
Load Diff
128
video/quicktime_video.h
Normal file
128
video/quicktime_video.h
Normal file
@ -0,0 +1,128 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#ifndef QUICKTIME_VIDEO_H
|
||||
#define QUICKTIME_VIDEO_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class IFileSystem;
|
||||
class IMaterialSystem;
|
||||
class CQuickTimeMaterial;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Global interfaces - you already did the needed includes, right?
|
||||
//-----------------------------------------------------------------------------
|
||||
extern IFileSystem *g_pFileSystem;
|
||||
extern IMaterialSystem *materials;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Quicktime includes - conditional compilation on #define QUICKTIME in VPC
|
||||
// The intent is to have a default functionality fallback if not defined
|
||||
// which provides a dynamically generated texture (moving line on background)
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined ( OSX )
|
||||
#include <quicktime/QTML.h>
|
||||
#include <quicktime/Movies.h>
|
||||
#elif defined ( WIN32 )
|
||||
#include <QTML.h>
|
||||
#include <Movies.h>
|
||||
#include <windows.h>
|
||||
#elif
|
||||
#error "Quicktime not supported on this target platform"
|
||||
#endif
|
||||
|
||||
|
||||
#include "video/ivideoservices.h"
|
||||
#include "videosubsystem.h"
|
||||
|
||||
#include "utlvector.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CQuickTimeVideoSubSystem - Implementation of IVideoSubSystem
|
||||
// -----------------------------------------------------------------------------
|
||||
class CQuickTimeVideoSubSystem : public CTier2AppSystem< IVideoSubSystem >
|
||||
{
|
||||
typedef CTier2AppSystem< IVideoSubSystem > BaseClass;
|
||||
|
||||
public:
|
||||
CQuickTimeVideoSubSystem();
|
||||
~CQuickTimeVideoSubSystem();
|
||||
|
||||
// Inherited from IAppSystem
|
||||
virtual bool Connect( CreateInterfaceFn factory );
|
||||
virtual void Disconnect();
|
||||
virtual void *QueryInterface( const char *pInterfaceName );
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Inherited from IVideoSubSystem
|
||||
|
||||
// SubSystem Identification functions
|
||||
virtual VideoSystem_t GetSystemID();
|
||||
virtual VideoSystemStatus_t GetSystemStatus();
|
||||
virtual VideoSystemFeature_t GetSupportedFeatures();
|
||||
virtual const char *GetVideoSystemName();
|
||||
|
||||
// Setup & Shutdown Services
|
||||
virtual bool InitializeVideoSystem( IVideoCommonServices *pCommonServices );
|
||||
virtual bool ShutdownVideoSystem();
|
||||
|
||||
virtual VideoResult_t VideoSoundDeviceCMD( VideoSoundDeviceOperation_t operation, void *pDevice = nullptr, void *pData = nullptr );
|
||||
|
||||
// get list of file extensions and features we support
|
||||
virtual int GetSupportedFileExtensionCount();
|
||||
virtual const char *GetSupportedFileExtension( int num );
|
||||
virtual VideoSystemFeature_t GetSupportedFileExtensionFeatures( int num );
|
||||
|
||||
// Video Playback and Recording Services
|
||||
virtual VideoResult_t PlayVideoFileFullScreen( const char *filename, void *mainWindow, int windowWidth, int windowHeight, int desktopWidth, int desktopHeight, bool windowed, float forcedMinTime, VideoPlaybackFlags_t playbackFlags );
|
||||
|
||||
// Create/destroy a video material
|
||||
virtual IVideoMaterial *CreateVideoMaterial( const char *pMaterialName, const char *pVideoFileName, VideoPlaybackFlags_t flags );
|
||||
virtual VideoResult_t DestroyVideoMaterial( IVideoMaterial *pVideoMaterial );
|
||||
|
||||
// Create/destroy a video encoder
|
||||
virtual IVideoRecorder *CreateVideoRecorder();
|
||||
virtual VideoResult_t DestroyVideoRecorder( IVideoRecorder *pRecorder );
|
||||
|
||||
virtual VideoResult_t CheckCodecAvailability( VideoEncodeCodec_t codec );
|
||||
|
||||
virtual VideoResult_t GetLastResult();
|
||||
|
||||
private:
|
||||
|
||||
bool SetupQuickTime();
|
||||
bool ShutdownQuickTime();
|
||||
|
||||
VideoResult_t SetResult( VideoResult_t status );
|
||||
|
||||
bool m_bQuickTimeInitialized;
|
||||
VideoResult_t m_LastResult;
|
||||
|
||||
VideoSystemStatus_t m_CurrentStatus;
|
||||
VideoSystemFeature_t m_AvailableFeatures;
|
||||
|
||||
IVideoCommonServices *m_pCommonServices;
|
||||
|
||||
CUtlVector< IVideoMaterial* > m_MaterialList;
|
||||
CUtlVector< IVideoRecorder* > m_RecorderList;
|
||||
|
||||
static const VideoSystemFeature_t DEFAULT_FEATURE_SET;
|
||||
};
|
||||
|
||||
#endif // QUICKTIME_VIDEO_H
|
114
video/video_macros.h
Normal file
114
video/video_macros.h
Normal file
@ -0,0 +1,114 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// videomacros.h - Commone macros used by valve video services
|
||||
//
|
||||
// Purpose - to save typing, make things cleaer, prep for c++0x, prep for 64-bit,
|
||||
// make lots of money, and drive Brian crazy
|
||||
//
|
||||
// ===========================================================================
|
||||
|
||||
#ifndef VIDEOMACROS_H
|
||||
#define VIDEOMACROS_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tier0/dbg.h>
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// MACROS
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#define nullchar ( (char) 0x00 )
|
||||
#ifndef nullptr
|
||||
#define nullptr ( 0 )
|
||||
#endif
|
||||
|
||||
#define ZeroVar( var ) V_memset( &var, nullchar, sizeof( var ) )
|
||||
#define ZeroVarPtr( pVar ) V_memset( pVar, nullchar, sizeof( *pVar) )
|
||||
|
||||
#define SAFE_DELETE( var ) if ( var != nullptr ) { delete var; var = nullptr; }
|
||||
#define SAFE_DELETE_ARRAY( var ) if ( var != nullptr ) { delete[] var; var = nullptr; }
|
||||
|
||||
#define SAFE_RELEASE( var ) if ( var != nullptr ) { var->Release(); var = nullptr; }
|
||||
#define SAFE_FREE( var ) if ( var != nullptr ) { free( var ); var = nullptr; }
|
||||
|
||||
// Common Assert Patterns
|
||||
|
||||
#define AssertPtr( _exp ) Assert( ( _exp ) != nullptr )
|
||||
#define AssertNull( _exp ) Assert( ( _exp ) == nullptr )
|
||||
#define AssertStr( _str ) Assert( ( _str ) != nullptr && *( _str ) != nullchar )
|
||||
|
||||
#define AssertInRange( _exp, low, high ) Assert( ( _exp ) > ( low ) && ( _exp ) < ( high ) )
|
||||
#define AssertIncRange( _exp, low, high ) Assert( ( _exp ) >= ( low ) && ( _exp ) <= ( high ) )
|
||||
|
||||
// AssertExit macros .. in release builds, or when Assert is disabled, they exit (w/ opt return value)
|
||||
// if the assert condition is false
|
||||
//
|
||||
|
||||
#ifdef DBGFLAG_ASSERT
|
||||
|
||||
#define AssertExit( _exp ) Assert( _exp )
|
||||
#define AssertExitV( _exp , _rval ) Assert( _exp )
|
||||
#define AssertExitF( _exp ) Assert( _exp )
|
||||
#define AssertExitN( _exp ) Assert( _exp )
|
||||
#define AssertExitFunc( _exp, _func ) Assert( _exp )
|
||||
|
||||
#define AssertPtrExit( _exp ) Assert( ( _exp ) != nullptr )
|
||||
#define AssertPtrExitV( _exp , _rval ) Assert( ( _exp ) != nullptr )
|
||||
#define AssertPtrExitF( _exp ) Assert( ( _exp ) != nullptr )
|
||||
#define AssertPtrExitN( _exp ) Assert( ( _exp ) != nullptr )
|
||||
|
||||
#define AssertInRangeExit( _exp , low , high ) Assert( ( _exp ) > ( low ) && ( _exp ) < ( high ) )
|
||||
#define AssertInRangeExitV( _exp , low , high, _rval ) Assert( ( _exp ) > ( low ) && ( _exp ) < ( high ) )
|
||||
#define AssertInRangeExitF( _exp , low , high ) Assert( ( _exp ) > ( low ) && ( _exp ) < ( high ) )
|
||||
#define AssertInRangeExitN( _exp , low , high ) Assert( ( _exp ) > ( low ) && ( _exp ) < ( high ) )
|
||||
|
||||
|
||||
#else // Asserts not enabled
|
||||
|
||||
#define AssertExit( _exp ) if ( !( _exp ) ) return
|
||||
#define AssertExitV( _exp , _rval ) if ( !( _exp ) ) return _rval
|
||||
#define AssertExitF( _exp ) if ( !( _exp ) ) return false
|
||||
#define AssertExitN( _exp ) if ( !( _exp ) ) return nullptr
|
||||
#define AssertExitFunc( _exp, _func ) if ( !( _exp ) ) { _func; return; }
|
||||
|
||||
#define AssertPtrExit( _exp ) if ( ( _exp ) == nullptr ) return
|
||||
#define AssertPtrExitV( _exp , _rval ) if ( ( _exp ) == nullptr ) return _rval
|
||||
#define AssertPtrExitF( _exp ) if ( ( _exp ) == nullptr ) return false
|
||||
#define AssertPtrExitN( _exp ) if ( ( _exp ) == nullptr ) return nullptr
|
||||
|
||||
#define AssertInRangeExit( _exp, low, high ) if ( ( _exp ) <= ( low ) || ( _exp ) >= ( high ) ) return
|
||||
#define AssertInRangeExitV( _exp, low, high, _rval ) if ( ( _exp ) <= ( low ) || ( _exp ) >= ( high ) ) return _rval
|
||||
#define AssertInRangeExitF( _exp, low, high ) if ( ( _exp ) <= ( low ) || ( _exp ) >= ( high ) ) return false
|
||||
#define AssertInRangeExitN( _exp, low, high ) if ( ( _exp ) <= ( low ) || ( _exp ) >= ( high ) ) return nullptr
|
||||
|
||||
#endif
|
||||
|
||||
#define WarningAssert( _msg ) AssertMsg( false, _msg )
|
||||
|
||||
|
||||
#define STRINGS_MATCH 0
|
||||
#define IS_NOT_EMPTY( str ) ( (str) != nullptr && *(str) != nullchar )
|
||||
#define IS_EMPTY_STR( str ) ( (str) == nullptr || *(str) == nullchar )
|
||||
|
||||
#define IS_IN_RANGE( var, low, high ) ( (var) >= (low) && (var) <= (high) )
|
||||
#define IS_IN_RANGECOUNT( var, low, high ) ( (var) >= (low) && (var) < (high) )
|
||||
|
||||
#define IS_OUT_OF_RANGE( var, low, high ) ( (var) < (low) || (var) > (high) )
|
||||
#define IS_OUT_OF_RANGECOUNT( var, low, high ) ( (var) < (low) || (var) >= (high) )
|
||||
|
||||
#define BITFLAGS_SET( var, bits ) ( ( (var) & (bits) ) == (bits) )
|
||||
#define ANY_BITFLAGS_SET( var, bits ) ( ( (var) & (bits) ) != 0 )
|
||||
|
||||
#define MAKE_UINT64( highVal, lowVal ) ( ( (uint64) highVal << 32 ) | (uint64) lowVal )
|
||||
|
||||
#define CONTAINING_MULTIPLE_OF( var, multVal ) ( (var) + ( multVal - 1) ) - ( ( (var) - 1) % multVal )
|
||||
|
||||
// use this whenever we do address arithmetic in bytes
|
||||
typedef unsigned char* memaddr_t;
|
||||
typedef int32 memoffset_t;
|
||||
|
||||
|
||||
#endif // VIDEOMACROS_H
|
84
video/video_quicktime.vpc
Normal file
84
video/video_quicktime.vpc
Normal file
@ -0,0 +1,84 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// video_quicktime.vpc
|
||||
//
|
||||
// Project Script
|
||||
// Created by: Matt Pritchard
|
||||
//
|
||||
// Description: Quicktime video sub-system (for video services system)
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
// Win32 - need to point to Quicktime 7 for Win SDK directory so that dependant includes will work
|
||||
$AdditionalIncludeDirectories "$BASE,..\common\quicktime_win32\" [$WIN32]
|
||||
$AdditionalIncludeDirectories "$BASE;$SRCDIR\dx9sdk\include" [$WIN32]
|
||||
}
|
||||
$Linker
|
||||
{
|
||||
$IgnoreImportLibrary "Yes" [$WIN32]
|
||||
$AdditionalDependencies "$BASE vfw32.lib" [$WIN32]
|
||||
$SystemLibraries "iconv" [$OSXALL]
|
||||
$SystemFrameworks "Quicktime;Carbon" [$OSXALL]
|
||||
$AdditionalLibraryDirectories "$BASE;$SRCDIR\dx9sdk\lib" [$WIN32]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory "Debug_Video_Quicktime" [$WINDOWS]
|
||||
$IntermediateDirectory "Debug_Video_Quicktime" [$WINDOWS]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory "Release_Video_Quicktime" [$WINDOWS]
|
||||
$IntermediateDirectory "Release_Video_Quicktime" [$WINDOWS]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$Project "video_quicktime"
|
||||
{
|
||||
$Folder "Source Files" [$OSXALL||$WIN32]
|
||||
{
|
||||
$file "quicktime_video.cpp"
|
||||
$file "quicktime_material.cpp"
|
||||
$file "quicktime_recorder.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files" [$OSXALL||$WIN32]
|
||||
{
|
||||
$file "videosubsystem.h"
|
||||
$file "video_macros.h"
|
||||
$file "quicktime_common.h"
|
||||
$file "quicktime_video.h"
|
||||
$file "quicktime_material.h"
|
||||
$file "quicktime_recorder.h"
|
||||
$file "$SRCDIR\public\pixelwriter.h"
|
||||
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib tier2
|
||||
$File "$SRCDIR\lib\common\quicktime\QTMLClient.lib" [$WIN32]
|
||||
$File "$SRCDIR\DX9SDK\lib\dsound.lib" [$WIN32]
|
||||
$File "$SRCDIR\DX9SDK\lib\dxguid.lib" [$WIN32]
|
||||
|
||||
}
|
||||
}
|
||||
|
72
video/video_services.vpc
Normal file
72
video/video_services.vpc
Normal file
@ -0,0 +1,72 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// videoservices.vpc
|
||||
//
|
||||
// Project Script
|
||||
// Created by: Matt Pritchard
|
||||
//
|
||||
// Description: Centralized & abstracted cross-platform video services
|
||||
// Provides a central interface where the game can
|
||||
// handle video requests and query video capabilities
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Linker
|
||||
{
|
||||
$SystemLibraries "iconv" [$OSXALL]
|
||||
$SystemFrameworks "Carbon" [$OSXALL]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory "debug_video_services" [$WINDOWS]
|
||||
$IntermediateDirectory "debug_video_services" [$WINDOWS]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory "Release_video_services" [$WINDOWS]
|
||||
$IntermediateDirectory "Release_video_services" [$WINDOWS]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$Project "video_services"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$file "videoservices.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$file "video_macros.h"
|
||||
$file "videoservices.h"
|
||||
$file "videosubsystem.h"
|
||||
}
|
||||
|
||||
$Folder "Interface"
|
||||
{
|
||||
$File "$SRCDIR\public\video\ivideoservices.h"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib tier2
|
||||
$Lib tier3
|
||||
}
|
||||
}
|
||||
|
86
video/video_webm.vpc
Normal file
86
video/video_webm.vpc
Normal file
@ -0,0 +1,86 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// video_webm.vpc
|
||||
//
|
||||
// Project Script
|
||||
//
|
||||
// Description: WebM video sub-system (for video services system)
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE;$SRCDIR\dx9sdk\include" [$WIN32]
|
||||
$AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\libvpx-v1.1.0"
|
||||
$AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\libvorbis-1.3.2\include"
|
||||
$AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\libwebm"
|
||||
$AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\libogg-1.2.2\include"
|
||||
}
|
||||
$Linker
|
||||
{
|
||||
$IgnoreImportLibrary "Yes" [$WIN32]
|
||||
$AdditionalDependencies "$BASE vfw32.lib" [$WIN32]
|
||||
$SystemLibraries "iconv" [$OSXALL]
|
||||
$SystemFrameworks "Carbon" [$OSXALL]
|
||||
$AdditionalLibraryDirectories "$BASE;$SRCDIR\dx9sdk\lib" [$WIN32]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory "Debug_Video_WebM"
|
||||
$IntermediateDirectory "Debug_Video_WebM"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory "Release_Video_WebM"
|
||||
$IntermediateDirectory "Release_Video_WebM"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$Project "video_webm"
|
||||
{
|
||||
$Folder "Source Files" [$OSXALL||$WIN32||$LINUXALL]
|
||||
{
|
||||
$file "webm_video.cpp"
|
||||
// $file "webm_material.cpp"
|
||||
$file "webm_recorder.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files" [$OSXALL||$WIN32||$LINUXALL]
|
||||
{
|
||||
$file "videosubsystem.h"
|
||||
$file "video_macros.h"
|
||||
$file "webm_video.h"
|
||||
$file "webm_recorder.h"
|
||||
$file "webm_common.h"
|
||||
$file "$SRCDIR\public\pixelwriter.h"
|
||||
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib tier1
|
||||
$Lib tier2
|
||||
$Lib $LIBCOMMON/libvorbis
|
||||
$Lib $LIBCOMMON/libvpx
|
||||
$Lib $LIBCOMMON/libwebm
|
||||
$Lib $LIBCOMMON/libvorbisenc
|
||||
$Lib $LIBCOMMON/libogg
|
||||
}
|
||||
}
|
||||
|
1464
video/videoservices.cpp
Normal file
1464
video/videoservices.cpp
Normal file
File diff suppressed because it is too large
Load Diff
198
video/videoservices.h
Normal file
198
video/videoservices.h
Normal file
@ -0,0 +1,198 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// The copyright to the contents herein is the property of Valve, L.L.C.
|
||||
// The contents may be used and/or copied only with the written permission of
|
||||
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
|
||||
// the agreement/contract under which the contents have been supplied.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef VIDEOSERVICES_H
|
||||
#define VIDEOSERVICES_H
|
||||
|
||||
#if defined ( WIN32 )
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "video/ivideoservices.h"
|
||||
|
||||
#include "videosubsystem.h"
|
||||
|
||||
|
||||
struct CVideFileoExtInfo_t
|
||||
{
|
||||
const char *m_pExtension; // extension including "."
|
||||
VideoSystem_t m_VideoSystemSupporting;
|
||||
VideoSystemFeature_t m_VideoFeaturesSupporting;
|
||||
};
|
||||
|
||||
|
||||
struct CActiveVideoObjectRecord_t
|
||||
{
|
||||
void *m_pObject;
|
||||
int m_VideoSystem;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Main VIDEO_SERVICES interface
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CValveVideoServices : public CTier3AppSystem< IVideoServices >
|
||||
{
|
||||
typedef CTier3AppSystem< IVideoServices > BaseClass;
|
||||
|
||||
public:
|
||||
CValveVideoServices();
|
||||
~CValveVideoServices();
|
||||
|
||||
// Inherited from IAppSystem
|
||||
virtual bool Connect( CreateInterfaceFn factory );
|
||||
virtual void Disconnect();
|
||||
virtual void *QueryInterface( const char *pInterfaceName );
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Inherited from IVideoServices
|
||||
|
||||
// Query the available video systems
|
||||
virtual int GetAvailableVideoSystemCount();
|
||||
virtual VideoSystem_t GetAvailableVideoSystem( int n );
|
||||
|
||||
virtual bool IsVideoSystemAvailable( VideoSystem_t videoSystem );
|
||||
virtual VideoSystemStatus_t GetVideoSystemStatus( VideoSystem_t videoSystem );
|
||||
virtual VideoSystemFeature_t GetVideoSystemFeatures( VideoSystem_t videoSystem );
|
||||
virtual const char *GetVideoSystemName( VideoSystem_t videoSystem );
|
||||
|
||||
virtual VideoSystem_t FindNextSystemWithFeature( VideoSystemFeature_t features, VideoSystem_t startAfter = VideoSystem::NONE );
|
||||
|
||||
virtual VideoResult_t GetLastResult();
|
||||
|
||||
// deal with video file extensions and video system mappings
|
||||
virtual int GetSupportedFileExtensionCount( VideoSystem_t videoSystem );
|
||||
virtual const char *GetSupportedFileExtension( VideoSystem_t videoSystem, int extNum = 0 );
|
||||
virtual VideoSystemFeature_t GetSupportedFileExtensionFeatures( VideoSystem_t videoSystem, int extNum = 0 );
|
||||
|
||||
|
||||
virtual VideoSystem_t LocateVideoSystemForPlayingFile( const char *pFileName, VideoSystemFeature_t playMode = VideoSystemFeature::PLAY_VIDEO_FILE_IN_MATERIAL );
|
||||
virtual VideoResult_t LocatePlayableVideoFile( const char *pSearchFileName, const char *pPathID, VideoSystem_t *pPlaybackSystem, char *pPlaybackFileName, int fileNameMaxLen, VideoSystemFeature_t playMode = VideoSystemFeature::FULL_PLAYBACK );
|
||||
|
||||
// Create/destroy a video material
|
||||
virtual IVideoMaterial *CreateVideoMaterial( const char *pMaterialName, const char *pVideoFileName, const char *pPathID = nullptr,
|
||||
VideoPlaybackFlags_t playbackFlags = VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS,
|
||||
VideoSystem_t videoSystem = VideoSystem::DETERMINE_FROM_FILE_EXTENSION, bool PlayAlternateIfNotAvailable = true );
|
||||
|
||||
virtual VideoResult_t DestroyVideoMaterial( IVideoMaterial* pVideoMaterial );
|
||||
virtual int GetUniqueMaterialID();
|
||||
|
||||
// Create/destroy a video encoder
|
||||
virtual VideoResult_t IsRecordCodecAvailable( VideoSystem_t videoSystem, VideoEncodeCodec_t codec );
|
||||
|
||||
virtual IVideoRecorder *CreateVideoRecorder( VideoSystem_t videoSystem );
|
||||
virtual VideoResult_t DestroyVideoRecorder( IVideoRecorder *pVideoRecorder );
|
||||
|
||||
// Plays a given video file until it completes or the user presses ESC, SPACE, or ENTER
|
||||
virtual VideoResult_t PlayVideoFileFullScreen( const char *pFileName, const char *pPathID, void *mainWindow, int windowWidth, int windowHeight, int desktopWidth, int desktopHeight, bool windowed, float forcedMinTime,
|
||||
VideoPlaybackFlags_t playbackFlags = VideoPlaybackFlags::DEFAULT_FULLSCREEN_OPTIONS,
|
||||
VideoSystem_t videoSystem = VideoSystem::DETERMINE_FROM_FILE_EXTENSION, bool PlayAlternateIfNotAvailable = true );
|
||||
|
||||
// Sets the sound devices that the video will decode to
|
||||
virtual VideoResult_t SoundDeviceCommand( VideoSoundDeviceOperation_t operation, void *pDevice = nullptr, void *pData = nullptr, VideoSystem_t videoSystem = VideoSystem::ALL_VIDEO_SYSTEMS );
|
||||
|
||||
// Get the name of a codec as a string
|
||||
const wchar_t *GetCodecName( VideoEncodeCodec_t nCodec );
|
||||
|
||||
private:
|
||||
|
||||
VideoResult_t ResolveToPlayableVideoFile( const char *pFileName, const char *pPathID, VideoSystem_t videoSystem, VideoSystemFeature_t requiredFeature, bool PlayAlternateIfNotAvailable,
|
||||
char *pResolvedFileName, int resolvedFileNameMaxLen, VideoSystem_t *pResolvedVideoSystem );
|
||||
|
||||
|
||||
VideoSystem_t LocateSystemAndFeaturesForFileName( const char *pFileName, VideoSystemFeature_t *pFeatures = nullptr, VideoSystemFeature_t requiredFeatures = VideoSystemFeature::NO_FEATURES );
|
||||
|
||||
bool IsMatchAnyExtension( const char *pFileName );
|
||||
|
||||
bool ConnectVideoLibraries( CreateInterfaceFn factory );
|
||||
bool DisconnectVideoLibraries();
|
||||
|
||||
int DestroyAllVideoInterfaces();
|
||||
|
||||
int GetIndexForSystem( VideoSystem_t n );
|
||||
VideoSystem_t GetSystemForIndex( int n );
|
||||
|
||||
VideoResult_t SetResult( VideoResult_t resultCode );
|
||||
|
||||
const char *GetFileExtension( const char *pFileName );
|
||||
|
||||
|
||||
static const int SYSTEM_NOT_FOUND = -1;
|
||||
|
||||
VideoResult_t m_LastResult;
|
||||
|
||||
int m_nInstalledSystems;
|
||||
bool m_bInitialized;
|
||||
|
||||
CSysModule *m_VideoSystemModule[VideoSystem::VIDEO_SYSTEM_COUNT];
|
||||
IVideoSubSystem *m_VideoSystems[VideoSystem::VIDEO_SYSTEM_COUNT];
|
||||
VideoSystem_t m_VideoSystemType[VideoSystem::VIDEO_SYSTEM_COUNT];
|
||||
VideoSystemFeature_t m_VideoSystemFeatures[VideoSystem::VIDEO_SYSTEM_COUNT];
|
||||
|
||||
CUtlVector< VideoFileExtensionInfo_t > m_ExtInfo; // info about supported file extensions
|
||||
|
||||
CUtlVector< CActiveVideoObjectRecord_t > m_RecorderList;
|
||||
CUtlVector< CActiveVideoObjectRecord_t > m_MaterialList;
|
||||
|
||||
int m_nMaterialCount;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class CVideoCommonServices : public IVideoCommonServices
|
||||
{
|
||||
public:
|
||||
|
||||
CVideoCommonServices();
|
||||
~CVideoCommonServices();
|
||||
|
||||
|
||||
virtual bool CalculateVideoDimensions( int videoWidth, int videoHeight, int displayWidth, int displayHeight, VideoPlaybackFlags_t playbackFlags,
|
||||
int *pOutputWidth, int *pOutputHeight, int *pXOffset, int *pYOffset );
|
||||
|
||||
virtual float GetSystemVolume();
|
||||
|
||||
virtual VideoResult_t InitFullScreenPlaybackInputHandler( VideoPlaybackFlags_t playbackFlags, float forcedMinTime, bool windowed );
|
||||
|
||||
virtual bool ProcessFullScreenInput( bool &bAbortEvent, bool &bPauseEvent, bool &bQuitEvent );
|
||||
|
||||
virtual VideoResult_t TerminateFullScreenPlaybackInputHandler();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void ResetInputHandlerState();
|
||||
|
||||
bool m_bInputHandlerInitialized;
|
||||
|
||||
bool m_bScanAll;
|
||||
bool m_bScanEsc;
|
||||
bool m_bScanReturn;
|
||||
bool m_bScanSpace;
|
||||
bool m_bPauseEnabled;
|
||||
bool m_bAbortEnabled;
|
||||
bool m_bEscLast;
|
||||
bool m_bReturnLast;
|
||||
bool m_bSpaceLast;
|
||||
bool m_bForceMinPlayTime;
|
||||
|
||||
bool m_bWindowed;
|
||||
VideoPlaybackFlags_t m_playbackFlags;
|
||||
float m_forcedMinTime;
|
||||
|
||||
double m_StartTime;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // VIDEOSERVICES_H
|
97
video/videosubsystem.h
Normal file
97
video/videosubsystem.h
Normal file
@ -0,0 +1,97 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// The copyright to the contents herein is the property of Valve, L.L.C.
|
||||
// The contents may be used and/or copied only with the written permission of
|
||||
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
|
||||
// the agreement/contract under which the contents have been supplied.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef VIDEOSUBSYSTEM_H
|
||||
#define VIDEOSUBSYSTEM_H
|
||||
|
||||
#if defined ( WIN32 )
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier2/tier2.h"
|
||||
#include "appframework/IAppSystem.h"
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common structure used to store supported file types
|
||||
//-----------------------------------------------------------------------------
|
||||
struct VideoFileExtensionInfo_t
|
||||
{
|
||||
const char *m_FileExtension;
|
||||
VideoSystem_t m_VideoSubSystem;
|
||||
VideoSystemFeature_t m_VideoFeatures;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class IVideoCommonServices
|
||||
{
|
||||
public:
|
||||
virtual bool CalculateVideoDimensions( int videoWidth, int videoHeight, int displayWidth, int displayHeight, VideoPlaybackFlags_t playbackFlags,
|
||||
int *pOutputWidth, int *pOutputHeight, int *pXOffset, int *pYOffset ) = 0;
|
||||
|
||||
virtual float GetSystemVolume() = 0;
|
||||
|
||||
virtual VideoResult_t InitFullScreenPlaybackInputHandler( VideoPlaybackFlags_t playbackFlags, float forcedMinTime, bool windowed ) = 0;
|
||||
|
||||
virtual bool ProcessFullScreenInput( bool &bAbortEvent, bool &bPauseEvent, bool &bQuitEvent ) = 0;
|
||||
|
||||
virtual VideoResult_t TerminateFullScreenPlaybackInputHandler() = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Main VIDEO_SERVICES interface
|
||||
//-----------------------------------------------------------------------------
|
||||
#define VIDEO_SUBSYSTEM_INTERFACE_VERSION "IVideoSubSystem002"
|
||||
|
||||
class IVideoSubSystem : public IAppSystem
|
||||
{
|
||||
public:
|
||||
// SubSystem Identification functions
|
||||
virtual VideoSystem_t GetSystemID() = 0;
|
||||
virtual VideoSystemStatus_t GetSystemStatus() = 0;
|
||||
virtual VideoSystemFeature_t GetSupportedFeatures() = 0;
|
||||
virtual const char *GetVideoSystemName() = 0;
|
||||
|
||||
// Setup & Shutdown Services
|
||||
virtual bool InitializeVideoSystem( IVideoCommonServices *pCommonServices ) = 0;
|
||||
virtual bool ShutdownVideoSystem() = 0;
|
||||
|
||||
virtual VideoResult_t VideoSoundDeviceCMD( VideoSoundDeviceOperation_t operation, void *pDevice, void *pData = nullptr ) = 0;
|
||||
|
||||
// get list of file extensions and features we support
|
||||
virtual int GetSupportedFileExtensionCount() = 0;
|
||||
virtual const char *GetSupportedFileExtension( int num ) = 0;
|
||||
virtual VideoSystemFeature_t GetSupportedFileExtensionFeatures( int num ) = 0;
|
||||
|
||||
// Video Playback and Recording Services
|
||||
|
||||
virtual VideoResult_t PlayVideoFileFullScreen( const char *filename, void *mainWindow, int windowWidth, int windowHeight, int desktopWidth, int desktopHeight, bool windowed, float forcedMinTime, VideoPlaybackFlags_t playbackFlags ) = 0;
|
||||
|
||||
// Create/destroy a video material
|
||||
virtual IVideoMaterial *CreateVideoMaterial( const char *pMaterialName, const char *pVideoFileName, VideoPlaybackFlags_t flags ) = 0;
|
||||
virtual VideoResult_t DestroyVideoMaterial( IVideoMaterial* pVideoMaterial ) = 0;
|
||||
|
||||
// Create/destroy a video encoder
|
||||
virtual IVideoRecorder *CreateVideoRecorder() = 0;
|
||||
virtual VideoResult_t DestroyVideoRecorder( IVideoRecorder *pRecorder ) = 0;
|
||||
|
||||
virtual VideoResult_t CheckCodecAvailability( VideoEncodeCodec_t codec ) = 0;
|
||||
|
||||
virtual VideoResult_t GetLastResult() = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
48
video/webm_common.h
Normal file
48
video/webm_common.h
Normal file
@ -0,0 +1,48 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// File: webm_common.h
|
||||
//
|
||||
// WebM limits and constants shared among all QuickTime functions
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#ifndef WEBM_COMMON_H
|
||||
#define WEBM_COMMON_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// constant that define the bounds of various inputs
|
||||
static const int cMinVideoFrameWidth = 16;
|
||||
static const int cMinVideoFrameHeight = 16;
|
||||
static const int cMaxVideoFrameWidth = 2 * 2048;
|
||||
static const int cMaxVideoFrameHeight = 2 * 2048;
|
||||
|
||||
static const int cMinFPS = 1;
|
||||
static const int cMaxFPS = 600;
|
||||
|
||||
static const float cMinDuration = 0.016666666f; // 1/60th second
|
||||
static const float cMaxDuration = 3600.0f; // 1 Hour
|
||||
|
||||
static const int cMinSampleRate = 11025; // 1/4 CD sample rate
|
||||
static const int cMaxSampleRate = 88200; // 2x CD rate
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
#endif // WEBM_COMMON_H
|
962
video/webm_material.cpp
Normal file
962
video/webm_material.cpp
Normal file
@ -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 <WinDef.h>
|
||||
#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 );
|
||||
}
|
||||
|
||||
|
221
video/webm_material.h
Normal file
221
video/webm_material.h
Normal file
@ -0,0 +1,221 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#ifndef QUICKTIME_MATERIAL_H
|
||||
#define QUICKTIME_MATERIAL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class IFileSystem;
|
||||
class IMaterialSystem;
|
||||
class CQuickTimeMaterial;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Global interfaces - you already did the needed includes, right?
|
||||
//-----------------------------------------------------------------------------
|
||||
extern IFileSystem *g_pFileSystem;
|
||||
extern IMaterialSystem *materials;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Quicktime includes
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined ( OSX )
|
||||
#include <quicktime/QTML.h>
|
||||
#include <quicktime/Movies.h>
|
||||
#include <quicktime/MediaHandlers.h>
|
||||
#elif defined ( WIN32 )
|
||||
#include <QTML.h>
|
||||
#include <Movies.h>
|
||||
#include <windows.h>
|
||||
#include <MediaHandlers.h>
|
||||
#elif
|
||||
#error "Quicktime not supported on this target platform"
|
||||
#endif
|
||||
|
||||
|
||||
#include "video/ivideoservices.h"
|
||||
|
||||
#include "video_macros.h"
|
||||
#include "quicktime_common.h"
|
||||
|
||||
#include "materialsystem/itexture.h"
|
||||
#include "materialsystem/imaterialsystem.h"
|
||||
#include "materialsystem/MaterialSystemUtil.h"
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Texture regenerator - callback to get new movie pixels into the texture
|
||||
// -----------------------------------------------------------------------------
|
||||
class CQuicktimeMaterialRGBTextureRegenerator : public ITextureRegenerator
|
||||
{
|
||||
public:
|
||||
CQuicktimeMaterialRGBTextureRegenerator();
|
||||
~CQuicktimeMaterialRGBTextureRegenerator();
|
||||
|
||||
void SetSourceGWorld( GWorldPtr theGWorld, int nWidth, int nHeight );
|
||||
|
||||
// Inherited from ITextureRegenerator
|
||||
virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect );
|
||||
virtual void Release();
|
||||
|
||||
private:
|
||||
GWorldPtr m_SrcGWorld;
|
||||
int m_nSourceWidth;
|
||||
int m_nSourceHeight;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Class used to play a QuickTime video onto a texture
|
||||
// -----------------------------------------------------------------------------
|
||||
class CQuickTimeMaterial : public IVideoMaterial
|
||||
{
|
||||
public:
|
||||
CQuickTimeMaterial();
|
||||
~CQuickTimeMaterial();
|
||||
|
||||
static const int MAX_QT_FILENAME_LEN = 255;
|
||||
static const int MAX_MATERIAL_NAME_LEN = 255;
|
||||
static const int TEXTURE_SIZE_ALIGNMENT = 8;
|
||||
|
||||
// Initializes, shuts down the material
|
||||
bool Init( const char *pMaterialName, const char *pFileName, VideoPlaybackFlags_t flags );
|
||||
void Shutdown();
|
||||
|
||||
// Video information functions
|
||||
virtual const char *GetVideoFileName(); // Gets the file name of the video this material is playing
|
||||
virtual VideoResult_t GetLastResult(); // Gets detailed info on the last operation
|
||||
|
||||
virtual VideoFrameRate_t &GetVideoFrameRate(); // Returns the frame rate of the associated video in FPS
|
||||
|
||||
// Audio Functions
|
||||
virtual bool HasAudio(); // Query if the video has an audio track
|
||||
|
||||
virtual bool SetVolume( float fVolume ); // Adjust the playback volume
|
||||
virtual float GetVolume(); // Query the current volume
|
||||
virtual void SetMuted( bool bMuteState ); // Mute/UnMutes the audio playback
|
||||
virtual bool IsMuted(); // Query muted status
|
||||
|
||||
virtual VideoResult_t SoundDeviceCommand( VideoSoundDeviceOperation_t operation, void *pDevice = nullptr, void *pData = nullptr ); // Assign Sound Device for this Video Material
|
||||
|
||||
// Video playback state functions
|
||||
virtual bool IsVideoReadyToPlay(); // Queries if the video material was initialized successfully and is ready for playback, but not playing or finished
|
||||
virtual bool IsVideoPlaying(); // Is the video currently playing (and needs update calls, etc)
|
||||
virtual bool IsNewFrameReady(); // Do we have a new frame to get & display?
|
||||
virtual bool IsFinishedPlaying(); // Have we reached the end of the movie
|
||||
|
||||
virtual bool StartVideo(); // Starts the video playing
|
||||
virtual bool StopVideo(); // Terminates the video playing
|
||||
|
||||
virtual void SetLooping( bool bLoopVideo ); // Sets the video to loop (or not)
|
||||
virtual bool IsLooping(); // Queries if the video is looping
|
||||
|
||||
virtual void SetPaused( bool bPauseState ); // Pauses or Unpauses video playback
|
||||
virtual bool IsPaused(); // Queries if the video is paused
|
||||
|
||||
// Position in playback functions
|
||||
virtual float GetVideoDuration(); // Returns the duration of the associated video in seconds
|
||||
virtual int GetFrameCount(); // Returns the total number of (unique) frames in the video
|
||||
|
||||
virtual bool SetFrame( int FrameNum ); // Sets the current frame # in the video to play next
|
||||
virtual int GetCurrentFrame(); // Gets the current frame # for the video playback, 0 Based
|
||||
|
||||
virtual bool SetTime( float flTime ); // Sets the video playback to specified time (in seconds)
|
||||
virtual float GetCurrentVideoTime(); // Gets the current time in the video playback
|
||||
|
||||
// Update function
|
||||
virtual bool Update(); // Updates the video frame to reflect the time passed, true = new frame available
|
||||
|
||||
// Material / Texture Info functions
|
||||
virtual IMaterial *GetMaterial(); // Gets the IMaterial associated with an video material
|
||||
|
||||
virtual void GetVideoTexCoordRange( float *pMaxU, float *pMaxV ) ; // Returns the max texture coordinate of the video portion of the material surface ( 0.0, 0.0 to U, V )
|
||||
virtual void GetVideoImageSize( int *pWidth, int *pHeight ); // Returns the frame size of the Video Image Frame in pixels ( the stored in a subrect of the material itself)
|
||||
|
||||
|
||||
|
||||
private:
|
||||
friend class CQuicktimeMaterialRGBTextureRegenerator;
|
||||
|
||||
void Reset(); // clears internal state
|
||||
void SetQTFileName( const char *theQTMovieFileName );
|
||||
VideoResult_t SetResult( VideoResult_t status );
|
||||
|
||||
// Initializes, shuts down the video stream
|
||||
void OpenQTMovie( const char *theQTMovieFileName );
|
||||
void CloseQTFile();
|
||||
|
||||
// Initializes, shuts down the procedural texture
|
||||
void CreateProceduralTexture( const char *pTextureName );
|
||||
void DestroyProceduralTexture();
|
||||
|
||||
// Initializes, shuts down the procedural material
|
||||
void CreateProceduralMaterial( const char *pMaterialName );
|
||||
void DestroyProceduralMaterial();
|
||||
|
||||
CQuicktimeMaterialRGBTextureRegenerator m_TextureRegen;
|
||||
|
||||
VideoResult_t m_LastResult;
|
||||
|
||||
CMaterialReference m_Material; // Ref to Material used for rendering the video frame
|
||||
CTextureReference m_Texture; // Ref to the renderable texture which contains the most recent video frame (in a sub-rect)
|
||||
|
||||
float m_TexCordU; // Max U texture coordinate of the texture sub-rect which holds the video frame
|
||||
float m_TexCordV; // Max V texture coordinate of the texture sub-rect which holds the video frame
|
||||
|
||||
int m_VideoFrameWidth; // Size of the movie frame in pixels
|
||||
int m_VideoFrameHeight;
|
||||
|
||||
char *m_pFileName; // resolved filename of the movie being played
|
||||
VideoPlaybackFlags_t m_PlaybackFlags; // option flags user supplied
|
||||
|
||||
bool m_bInitCalled;
|
||||
bool m_bMovieInitialized;
|
||||
bool m_bMoviePlaying;
|
||||
bool m_bMovieFinishedPlaying;
|
||||
bool m_bMoviePaused;
|
||||
bool m_bLoopMovie;
|
||||
|
||||
bool m_bHasAudio;
|
||||
bool m_bMuted;
|
||||
|
||||
float m_CurrentVolume;
|
||||
|
||||
// QuickTime Stuff
|
||||
Movie m_QTMovie;
|
||||
|
||||
TimeScale m_QTMovieTimeScale; // Units per second
|
||||
TimeValue m_QTMovieDuration; // movie duration in TimeScale Units Per Second
|
||||
float m_QTMovieDurationinSec; // movie duration in seconds
|
||||
VideoFrameRate_t m_QTMovieFrameRate; // Frame Rate of movie
|
||||
int m_QTMovieFrameCount;
|
||||
|
||||
Rect m_QTMovieRect;
|
||||
GWorldPtr m_MovieGWorld;
|
||||
|
||||
QTAudioContextRef m_AudioContext;
|
||||
|
||||
TimeValue m_MovieFirstFrameTime;
|
||||
TimeValue m_NextInterestingTimeToPlay;
|
||||
TimeValue m_MoviePauseTime;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // QUICKTIME_MATERIAL_H
|
811
video/webm_recorder.cpp
Normal file
811
video/webm_recorder.cpp
Normal file
@ -0,0 +1,811 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include "webm_video.h"
|
||||
#include "webm_recorder.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Bitrate table for various resolutions, used to compute estimated size of files as well
|
||||
// as to set the WebM codec.
|
||||
|
||||
struct WebMEncodingDataRateInfo_t
|
||||
{
|
||||
int m_XResolution;
|
||||
int m_YResolution;
|
||||
float m_MinDataRate; // in KBits / second
|
||||
float m_MaxDataRate; // in KBits / second
|
||||
float m_MinAudioDataRate; // in KBits / second
|
||||
float m_MaxAudioDataRate; // in KBits / second
|
||||
};
|
||||
|
||||
// Quality is passed into us as a number between 0 and 100, we use that to scale between the min and max bitrates.
|
||||
static WebMEncodingDataRateInfo_t s_WebMEncodeRates[] =
|
||||
{
|
||||
{ 320, 240, 1000, 1000, 128, 128},
|
||||
{ 640, 960, 2000, 2000, 128, 128},
|
||||
{ 960, 640, 2000, 2000, 128, 128},
|
||||
{ 720, 480, 2500, 2500, 128, 128},
|
||||
{1280, 720, 5000, 5000, 384, 384},
|
||||
{1920, 1080, 8000, 8000, 384, 384},
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// CWebMVideoRecorder class - implements IVideoRecorder interface for
|
||||
// WebM and buffers commands to the actual encoder object
|
||||
// ===========================================================================
|
||||
CWebMVideoRecorder::CWebMVideoRecorder() :
|
||||
m_LastResult( VideoResult::SUCCESS ),
|
||||
m_bHasAudio( false ),
|
||||
m_bMovieFinished( false ),
|
||||
m_SrcImageYV12Buffer( NULL ),
|
||||
m_nFramesAdded( 0 ),
|
||||
m_nAudioFramesAdded( 0 ),
|
||||
m_nSamplesAdded( 0 ),
|
||||
m_audioChannels( 2 ),
|
||||
m_audioSampleRate( 44100 ),
|
||||
m_audioBitDepth( 16 ),
|
||||
m_audioSampleGroupSize( 0 )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::CWebMVideoRecorder() \n");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CWebMVideoRecorder::~CWebMVideoRecorder()
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::~CWebMVideoRecorder() \n");
|
||||
#endif
|
||||
if (m_SrcImageYV12Buffer)
|
||||
{
|
||||
vpx_img_free(m_SrcImageYV12Buffer);
|
||||
m_SrcImageYV12Buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// All webm movies use 1000000 as a scale
|
||||
#define WEBM_TIMECODE_SCALE 1000000
|
||||
// All webm movies using 128kbps as audio bitrate
|
||||
#define WEBM_AUDIO_BITRATE 128000
|
||||
|
||||
|
||||
bool CWebMVideoRecorder::CreateNewMovieFile( const char *pFilename, bool hasAudio )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::CreateNewMovieFile() \n");
|
||||
#endif
|
||||
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
AssertExitF( IS_NOT_EMPTY( pFilename ) );
|
||||
|
||||
SetResult( VideoResult::OPERATION_ALREADY_PERFORMED );
|
||||
|
||||
// crete the webm file
|
||||
if (!m_mkvWriter.Open(pFilename))
|
||||
{
|
||||
SetResult( VideoResult::OPERATION_ALREADY_PERFORMED );
|
||||
return false;
|
||||
}
|
||||
|
||||
// init the webm file and write out the header
|
||||
m_mkvMuxerSegment.Init(&m_mkvWriter);
|
||||
m_mkvMuxerSegment.set_mode(mkvmuxer::Segment::kFile);
|
||||
m_mkvMuxerSegment.OutputCues(true);
|
||||
|
||||
|
||||
mkvmuxer::SegmentInfo* const mkvInfo = m_mkvMuxerSegment.GetSegmentInfo();
|
||||
mkvInfo->set_timecode_scale(WEBM_TIMECODE_SCALE);
|
||||
|
||||
// get the app name
|
||||
char appname[ 256 ];
|
||||
KeyValues *modinfo = new KeyValues( "ModInfo" );
|
||||
|
||||
if ( modinfo->LoadFromFile( g_pFullFileSystem, "gameinfo.txt" ) )
|
||||
Q_strncpy( appname, modinfo->GetString( "game" ), sizeof( appname ) );
|
||||
else
|
||||
Q_strncpy( appname, "Source1 Game", sizeof( appname ) );
|
||||
|
||||
modinfo->deleteThis();
|
||||
modinfo = NULL;
|
||||
|
||||
mkvInfo->set_writing_app(appname);
|
||||
|
||||
m_bHasAudio = hasAudio;
|
||||
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoRecorder::SetMovieVideoParameters( VideoEncodeCodec_t theCodec, int videoQuality, int movieFrameWidth, int movieFrameHeight, VideoFrameRate_t movieFPS, VideoEncodeGamma_t gamma )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::SetMovieVideoParameters()\n");
|
||||
#endif
|
||||
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
AssertExitF( IS_IN_RANGECOUNT( theCodec, VideoEncodeCodec::DEFAULT_CODEC, VideoEncodeCodec::CODEC_COUNT ) );
|
||||
AssertExitF( IS_IN_RANGE( videoQuality, VideoEncodeQuality::MIN_QUALITY, VideoEncodeQuality::MAX_QUALITY ) );
|
||||
AssertExitF( IS_IN_RANGE( movieFrameWidth, cMinVideoFrameWidth, cMaxVideoFrameWidth ) && IS_IN_RANGE( movieFrameHeight, cMinVideoFrameHeight, cMaxVideoFrameHeight ) );
|
||||
AssertExitF( IS_IN_RANGE( movieFPS.GetFPS(), cMinFPS, cMaxFPS ) );
|
||||
AssertExitF( IS_IN_RANGECOUNT( gamma, VideoEncodeGamma::NO_GAMMA_ADJUST, VideoEncodeGamma::GAMMA_COUNT ) );
|
||||
|
||||
SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE );
|
||||
|
||||
// Get the defaults for the WebM encoder
|
||||
if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &m_vpxConfig, 0) != VPX_CODEC_OK)
|
||||
{
|
||||
SetResult( VideoResult::INITIALIZATION_ERROR_OCCURED );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_MovieFrameWidth = movieFrameWidth;
|
||||
m_MovieFrameHeight = movieFrameHeight;
|
||||
|
||||
m_MovieGamma = gamma;
|
||||
|
||||
m_vpxConfig.g_h = movieFrameHeight;
|
||||
m_vpxConfig.g_w = movieFrameWidth;
|
||||
|
||||
// How many threads should we use? How about 1 per logical processor
|
||||
const CPUInformation *pi = GetCPUInformation();
|
||||
m_vpxConfig.g_threads = pi->m_nLogicalProcessors;
|
||||
|
||||
// FPS
|
||||
m_vpxConfig.g_timebase.den = movieFPS.GetUnitsPerSecond();
|
||||
m_vpxConfig.g_timebase.num = movieFPS.GetUnitsPerFrame();
|
||||
|
||||
m_MovieRecordFPS = movieFPS;
|
||||
|
||||
m_DurationPerFrame = m_MovieRecordFPS.GetUnitsPerFrame();
|
||||
m_MovieTimeScale = m_MovieRecordFPS.GetUnitsPerSecond();
|
||||
|
||||
// Compute how long each frame is, in nanoseconds
|
||||
m_FrameDuration = (unsigned long)(((double)m_DurationPerFrame*(double)1000000000)/(double)m_MovieTimeScale);
|
||||
|
||||
// Set the bitrate for this size and level of quality
|
||||
m_vpxConfig.rc_target_bitrate = GetVideoDataRate(videoQuality, movieFrameWidth, movieFrameHeight);
|
||||
|
||||
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg( "Video Frame Rate = %f FPS\n %d time units per second\n %d time units per frame\n", m_MovieRecordFPS.GetFPS(), m_MovieRecordFPS.GetUnitsPerSecond(), m_MovieRecordFPS.GetUnitsPerFrame() );
|
||||
if ( m_MovieRecordFPS.IsNTSCRate() )
|
||||
Msg( " IS CONSIDERED NTSC RATE\n");
|
||||
Msg( "Using %d threads for WebM encoding\n", m_vpxConfig.g_threads);
|
||||
Msg( "MovieTimeScale is being set to %d\nDuration Per Frame is %d\n", m_MovieTimeScale, m_DurationPerFrame );
|
||||
Msg( "Time per frame in nanoseconds %d\n\n", m_FrameDuration);
|
||||
|
||||
#endif
|
||||
|
||||
// Init the codec
|
||||
if (vpx_codec_enc_init(&m_vpxContext, vpx_codec_vp8_cx(), &m_vpxConfig, 0) != VPX_CODEC_OK)
|
||||
{
|
||||
SetResult( VideoResult::INITIALIZATION_ERROR_OCCURED );
|
||||
return false;
|
||||
}
|
||||
|
||||
// add the video track
|
||||
m_vid_track = m_mkvMuxerSegment.AddVideoTrack(static_cast<int>(m_vpxConfig.g_w),
|
||||
static_cast<int>(m_vpxConfig.g_h),
|
||||
1);
|
||||
|
||||
mkvmuxer::VideoTrack* const video =
|
||||
static_cast<mkvmuxer::VideoTrack*>(
|
||||
m_mkvMuxerSegment.GetTrackByNumber(m_vid_track));
|
||||
|
||||
video->set_display_width(m_vpxConfig.g_w);
|
||||
video->set_display_height(m_vpxConfig.g_h);
|
||||
video->set_frame_rate(movieFPS.GetFPS());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoRecorder::SetMovieSourceImageParameters( VideoEncodeSourceFormat_t srcImageFormat, int imgWidth, int imgHeight )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::SetMovieSourceImageParameters()\n");
|
||||
#endif
|
||||
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
AssertExitF( IS_IN_RANGECOUNT( srcImageFormat, VideoEncodeSourceFormat::VIDEO_FORMAT_FIRST, VideoEncodeSourceFormat::VIDEO_FORMAT_COUNT ) );
|
||||
// WebM Recorder only supports BGRA_32BIT and BGR_24BIT
|
||||
AssertExitF( (srcImageFormat == VideoEncodeSourceFormat::BGRA_32BIT) ||
|
||||
(srcImageFormat == VideoEncodeSourceFormat::BGR_24BIT) );
|
||||
|
||||
AssertExitF( IS_IN_RANGE( imgWidth, cMinVideoFrameWidth, cMaxVideoFrameWidth ) && IS_IN_RANGE( imgHeight, cMinVideoFrameHeight, cMaxVideoFrameHeight ) );
|
||||
|
||||
SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE );
|
||||
|
||||
m_SrcImageWidth = imgWidth;
|
||||
m_SrcImageHeight = imgHeight;
|
||||
m_SrcImageFormat = srcImageFormat;
|
||||
|
||||
// Setup the buffers for encoding the frame. WebM requires a frame in YV12 format
|
||||
m_SrcImageYV12Buffer = vpx_img_alloc(NULL, VPX_IMG_FMT_YV12, m_SrcImageWidth, m_SrcImageHeight, 1);
|
||||
|
||||
// Set Cues element attributes
|
||||
mkvmuxer::Cues* const cues = m_mkvMuxerSegment.GetCues();
|
||||
cues->set_output_block_number(1);
|
||||
m_mkvMuxerSegment.CuesTrack(m_vid_track);
|
||||
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoRecorder::SetMovieSourceAudioParameters( AudioEncodeSourceFormat_t srcAudioFormat, int audioSampleRate, AudioEncodeOptions_t audioOptions, int audioSampleGroupSize )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::SetMovieSourceAudioParameters()\n");
|
||||
#endif
|
||||
|
||||
SetResult( VideoResult::ILLEGAL_OPERATION );
|
||||
AssertExitF( m_bHasAudio );
|
||||
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
AssertExitF( IS_IN_RANGECOUNT( srcAudioFormat, AudioEncodeSourceFormat::AUDIO_NONE, AudioEncodeSourceFormat::AUDIO_FORMAT_COUNT ) );
|
||||
AssertExitF( audioSampleRate == 0 || IS_IN_RANGE( audioSampleRate, cMinSampleRate, cMaxSampleRate ) );
|
||||
|
||||
SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE );
|
||||
|
||||
m_audioSampleRate = audioSampleRate;
|
||||
m_audioSampleGroupSize = audioSampleGroupSize;
|
||||
|
||||
// now setup the audio
|
||||
vorbis_info_init(&m_vi);
|
||||
|
||||
// We are always using 128kbps for the audio
|
||||
int ret=vorbis_encode_init(&m_vi,m_audioChannels,m_audioSampleRate,-1,WEBM_AUDIO_BITRATE,-1);
|
||||
if (ret)
|
||||
{
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
return false;
|
||||
}
|
||||
/* set up the analysis state and auxiliary encoding storage */
|
||||
vorbis_comment_init(&m_vc);
|
||||
|
||||
// get the app name
|
||||
char appname[ 256 ];
|
||||
KeyValues *modinfo = new KeyValues( "ModInfo" );
|
||||
|
||||
if ( modinfo->LoadFromFile( g_pFullFileSystem, "gameinfo.txt" ) )
|
||||
Q_strncpy( appname, modinfo->GetString( "game" ), sizeof( appname ) );
|
||||
else
|
||||
Q_strncpy( appname, "Source1 Game", sizeof( appname ) );
|
||||
|
||||
modinfo->deleteThis();
|
||||
modinfo = NULL;
|
||||
|
||||
vorbis_comment_add_tag(&m_vc,"ENCODER",appname);
|
||||
|
||||
vorbis_analysis_init(&m_vd,&m_vi);
|
||||
vorbis_block_init(&m_vd,&m_vb);
|
||||
|
||||
// setup the audio track
|
||||
m_aud_track = m_mkvMuxerSegment.AddAudioTrack(static_cast<int>(m_audioSampleRate),
|
||||
static_cast<int>(m_audioChannels),
|
||||
0);
|
||||
if (!m_aud_track)
|
||||
{
|
||||
printf("\n Could not add audio track.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
mkvmuxer::AudioTrack* const audio =
|
||||
static_cast<mkvmuxer::AudioTrack*>(
|
||||
m_mkvMuxerSegment.GetTrackByNumber(m_aud_track));
|
||||
if (!audio)
|
||||
{
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Vorbis streams begin with three headers; the initial header (with
|
||||
most of the codec setup parameters) which is mandated by the Ogg
|
||||
bitstream spec. The second header holds any comment fields. The
|
||||
third header holds the bitstream codebook. We merely need to
|
||||
make the headers, then pass them to libvorbis one at a time;
|
||||
libvorbis handles the additional Ogg bitstream constraints */
|
||||
|
||||
{
|
||||
ogg_packet ident_packet;
|
||||
ogg_packet comments_packet;
|
||||
ogg_packet setup_packet;
|
||||
int iHeaderLength;
|
||||
uint8 *privateHeader=NULL;
|
||||
uint8 *pbPrivateHeader=NULL;
|
||||
|
||||
vorbis_analysis_headerout(&m_vd,&m_vc,&ident_packet,&comments_packet,&setup_packet);
|
||||
iHeaderLength = 3 + ident_packet.bytes + comments_packet.bytes + setup_packet.bytes;
|
||||
privateHeader = new uint8[iHeaderLength];
|
||||
pbPrivateHeader = privateHeader;
|
||||
|
||||
*pbPrivateHeader++ = 2; // number of headers - 1
|
||||
*pbPrivateHeader++ = (uint8)ident_packet.bytes;
|
||||
*pbPrivateHeader++ = (uint8)comments_packet.bytes;
|
||||
|
||||
memcpy(pbPrivateHeader, ident_packet.packet, ident_packet.bytes);
|
||||
pbPrivateHeader+= ident_packet.bytes;
|
||||
|
||||
memcpy(pbPrivateHeader, comments_packet.packet, comments_packet.bytes);
|
||||
pbPrivateHeader+= comments_packet.bytes;
|
||||
|
||||
memcpy(pbPrivateHeader, setup_packet.packet, setup_packet.bytes);
|
||||
pbPrivateHeader+= setup_packet.bytes;
|
||||
|
||||
audio->SetCodecPrivate(privateHeader,iHeaderLength);
|
||||
delete [] privateHeader;
|
||||
}
|
||||
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoRecorder::IsReadyToRecord()
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::IsReadyToRecord()\n");
|
||||
#endif
|
||||
|
||||
return ( m_SrcImageYV12Buffer != NULL && !m_bMovieFinished);
|
||||
}
|
||||
|
||||
|
||||
VideoResult_t CWebMVideoRecorder::GetLastResult()
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::GetLastResult()\n");
|
||||
#endif
|
||||
|
||||
return m_LastResult;
|
||||
}
|
||||
|
||||
|
||||
void CWebMVideoRecorder::SetResult( VideoResult_t resultCode )
|
||||
{
|
||||
m_LastResult = resultCode;
|
||||
}
|
||||
|
||||
void CWebMVideoRecorder::ConvertBGRAToYV12( void *pFrameBuffer, int nStrideAdjustBytes, vpx_image_t *m_SrcImageYV12Buffer, bool fIncludesAlpha )
|
||||
{
|
||||
int iSrcBytesPerPixel;
|
||||
|
||||
// Handle 32-bit with alpha and 24-bit
|
||||
if (fIncludesAlpha)
|
||||
iSrcBytesPerPixel = 4;
|
||||
else
|
||||
iSrcBytesPerPixel = 3;
|
||||
|
||||
int srcStride = m_SrcImageWidth * iSrcBytesPerPixel + nStrideAdjustBytes;
|
||||
int iX,iY;
|
||||
byte *pSrc;
|
||||
byte *pDstY,*pDstU,*pDstV;
|
||||
byte r,g,b,a;
|
||||
byte y,u,v;
|
||||
|
||||
// This isn't fast or good, but it works and that's good enough for a first pass
|
||||
pSrc = (byte *)pFrameBuffer;
|
||||
|
||||
// YV12 has a complete frame of Y, followed by half sized U and V
|
||||
pDstY = m_SrcImageYV12Buffer->planes[0];
|
||||
pDstU = m_SrcImageYV12Buffer->planes[1];
|
||||
pDstV = m_SrcImageYV12Buffer->planes[2];
|
||||
|
||||
|
||||
for (iY=0;iY<m_MovieFrameHeight;iY++)
|
||||
{
|
||||
for(iX=0;iX<m_MovieFrameWidth;iX++)
|
||||
{
|
||||
b = pSrc[iSrcBytesPerPixel*iX+0];
|
||||
g = pSrc[iSrcBytesPerPixel*iX+1];
|
||||
r = pSrc[iSrcBytesPerPixel*iX+2];
|
||||
if (fIncludesAlpha)
|
||||
a = pSrc[iSrcBytesPerPixel*iX+3];
|
||||
|
||||
y = (byte)((66*r + 129*g + 25*b + 128) >> 8) + 16;
|
||||
|
||||
pDstY[iX] = y;
|
||||
if ((iY%2 == 0) && (iX%2 == 0))
|
||||
{
|
||||
u = (byte)((-38*r - 74*g + 112*b + 128) >> 8) + 128;
|
||||
v = (byte)((112*r - 94*g - 18*b + 128) >> 8) + 128;
|
||||
|
||||
pDstU[iX/2] = u;
|
||||
pDstV[iX/2] = v;
|
||||
}
|
||||
}
|
||||
// next row, using strides
|
||||
pDstY += m_SrcImageYV12Buffer->stride[0];
|
||||
if ((iY%2) == 0)
|
||||
{
|
||||
pDstU += m_SrcImageYV12Buffer->stride[1];
|
||||
pDstV += m_SrcImageYV12Buffer->stride[2];
|
||||
}
|
||||
pSrc += srcStride;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CWebMVideoRecorder::AppendVideoFrame( void *pFrameBuffer, int nStrideAdjustBytes )
|
||||
{
|
||||
uint64 time_ns;
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::AppendVideoFrame()\n");
|
||||
#endif
|
||||
|
||||
// $$$DEBUG Dump the Frame $$$DEBUG
|
||||
// If you are trying to debug how the webm libraries work it's very difficult to use the full TF2 replay system as a testbed
|
||||
// so this section of code here and in the AppendAudioSamples writes out the data TF2 would send to the video recorder to
|
||||
// plain files on the disc. You can then use those files with an extracted framework to work with the webm libraries
|
||||
#ifdef NEVER
|
||||
{
|
||||
FILE *fp=NULL;
|
||||
char rgch[256];
|
||||
int i,j;
|
||||
byte *pByte;
|
||||
|
||||
sprintf(rgch, "./frames/vid_%d", m_nFramesAdded);
|
||||
fp = fopen(rgch, "wb");
|
||||
|
||||
pByte = (byte *)pFrameBuffer;
|
||||
for (i=0;i<m_MovieFrameHeight;i++)
|
||||
{
|
||||
fwrite(pByte, 4, m_MovieFrameWidth, fp);
|
||||
pByte += (m_MovieFrameWidth*4 + nStrideAdjustBytes);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
AssertExitF( pFrameBuffer != nullptr );
|
||||
|
||||
SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE );
|
||||
AssertExitF( IsReadyToRecord() );
|
||||
|
||||
// Convert the frame in pFrameBuffer into YV12 and add it to the stream
|
||||
// only convert BGRA_32BIT and BGR_24BIT right now
|
||||
switch(m_SrcImageFormat)
|
||||
{
|
||||
case VideoEncodeSourceFormat::BGR_24BIT:
|
||||
ConvertBGRAToYV12(pFrameBuffer, nStrideAdjustBytes, m_SrcImageYV12Buffer, false);
|
||||
break;
|
||||
case VideoEncodeSourceFormat::BGRA_32BIT:
|
||||
ConvertBGRAToYV12(pFrameBuffer, nStrideAdjustBytes, m_SrcImageYV12Buffer, true);
|
||||
break;
|
||||
default:
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Compress it with the webm codec
|
||||
|
||||
time_ns = ((uint64)m_FrameDuration*(uint64)(m_nFramesAdded+1));
|
||||
|
||||
vpx_codec_err_t vpxError = vpx_codec_encode(&m_vpxContext, m_SrcImageYV12Buffer, time_ns, m_FrameDuration, 0, 0);
|
||||
|
||||
if (vpxError != VPX_CODEC_OK)
|
||||
{
|
||||
SetResult( VideoResult::VIDEO_ERROR_OCCURED );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add this frame to the stream
|
||||
vpx_codec_iter_t vpxIter = NULL;
|
||||
const vpx_codec_cx_pkt_t *vpxPacket;
|
||||
bool bKeyframe=false;
|
||||
|
||||
while ( ( vpxPacket = vpx_codec_get_cx_data(&m_vpxContext, &vpxIter)) != NULL )
|
||||
{
|
||||
if (vpxPacket->kind == VPX_CODEC_CX_FRAME_PKT)
|
||||
{
|
||||
// Extract if this is a keyframe from the first packet of data for each frame
|
||||
bKeyframe = vpxPacket->data.frame.flags & VPX_FRAME_IS_KEY;
|
||||
|
||||
m_mkvMuxerSegment.AddFrame((const uint8 *)vpxPacket->data.frame.buf, vpxPacket->data.frame.sz, m_vid_track,
|
||||
time_ns, bKeyframe);
|
||||
}
|
||||
|
||||
}
|
||||
m_nFramesAdded++;
|
||||
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoRecorder::FlushAudioSamples()
|
||||
{
|
||||
ogg_packet op;
|
||||
|
||||
// See if there are any samples available
|
||||
|
||||
while (vorbis_analysis_blockout(&m_vd, &m_vb) == 1)
|
||||
{
|
||||
int status_analysis = vorbis_analysis(&m_vb, NULL);
|
||||
if (status_analysis)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
status_analysis = vorbis_bitrate_addblock(&m_vb);
|
||||
if (status_analysis)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
while ((status_analysis = vorbis_bitrate_flushpacket(&m_vd, &op)) > 0)
|
||||
{
|
||||
// Add this packet to the webm stream
|
||||
uint64 time_ns = ((uint64)m_FrameDuration*(uint64)(m_nFramesAdded+1));
|
||||
|
||||
if (!m_mkvMuxerSegment.AddFrame(op.packet,
|
||||
op.bytes,
|
||||
m_aud_track,
|
||||
time_ns,
|
||||
true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWebMVideoRecorder::AppendAudioSamples( void *pSampleBuffer, size_t sampleSize )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::AppendAudioSamples()\n");
|
||||
#endif
|
||||
|
||||
SetResult( VideoResult::ILLEGAL_OPERATION );
|
||||
AssertExitF( m_bHasAudio );
|
||||
|
||||
SetResult( VideoResult::BAD_INPUT_PARAMETERS );
|
||||
AssertExitF( pSampleBuffer != nullptr );
|
||||
|
||||
SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE );
|
||||
AssertExitF( IsReadyToRecord() );
|
||||
|
||||
// $$$DEBUG Dump the Audio $$$DEBUG
|
||||
// The audio version of the dumping of video/audio data to files for debugging
|
||||
// You may need to change the path to put these files where you want them.
|
||||
#ifdef NEVER
|
||||
{
|
||||
FILE *fp=NULL;
|
||||
char rgch[256];
|
||||
int i,j;
|
||||
byte *pByte;
|
||||
static int i_AudSample_batch=0;
|
||||
static int i_AudSample_frame=-1;
|
||||
|
||||
if (m_nFramesAdded != i_AudSample_frame)
|
||||
i_AudSample_batch = 0;
|
||||
|
||||
i_AudSample_frame = m_nFramesAdded;
|
||||
|
||||
sprintf(rgch, "./frames/aud_%d_%d", i_AudSample_frame, i_AudSample_batch);
|
||||
fp = fopen(rgch, "wb");
|
||||
|
||||
// Size
|
||||
fwrite(&sampleSize, sizeof(sampleSize), 1, fp);
|
||||
|
||||
// Data
|
||||
pByte = (byte *)pSampleBuffer;
|
||||
fwrite(pByte, sampleSize, 1, fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
i_AudSample_batch++;
|
||||
|
||||
}
|
||||
#endif
|
||||
int num_blocks = sampleSize / ((m_audioBitDepth/8)*m_audioChannels);
|
||||
float **buffer=vorbis_analysis_buffer(&m_vd,num_blocks);
|
||||
|
||||
// Deinterleave input samples, convert them to float, and store them in
|
||||
// buffer
|
||||
const int16* pPCMsamples = (int16*)pSampleBuffer;
|
||||
|
||||
for (int i = 0; i < num_blocks; ++i)
|
||||
{
|
||||
for (int c = 0; c < m_audioChannels; ++c)
|
||||
{
|
||||
buffer[c][i] = pPCMsamples[i * m_audioChannels + c] / 32768.f;
|
||||
}
|
||||
}
|
||||
|
||||
vorbis_analysis_wrote(&m_vd, num_blocks);
|
||||
|
||||
FlushAudioSamples();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int CWebMVideoRecorder::GetFrameCount()
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::GetFrameCount()\n");
|
||||
#endif
|
||||
|
||||
return m_nFramesAdded;
|
||||
}
|
||||
|
||||
|
||||
int CWebMVideoRecorder::GetSampleCount()
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::GetSampleCount()\n");
|
||||
#endif
|
||||
|
||||
// return ( m_pEncoder == nullptr ) ? 0 : m_pEncoder->GetSampleCount();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
VideoFrameRate_t CWebMVideoRecorder::GetFPS()
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::GetFPS()\n");
|
||||
#endif
|
||||
|
||||
return m_MovieRecordFPS;
|
||||
}
|
||||
|
||||
|
||||
int CWebMVideoRecorder::GetSampleRate()
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::GetSampleRate()\n");
|
||||
#endif
|
||||
// return ( m_pEncoder == nullptr ) ? 0 : m_pEncoder->GetSampleRate();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoRecorder::AbortMovie()
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::AbortMovie()\n");
|
||||
#endif
|
||||
SetResult( VideoResult::OPERATION_OUT_OF_SEQUENCE );
|
||||
// AssertExitF( m_pEncoder != nullptr && !m_bMovieFinished );
|
||||
|
||||
m_bMovieFinished = true;
|
||||
// return m_pEncoder->AbortMovie();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoRecorder::FinishMovie( bool SaveMovieToDisk )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::FinishMovie()\n");
|
||||
#endif
|
||||
// Have to finish out the compress with a NULL frame
|
||||
// Compress it with the webm codec
|
||||
m_nFramesAdded++;
|
||||
uint64 time_ns = ((uint64)m_FrameDuration*(uint64)(m_nFramesAdded+1));
|
||||
|
||||
vpx_codec_err_t vpxError = vpx_codec_encode(&m_vpxContext, NULL, time_ns, m_FrameDuration, 0, 0);
|
||||
|
||||
if (vpxError != VPX_CODEC_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add this frame to the stream
|
||||
vpx_codec_iter_t vpxIter = NULL;
|
||||
const vpx_codec_cx_pkt_t *vpxPacket;
|
||||
|
||||
while ( ( vpxPacket = vpx_codec_get_cx_data(&m_vpxContext, &vpxIter) ) != NULL )
|
||||
{
|
||||
if (vpxPacket->kind == VPX_CODEC_CX_FRAME_PKT)
|
||||
{
|
||||
uint64 time_ns;
|
||||
bool bKeyframe=false;
|
||||
|
||||
// Extract if this is a keyframe from the first packet of data for each frame
|
||||
bKeyframe = vpxPacket->data.frame.flags & VPX_FRAME_IS_KEY;
|
||||
time_ns = ((uint64)m_FrameDuration*(uint64)m_nFramesAdded);
|
||||
|
||||
m_mkvMuxerSegment.AddFrame((const uint8 *)vpxPacket->data.frame.buf, vpxPacket->data.frame.sz, m_vid_track, time_ns, bKeyframe);
|
||||
}
|
||||
}
|
||||
|
||||
m_mkvMuxerSegment.Finalize();
|
||||
m_mkvWriter.Close();
|
||||
|
||||
vorbis_dsp_clear(&m_vd);
|
||||
vorbis_block_clear(&m_vb);
|
||||
vorbis_info_clear(&m_vi);
|
||||
|
||||
m_bMovieFinished = true;
|
||||
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::FinishMovie() movie encoded.\n");
|
||||
Msg("Total frames written: %d Time for movie in nanoseconds: %lu\n", m_nFramesAdded, ((unsigned long)m_nFramesAdded*m_FrameDuration));
|
||||
#endif
|
||||
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWebMVideoRecorder::EstimateMovieFileSize( size_t *pEstSize, int movieWidth, int movieHeight, VideoFrameRate_t movieFps, float movieDuration, VideoEncodeCodec_t theCodec, int videoQuality, AudioEncodeSourceFormat_t srcAudioFormat, int audioSampleRate )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::EstimateMovieFileSize()\n");
|
||||
#endif
|
||||
float fVidRate;
|
||||
float fAudRate;
|
||||
float movieDurationInSeconds;
|
||||
|
||||
fVidRate = GetVideoDataRate(videoQuality, movieWidth, movieHeight);
|
||||
fAudRate = GetAudioDataRate(videoQuality, movieWidth, movieHeight);
|
||||
movieDurationInSeconds = movieDuration;
|
||||
|
||||
// data rates is in killobits/second so convert to bytes/second
|
||||
*pEstSize = (size_t)((fVidRate*1000*movieDurationInSeconds/8) + (fAudRate*1000*movieDuration*movieDurationInSeconds/8));
|
||||
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
float CWebMVideoRecorder::GetVideoDataRate( int quality, int width, int height )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::GetDataRate()\n");
|
||||
#endif
|
||||
for(int i = 0; i< ARRAYSIZE( s_WebMEncodeRates ); i++ )
|
||||
{
|
||||
if (s_WebMEncodeRates[i].m_XResolution == width && s_WebMEncodeRates[i].m_YResolution == height)
|
||||
{
|
||||
return s_WebMEncodeRates[i].m_MinDataRate + (((s_WebMEncodeRates[i].m_MaxDataRate - s_WebMEncodeRates[i].m_MinDataRate)*(float)quality)/100.0);
|
||||
}
|
||||
}
|
||||
// Didn't find the resolution, odd
|
||||
Msg("Unable to find WebM resolution (%d, %d) at quality %d\n", width, height, quality);
|
||||
// Default to 2kb/s
|
||||
return 2000.0f;
|
||||
}
|
||||
|
||||
float CWebMVideoRecorder::GetAudioDataRate( int quality, int width, int height )
|
||||
{
|
||||
#ifdef LOG_ENCODER_OPERATIONS
|
||||
Msg("CWebMVideoRecorder::GetDataRate()\n");
|
||||
#endif
|
||||
for(int i = 0; i< ARRAYSIZE( s_WebMEncodeRates ); i++ )
|
||||
{
|
||||
if (s_WebMEncodeRates[i].m_XResolution == width && s_WebMEncodeRates[i].m_YResolution == height)
|
||||
{
|
||||
return s_WebMEncodeRates[i].m_MinAudioDataRate + (((s_WebMEncodeRates[i].m_MaxAudioDataRate - s_WebMEncodeRates[i].m_MinAudioDataRate)*(float)quality)/100.0);
|
||||
}
|
||||
}
|
||||
// Didn't find the resolution, odd
|
||||
Msg("Unable to find WebM resolution (%d, %d) at quality %d\n", width, height, quality);
|
||||
|
||||
// Default to 128kb/s for audio
|
||||
return 128.0f;
|
||||
}
|
111
video/webm_recorder.h
Normal file
111
video/webm_recorder.h
Normal file
@ -0,0 +1,111 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#ifndef WEBM_RECORDER_H
|
||||
#define WEBM_RECORDER_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//#include ""
|
||||
|
||||
#include "video/ivideoservices.h"
|
||||
|
||||
#include "video_macros.h"
|
||||
#include "webm_common.h"
|
||||
|
||||
// comment out to prevent logging of creation data
|
||||
//#define LOG_ENCODER_OPERATIONS
|
||||
|
||||
#if defined( LOG_ENCODER_OPERATIONS ) || defined( LOG_ENCODER_AUDIO_OPERATIONS ) || defined ( LOG_FRAMES_TO_TGA ) || defined ( ENABLE_EXTERNAL_ENCODER_LOGGING )
|
||||
#include <filesystem.h>
|
||||
#endif
|
||||
|
||||
|
||||
class CWebMVideoRecorder : public IVideoRecorder
|
||||
{
|
||||
public:
|
||||
CWebMVideoRecorder();
|
||||
~CWebMVideoRecorder();
|
||||
|
||||
virtual bool EstimateMovieFileSize( size_t *pEstSize, int movieWidth, int movieHeight, VideoFrameRate_t movieFps, float movieDuration, VideoEncodeCodec_t theCodec, int videoQuality, AudioEncodeSourceFormat_t srcAudioFormat = AudioEncodeSourceFormat::AUDIO_NONE, int audioSampleRate = 0 );
|
||||
|
||||
virtual bool CreateNewMovieFile( const char *pFilename, bool hasAudioTrack = false );
|
||||
|
||||
virtual bool SetMovieVideoParameters( VideoEncodeCodec_t theCodec, int videoQuality, int movieFrameWidth, int movieFrameHeight, VideoFrameRate_t movieFPS, VideoEncodeGamma_t gamma = VideoEncodeGamma::NO_GAMMA_ADJUST );
|
||||
virtual bool SetMovieSourceImageParameters( VideoEncodeSourceFormat_t srcImageFormat, int imgWidth, int imgHeight );
|
||||
virtual bool SetMovieSourceAudioParameters( AudioEncodeSourceFormat_t srcAudioFormat = AudioEncodeSourceFormat::AUDIO_NONE, int audioSampleRate = 0, AudioEncodeOptions_t audioOptions = AudioEncodeOptions::NO_AUDIO_OPTIONS, int audioSampleGroupSize = 0 );
|
||||
|
||||
virtual bool IsReadyToRecord();
|
||||
virtual VideoResult_t GetLastResult();
|
||||
|
||||
virtual bool AppendVideoFrame( void *pFrameBuffer, int nStrideAdjustBytes = 0 );
|
||||
virtual bool AppendAudioSamples( void *pSampleBuffer, size_t sampleSize );
|
||||
|
||||
virtual int GetFrameCount();
|
||||
virtual int GetSampleCount();
|
||||
virtual int GetSampleRate();
|
||||
virtual VideoFrameRate_t GetFPS();
|
||||
|
||||
virtual bool AbortMovie();
|
||||
virtual bool FinishMovie( bool SaveMovieToDisk = true );
|
||||
|
||||
private:
|
||||
bool FlushAudioSamples();
|
||||
void ConvertBGRAToYV12( void *pFrameBuffer, int nStrideAdjustBytes, vpx_image_t *m_SrcImageYV12Buffer, bool fIncludesAlpha );
|
||||
void SetResult( VideoResult_t resultCode );
|
||||
|
||||
float GetVideoDataRate( int quality, int width, int height );
|
||||
float GetAudioDataRate( int quality, int width, int height );
|
||||
|
||||
VideoResult_t m_LastResult;
|
||||
bool m_bHasAudio;
|
||||
bool m_bMovieFinished;
|
||||
|
||||
int m_nFramesAdded;
|
||||
int m_nAudioFramesAdded;
|
||||
int m_nSamplesAdded;
|
||||
|
||||
VideoFrameRate_t m_MovieRecordFPS;
|
||||
int m_MovieTimeScale;
|
||||
int m_DurationPerFrame;
|
||||
|
||||
unsigned long m_FrameDuration;
|
||||
|
||||
int m_MovieFrameWidth;
|
||||
int m_MovieFrameHeight;
|
||||
|
||||
vpx_image_t *m_SrcImageYV12Buffer;
|
||||
|
||||
VideoEncodeGamma_t m_MovieGamma;
|
||||
|
||||
VideoEncodeSourceFormat_t m_SrcImageFormat;
|
||||
int m_SrcImageWidth;
|
||||
int m_SrcImageHeight;
|
||||
|
||||
// WebM VPX
|
||||
vpx_codec_ctx_t m_vpxContext;
|
||||
vpx_codec_enc_cfg_t m_vpxConfig;
|
||||
mkvmuxer::MkvWriter m_mkvWriter;
|
||||
mkvmuxer::Segment m_mkvMuxerSegment;
|
||||
uint64 m_vid_track;
|
||||
|
||||
// Vorbis audio
|
||||
uint64 m_aud_track;
|
||||
int m_audioChannels;
|
||||
int m_audioSampleRate;
|
||||
int m_audioSampleGroupSize;
|
||||
int m_audioBitDepth;
|
||||
|
||||
vorbis_info m_vi;
|
||||
vorbis_dsp_state m_vd;
|
||||
vorbis_block m_vb;
|
||||
vorbis_comment m_vc;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // WEBM_RECORDER_H
|
353
video/webm_video.cpp
Normal file
353
video/webm_video.cpp
Normal file
@ -0,0 +1,353 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "webm_video.h"
|
||||
#include "webm_recorder.h"
|
||||
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "tier0/icommandline.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 "tier2/tier2.h"
|
||||
#include "platform.h"
|
||||
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// Singleton to expose WebM video subsystem
|
||||
// ===========================================================================
|
||||
static CWebMVideoSubSystem g_WebMSystem;
|
||||
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CWebMVideoSubSystem, IVideoSubSystem, VIDEO_SUBSYSTEM_INTERFACE_VERSION, g_WebMSystem );
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// List of file extensions and features supported by this subsystem
|
||||
// ===========================================================================
|
||||
VideoFileExtensionInfo_t s_WebMExtensions[] =
|
||||
{
|
||||
{ ".webm", VideoSystem::WEBM, VideoSystemFeature::FULL_ENCODE },
|
||||
};
|
||||
|
||||
const int s_WebMExtensionCount = ARRAYSIZE( s_WebMExtensions );
|
||||
|
||||
const VideoSystemFeature_t CWebMVideoSubSystem::DEFAULT_FEATURE_SET = VideoSystemFeature::FULL_ENCODE;
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// CWebMVideoSubSystem class
|
||||
// ===========================================================================
|
||||
CWebMVideoSubSystem::CWebMVideoSubSystem() :
|
||||
m_bWebMInitialized( false ),
|
||||
m_LastResult( VideoResult::SUCCESS ),
|
||||
m_CurrentStatus( VideoSystemStatus::NOT_INITIALIZED ),
|
||||
m_AvailableFeatures( CWebMVideoSubSystem::DEFAULT_FEATURE_SET ),
|
||||
m_pCommonServices( nullptr )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
CWebMVideoSubSystem::~CWebMVideoSubSystem()
|
||||
{
|
||||
ShutdownWebM(); // Super redundant safety check
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// IAppSystem methods
|
||||
// ===========================================================================
|
||||
bool CWebMVideoSubSystem::Connect( CreateInterfaceFn factory )
|
||||
{
|
||||
if ( !BaseClass::Connect( factory ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( g_pFullFileSystem == nullptr || materials == nullptr )
|
||||
{
|
||||
Msg( "WebM video subsystem failed to connect to missing a required system\n" );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CWebMVideoSubSystem::Disconnect()
|
||||
{
|
||||
BaseClass::Disconnect();
|
||||
}
|
||||
|
||||
|
||||
void* CWebMVideoSubSystem::QueryInterface( const char *pInterfaceName )
|
||||
{
|
||||
|
||||
if ( IS_NOT_EMPTY( pInterfaceName ) )
|
||||
{
|
||||
if ( V_strncmp( pInterfaceName, VIDEO_SUBSYSTEM_INTERFACE_VERSION, Q_strlen( VIDEO_SUBSYSTEM_INTERFACE_VERSION ) + 1) == STRINGS_MATCH )
|
||||
{
|
||||
return (IVideoSubSystem*) this;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
InitReturnVal_t CWebMVideoSubSystem::Init()
|
||||
{
|
||||
InitReturnVal_t nRetVal = BaseClass::Init();
|
||||
if ( nRetVal != INIT_OK )
|
||||
{
|
||||
return nRetVal;
|
||||
}
|
||||
|
||||
return INIT_OK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CWebMVideoSubSystem::Shutdown()
|
||||
{
|
||||
// Make sure we shut down WebM
|
||||
ShutdownWebM();
|
||||
|
||||
BaseClass::Shutdown();
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// IVideoSubSystem identification methods
|
||||
// ===========================================================================
|
||||
VideoSystem_t CWebMVideoSubSystem::GetSystemID()
|
||||
{
|
||||
return VideoSystem::WEBM;
|
||||
}
|
||||
|
||||
|
||||
VideoSystemStatus_t CWebMVideoSubSystem::GetSystemStatus()
|
||||
{
|
||||
return m_CurrentStatus;
|
||||
}
|
||||
|
||||
|
||||
VideoSystemFeature_t CWebMVideoSubSystem::GetSupportedFeatures()
|
||||
{
|
||||
return m_AvailableFeatures;
|
||||
}
|
||||
|
||||
|
||||
const char* CWebMVideoSubSystem::GetVideoSystemName()
|
||||
{
|
||||
return "WebM";
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// IVideoSubSystem setup and shutdown services
|
||||
// ===========================================================================
|
||||
bool CWebMVideoSubSystem::InitializeVideoSystem( IVideoCommonServices *pCommonServices )
|
||||
{
|
||||
m_AvailableFeatures = DEFAULT_FEATURE_SET; // Put here because of issue with static const int, binary OR and DEBUG builds
|
||||
|
||||
AssertPtr( pCommonServices );
|
||||
m_pCommonServices = pCommonServices;
|
||||
|
||||
return ( m_bWebMInitialized ) ? true : SetupWebM();
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoSubSystem::ShutdownVideoSystem()
|
||||
{
|
||||
return ( m_bWebMInitialized ) ? ShutdownWebM() : true;
|
||||
}
|
||||
|
||||
|
||||
VideoResult_t CWebMVideoSubSystem::VideoSoundDeviceCMD( VideoSoundDeviceOperation_t operation, void *pDevice, void *pData )
|
||||
{
|
||||
switch ( operation )
|
||||
{
|
||||
case VideoSoundDeviceOperation::SET_DIRECT_SOUND_DEVICE:
|
||||
{
|
||||
return SetResult( VideoResult::OPERATION_NOT_SUPPORTED );
|
||||
}
|
||||
|
||||
case VideoSoundDeviceOperation::SET_MILES_SOUND_DEVICE:
|
||||
case VideoSoundDeviceOperation::HOOK_X_AUDIO:
|
||||
{
|
||||
return SetResult( VideoResult::OPERATION_NOT_SUPPORTED );
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return SetResult( VideoResult::UNKNOWN_OPERATION );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// IVideoSubSystem supported extensions & features
|
||||
// ===========================================================================
|
||||
int CWebMVideoSubSystem::GetSupportedFileExtensionCount()
|
||||
{
|
||||
return s_WebMExtensionCount;
|
||||
}
|
||||
|
||||
|
||||
const char* CWebMVideoSubSystem::GetSupportedFileExtension( int num )
|
||||
{
|
||||
return ( num < 0 || num >= s_WebMExtensionCount ) ? nullptr : s_WebMExtensions[num].m_FileExtension;
|
||||
}
|
||||
|
||||
|
||||
VideoSystemFeature_t CWebMVideoSubSystem::GetSupportedFileExtensionFeatures( int num )
|
||||
{
|
||||
return ( num < 0 || num >= s_WebMExtensionCount ) ? VideoSystemFeature::NO_FEATURES : s_WebMExtensions[num].m_VideoFeatures;
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// IVideoSubSystem Video Playback and Recording Services
|
||||
// ===========================================================================
|
||||
VideoResult_t CWebMVideoSubSystem::PlayVideoFileFullScreen( const char *filename, void *mainWindow, int windowWidth, int windowHeight, int desktopWidth, int desktopHeight, bool windowed, float forcedMinTime, VideoPlaybackFlags_t playbackFlags )
|
||||
{
|
||||
return SetResult( VideoResult::FEATURE_NOT_AVAILABLE );
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// IVideoSubSystem Video Material Services
|
||||
// note that the filename is absolute and has already resolved any paths
|
||||
// ===========================================================================
|
||||
IVideoMaterial* CWebMVideoSubSystem::CreateVideoMaterial( const char *pMaterialName, const char *pVideoFileName, VideoPlaybackFlags_t flags )
|
||||
{
|
||||
SetResult( VideoResult::FEATURE_NOT_AVAILABLE );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
VideoResult_t CWebMVideoSubSystem::DestroyVideoMaterial( IVideoMaterial *pVideoMaterial )
|
||||
{
|
||||
return SetResult (VideoResult::FEATURE_NOT_AVAILABLE );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// IVideoSubSystem Video Recorder Services
|
||||
// ===========================================================================
|
||||
IVideoRecorder* CWebMVideoSubSystem::CreateVideoRecorder()
|
||||
{
|
||||
SetResult( VideoResult::SYSTEM_NOT_AVAILABLE );
|
||||
AssertExitN( m_CurrentStatus == VideoSystemStatus::OK );
|
||||
|
||||
CWebMVideoRecorder *pVideoRecorder = new CWebMVideoRecorder();
|
||||
|
||||
if ( pVideoRecorder != nullptr )
|
||||
{
|
||||
IVideoRecorder *pInterface = (IVideoRecorder*) pVideoRecorder;
|
||||
m_RecorderList.AddToTail( pInterface );
|
||||
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
SetResult( VideoResult::VIDEO_ERROR_OCCURED );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
VideoResult_t CWebMVideoSubSystem::DestroyVideoRecorder( IVideoRecorder *pRecorder )
|
||||
{
|
||||
AssertExitV( m_CurrentStatus == VideoSystemStatus::OK, SetResult( VideoResult::SYSTEM_NOT_AVAILABLE ) );
|
||||
AssertPtrExitV( pRecorder, SetResult( VideoResult::BAD_INPUT_PARAMETERS ) );
|
||||
|
||||
if ( m_RecorderList.Find( pRecorder ) != -1 )
|
||||
{
|
||||
CWebMVideoRecorder *pVideoRecorder = (CWebMVideoRecorder*) pRecorder;
|
||||
delete pVideoRecorder;
|
||||
|
||||
m_RecorderList.FindAndFastRemove( pRecorder );
|
||||
|
||||
return SetResult( VideoResult::SUCCESS );
|
||||
}
|
||||
|
||||
return SetResult( VideoResult::RECORDER_NOT_FOUND );
|
||||
}
|
||||
|
||||
VideoResult_t CWebMVideoSubSystem::CheckCodecAvailability( VideoEncodeCodec_t codec )
|
||||
{
|
||||
AssertExitV( m_CurrentStatus == VideoSystemStatus::OK, SetResult( VideoResult::SYSTEM_NOT_AVAILABLE ) );
|
||||
AssertExitV( codec >= VideoEncodeCodec::DEFAULT_CODEC && codec < VideoEncodeCodec::CODEC_COUNT, SetResult( VideoResult::BAD_INPUT_PARAMETERS ) );
|
||||
|
||||
return SetResult( VideoResult::FEATURE_NOT_AVAILABLE );
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// Status support
|
||||
// ===========================================================================
|
||||
VideoResult_t CWebMVideoSubSystem::GetLastResult()
|
||||
{
|
||||
return m_LastResult;
|
||||
}
|
||||
|
||||
|
||||
VideoResult_t CWebMVideoSubSystem::SetResult( VideoResult_t status )
|
||||
{
|
||||
m_LastResult = status;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// WebM Initialization & Shutdown
|
||||
// ===========================================================================
|
||||
bool CWebMVideoSubSystem::SetupWebM()
|
||||
{
|
||||
SetResult( VideoResult::INITIALIZATION_ERROR_OCCURED);
|
||||
AssertExitF( m_bWebMInitialized == false );
|
||||
|
||||
// This is set early to indicate we have already been through here, even if we error out for some reason
|
||||
m_bWebMInitialized = true;
|
||||
m_CurrentStatus = VideoSystemStatus::OK;
|
||||
m_AvailableFeatures = DEFAULT_FEATURE_SET;
|
||||
// $$INIT CODE HERE$$
|
||||
|
||||
|
||||
// Note that we are now open for business....
|
||||
m_bWebMInitialized = true;
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CWebMVideoSubSystem::ShutdownWebM()
|
||||
{
|
||||
if ( m_bWebMInitialized && m_CurrentStatus == VideoSystemStatus::OK )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
m_bWebMInitialized = false;
|
||||
m_CurrentStatus = VideoSystemStatus::NOT_INITIALIZED;
|
||||
m_AvailableFeatures = VideoSystemFeature::NO_FEATURES;
|
||||
SetResult( VideoResult::SUCCESS );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
133
video/webm_video.h
Normal file
133
video/webm_video.h
Normal file
@ -0,0 +1,133 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#ifndef WEBM_VIDEO_H
|
||||
#define WEBM_VIDEO_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class IFileSystem;
|
||||
class IMaterialSystem;
|
||||
class CQuickTimeMaterial;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Global interfaces - you already did the needed includes, right?
|
||||
//-----------------------------------------------------------------------------
|
||||
extern IFileSystem *g_pFileSystem;
|
||||
extern IMaterialSystem *materials;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// WebM Header files, anything that seems strange here is to make it so their headers
|
||||
// can mesh with ours
|
||||
//-----------------------------------------------------------------------------
|
||||
#define VPX_CODEC_DISABLE_COMPAT 1
|
||||
#include "vpx/vpx_codec.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx/vpx_image.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#ifdef UNUSED
|
||||
#undef UNUSED
|
||||
#endif
|
||||
|
||||
// libwebm, support for reading/writing webm files
|
||||
#include "mkvreader.hpp"
|
||||
#include "mkvparser.hpp"
|
||||
#include "mkvmuxer.hpp"
|
||||
#include "mkvwriter.hpp"
|
||||
#include "mkvmuxerutil.hpp"
|
||||
|
||||
#include "vorbis/vorbisenc.h"
|
||||
#include "vorbis/codec.h"
|
||||
|
||||
#include "video/ivideoservices.h"
|
||||
#include "videosubsystem.h"
|
||||
|
||||
#include "utlvector.h"
|
||||
#include "tier1/KeyValues.h"
|
||||
#include "tier0/platform.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CQuickTimeVideoSubSystem - Implementation of IVideoSubSystem
|
||||
// -----------------------------------------------------------------------------
|
||||
class CWebMVideoSubSystem : public CTier2AppSystem< IVideoSubSystem >
|
||||
{
|
||||
typedef CTier2AppSystem< IVideoSubSystem > BaseClass;
|
||||
|
||||
public:
|
||||
CWebMVideoSubSystem();
|
||||
~CWebMVideoSubSystem();
|
||||
|
||||
// Inherited from IAppSystem
|
||||
virtual bool Connect( CreateInterfaceFn factory );
|
||||
virtual void Disconnect();
|
||||
virtual void *QueryInterface( const char *pInterfaceName );
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Inherited from IVideoSubSystem
|
||||
|
||||
// SubSystem Identification functions
|
||||
virtual VideoSystem_t GetSystemID();
|
||||
virtual VideoSystemStatus_t GetSystemStatus();
|
||||
virtual VideoSystemFeature_t GetSupportedFeatures();
|
||||
virtual const char *GetVideoSystemName();
|
||||
|
||||
// Setup & Shutdown Services
|
||||
virtual bool InitializeVideoSystem( IVideoCommonServices *pCommonServices );
|
||||
virtual bool ShutdownVideoSystem();
|
||||
|
||||
virtual VideoResult_t VideoSoundDeviceCMD( VideoSoundDeviceOperation_t operation, void *pDevice = nullptr, void *pData = nullptr );
|
||||
|
||||
// get list of file extensions and features we support
|
||||
virtual int GetSupportedFileExtensionCount();
|
||||
virtual const char *GetSupportedFileExtension( int num );
|
||||
virtual VideoSystemFeature_t GetSupportedFileExtensionFeatures( int num );
|
||||
|
||||
// Video Playback and Recording Services
|
||||
virtual VideoResult_t PlayVideoFileFullScreen( const char *filename, void *mainWindow, int windowWidth, int windowHeight, int desktopWidth, int desktopHeight, bool windowed, float forcedMinTime, VideoPlaybackFlags_t playbackFlags );
|
||||
|
||||
// Create/destroy a video material
|
||||
virtual IVideoMaterial *CreateVideoMaterial( const char *pMaterialName, const char *pVideoFileName, VideoPlaybackFlags_t flags );
|
||||
virtual VideoResult_t DestroyVideoMaterial( IVideoMaterial *pVideoMaterial );
|
||||
|
||||
// Create/destroy a video encoder
|
||||
virtual IVideoRecorder *CreateVideoRecorder();
|
||||
virtual VideoResult_t DestroyVideoRecorder( IVideoRecorder *pRecorder );
|
||||
|
||||
virtual VideoResult_t CheckCodecAvailability( VideoEncodeCodec_t codec );
|
||||
|
||||
virtual VideoResult_t GetLastResult();
|
||||
|
||||
private:
|
||||
|
||||
bool SetupWebM();
|
||||
bool ShutdownWebM();
|
||||
|
||||
VideoResult_t SetResult( VideoResult_t status );
|
||||
|
||||
bool m_bWebMInitialized;
|
||||
VideoResult_t m_LastResult;
|
||||
|
||||
VideoSystemStatus_t m_CurrentStatus;
|
||||
VideoSystemFeature_t m_AvailableFeatures;
|
||||
|
||||
IVideoCommonServices *m_pCommonServices;
|
||||
|
||||
CUtlVector< IVideoMaterial* > m_MaterialList;
|
||||
CUtlVector< IVideoRecorder* > m_RecorderList;
|
||||
|
||||
static const VideoSystemFeature_t DEFAULT_FEATURE_SET;
|
||||
};
|
||||
|
||||
#endif // WEBM_VIDEO_H
|
Reference in New Issue
Block a user