You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
788 lines
26 KiB
788 lines
26 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
//----------------------------------------------------------------------------- |
|
// File: WMVPlayer.cpp |
|
// |
|
// Desc: This helper class provides simple WMV decoding and playback |
|
// functionality. It will be expanded as new playback methods are |
|
// exposed |
|
// |
|
// Hist: 2.7.03 - Created, based on work by Jeff Sullivan |
|
// |
|
// Copyright (c) Microsoft Corporation. All rights reserved. |
|
//----------------------------------------------------------------------------- |
|
|
|
#include "xbox_loader.h" |
|
#include <xtl.h> |
|
#include "XMVHelper.h" |
|
#include "XBUtil.h" |
|
#include <stdio.h> |
|
|
|
|
|
// Funtion Prototypes for packet loading functions for loading from a file. |
|
HRESULT CALLBACK GetNextPacket( DWORD dwContext, |
|
void **ppPacket, |
|
DWORD* pOffsetToNextPacket ); |
|
|
|
HRESULT CALLBACK ReleasePreviousPacket( DWORD dwContext, |
|
LONGLONG llNextReadByteOffset, |
|
DWORD dwNextPacketSize ); |
|
|
|
// Funtion Prototypes for packet loading functions for loading from a block of memory. |
|
HRESULT CALLBACK GetNextMemoryPacket( DWORD dwContext, |
|
void **ppPacket, |
|
DWORD* pOffsetToNextPacket ); |
|
|
|
HRESULT CALLBACK ReleasePreviousMemoryPacket( DWORD dwContext, |
|
LONGLONG llNextReadByteOffset, |
|
DWORD dwNextPacketSize ); |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: CXMVPlayer() |
|
// Desc: Constructor for CXMVPlayer |
|
//----------------------------------------------------------------------------- |
|
CXMVPlayer::CXMVPlayer() |
|
{ |
|
m_pXMVDecoder = NULL; |
|
ZeroMemory( &m_VideoDesc, sizeof( m_VideoDesc ) ); |
|
ZeroMemory( &m_AudioDesc, sizeof( m_AudioDesc ) ); |
|
for ( UINT i=0; i<XMVPLAYER_NUMTEXTURES; i++ ) |
|
{ |
|
m_pTextures[i] = NULL; |
|
} |
|
|
|
m_dwCurrentFrame = -1; // Will be zero after we decode the first frame. |
|
|
|
m_bPlaying = FALSE; |
|
m_bOverlaysEnabled = FALSE; |
|
|
|
m_loadContext.hFile = INVALID_HANDLE_VALUE; |
|
m_loadContext.pInputBuffer = 0; |
|
m_physicalBuffer = 0; |
|
m_bError = FALSE; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: ~CXMVPlayer() |
|
// Desc: Destructor for CXMVPlayer |
|
//----------------------------------------------------------------------------- |
|
CXMVPlayer::~CXMVPlayer() |
|
{ |
|
Destroy(); |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: Destroy() |
|
// Desc: Free all resources and clear are resource pointers and handles. |
|
//----------------------------------------------------------------------------- |
|
HRESULT CXMVPlayer::Destroy() |
|
{ |
|
// Disable overlays if we were using them. |
|
if ( m_bOverlaysEnabled ) |
|
{ |
|
m_pDevice->EnableOverlay( FALSE ); |
|
m_bOverlaysEnabled = FALSE; |
|
} |
|
|
|
// Free the XMV decoder. |
|
if ( NULL != m_pXMVDecoder ) |
|
{ |
|
m_pXMVDecoder->CloseDecoder(); |
|
m_pXMVDecoder = NULL; |
|
} |
|
|
|
ZeroMemory( &m_VideoDesc, sizeof( m_VideoDesc ) ); |
|
ZeroMemory( &m_AudioDesc, sizeof( m_AudioDesc ) ); |
|
|
|
// Release our textures. |
|
for ( UINT i=0; i<XMVPLAYER_NUMTEXTURES; i++ ) |
|
{ |
|
if ( m_pTextures[i] ) |
|
m_pTextures[i]->Release(); |
|
m_pTextures[i] = 0; |
|
} |
|
|
|
m_dwCurrentFrame = -1; |
|
m_dwStartTime = 0; |
|
|
|
m_bPlaying = FALSE; |
|
|
|
// Release any file handles we were using. |
|
if( INVALID_HANDLE_VALUE != m_loadContext.hFile ) |
|
{ |
|
CloseHandle( m_loadContext.hFile ); |
|
m_loadContext.hFile = INVALID_HANDLE_VALUE; |
|
} |
|
|
|
// Free up memory used for playing a movie from memory. |
|
if ( m_loadContext.pInputBuffer ) |
|
{ |
|
free( m_loadContext.pInputBuffer ); |
|
m_loadContext.pInputBuffer = 0; |
|
} |
|
|
|
// Be sure to release the physical memory last! |
|
if( m_physicalBuffer ) |
|
{ |
|
XPhysicalFree( m_physicalBuffer ); |
|
m_physicalBuffer = 0; |
|
} |
|
|
|
return S_OK; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: FinishOpeningFile() |
|
// Desc: Helper function for the three Open functions. Enables the audio streams, |
|
// initializes the video descriptor, and allocates textures if needed. |
|
//----------------------------------------------------------------------------- |
|
HRESULT CXMVPlayer::FinishOpeningFile( D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ) |
|
{ |
|
assert( format == D3DFMT_YUY2 || format == D3DFMT_LIN_A8R8G8B8 ); |
|
assert( XMVPLAYER_NUMTEXTURES >= 2); |
|
|
|
HRESULT hr = S_OK; |
|
|
|
m_pXMVDecoder->GetVideoDescriptor( &m_VideoDesc ); |
|
|
|
// Enable the audio streams |
|
for ( unsigned i=0; i < m_VideoDesc.AudioStreamCount; i++ ) |
|
{ |
|
m_pXMVDecoder->GetAudioDescriptor( i, &m_AudioDesc ); |
|
hr = m_pXMVDecoder->EnableAudioStream( i, 0, NULL, NULL); |
|
if ( FAILED( hr ) ) |
|
{ |
|
XBUtil_DebugPrint( "Unable to enable audio stream 0 (error %x)\n", hr ); |
|
Destroy(); |
|
return hr; |
|
} |
|
} |
|
|
|
for ( int i = 0; i < XMVPLAYER_NUMTEXTURES; i++ ) |
|
{ |
|
m_pTextures[i] = 0; |
|
if ( bAllocateTextures ) |
|
{ |
|
hr = pDevice->CreateTexture( m_VideoDesc.Width, m_VideoDesc.Height, 1, 0, format, 0, &m_pTextures[i] ); |
|
if ( FAILED( hr ) ) |
|
{ |
|
XBUtil_DebugPrint( "Unable to create texture %d (error %x)\n", i, hr ); |
|
Destroy(); |
|
return hr; |
|
} |
|
} |
|
} |
|
|
|
// Initialize what texture we are decoding to, if decoding for texture mapping. |
|
m_nDecodeTextureIndex = 0; |
|
|
|
// Initialize the various texture pointers for use when decoding for overlays. |
|
pShowingTexture = m_pTextures[0]; |
|
pDecodingTexture = m_pTextures[1]; |
|
pSubmittedTexture = 0; |
|
|
|
m_bPlaying = TRUE; |
|
m_dwStartTime = GetTickCount(); |
|
|
|
return hr; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: OpenFile() |
|
// Desc: Create an XMV decoder object that reads from a file. |
|
//----------------------------------------------------------------------------- |
|
HRESULT CXMVPlayer::OpenFile( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ) |
|
{ |
|
HRESULT hr = S_OK; |
|
|
|
m_bError = FALSE; |
|
|
|
if ( NULL == lpFilename || NULL == pDevice ) |
|
{ |
|
XBUtil_DebugPrint( "Bad parameter to OpenFile()\n" ); |
|
m_bError = TRUE; |
|
return E_FAIL; |
|
} |
|
|
|
hr = XMVDecoder_CreateDecoderForFile( XMVFLAG_SYNC_ON_NEXT_VBLANK, ( CHAR* )lpFilename, &m_pXMVDecoder ); |
|
if ( FAILED( hr ) ) |
|
{ |
|
XBUtil_DebugPrint( "Unable to create XMV Decoder for %s (error: %x)\n", lpFilename, hr ); |
|
m_bError = TRUE; |
|
return hr; |
|
} |
|
|
|
hr = FinishOpeningFile( format, pDevice, bAllocateTextures ); |
|
|
|
if ( FAILED( hr ) ) |
|
{ |
|
m_bError = TRUE; |
|
} |
|
|
|
return hr; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: OpenFileForPackets() |
|
// Desc: Create an XMV decoder object that uses the packet reading interface. |
|
// Currently this just reads from a file, but it can be altered to read from |
|
// custom formats, start partway through a file, etc. |
|
//----------------------------------------------------------------------------- |
|
HRESULT CXMVPlayer::OpenFileForPackets( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ) |
|
{ |
|
HRESULT hr = S_OK; |
|
|
|
// We need to read in the first 4K of data for the XMV player to initialize |
|
// itself from. This is most conveniently read as an array of DWORDS. |
|
DWORD first4Kbytes[4096 / sizeof( DWORD )]; |
|
|
|
// Clear entire context struct to zero |
|
ZeroMemory( &m_loadContext, sizeof( m_loadContext ) ); |
|
|
|
// Open the input file. |
|
m_loadContext.hFile = CreateFile( lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, |
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, |
|
NULL ); |
|
|
|
if( m_loadContext.hFile == INVALID_HANDLE_VALUE ) |
|
{ |
|
Destroy(); |
|
return E_INVALIDARG; |
|
} |
|
|
|
// Read the first page from the file. We opened it for |
|
// overlapped IO so we do a pair of reads. |
|
m_loadContext.Overlapped.Offset = 0; |
|
m_loadContext.Overlapped.OffsetHigh = 0; |
|
|
|
// Start the read. |
|
if( 0 == ReadFile( m_loadContext.hFile, first4Kbytes, sizeof( first4Kbytes ), NULL, &m_loadContext.Overlapped ) ) |
|
{ |
|
if( GetLastError() != ERROR_IO_PENDING ) |
|
{ |
|
Destroy(); |
|
return E_FAIL; |
|
} |
|
} |
|
|
|
// Wait for the read to finish. |
|
DWORD dwBytesRead; |
|
if( !GetOverlappedResult( m_loadContext.hFile, &m_loadContext.Overlapped, &dwBytesRead, TRUE ) ) |
|
{ |
|
Destroy(); |
|
return E_FAIL; |
|
} |
|
|
|
// Check size to make sure input is a valid XMV file. |
|
if( dwBytesRead != 4096 ) |
|
{ |
|
Destroy(); |
|
return E_FAIL; |
|
} |
|
|
|
// Create an XMV decoder |
|
hr = XMVDecoder_CreateDecoderForPackets( XMVFLAG_SYNC_ON_NEXT_VBLANK, first4Kbytes, ( DWORD )&m_loadContext, |
|
GetNextPacket, ReleasePreviousPacket, &m_pXMVDecoder ); |
|
if( FAILED( hr ) ) |
|
{ |
|
Destroy(); |
|
return E_FAIL; |
|
} |
|
|
|
// The size of the first packet and the minimum size of the two packet buffers are stored in the |
|
// second and third DWORDS of the file. From xmv.h: |
|
// * DWORD NextPacketSize // The size of the next packet |
|
// * DWORD ThisPacketSize // The size of this packet |
|
// * DWORD MaxPacketSize // The size of the largest packet in the file |
|
DWORD dwThisPacketSize = first4Kbytes[1]; |
|
DWORD dwRequiredPacketSize = first4Kbytes[2]; |
|
|
|
// Check for illegal parameters. |
|
if( dwThisPacketSize > dwRequiredPacketSize ) |
|
{ |
|
Destroy(); |
|
return E_FAIL; |
|
} |
|
|
|
// XPhysicalAlloc is used so that 5.1 or compressed audio streams can be played. |
|
m_physicalBuffer = ( BYTE* )XPhysicalAlloc( dwRequiredPacketSize * 2, MAXULONG_PTR, 0, PAGE_READWRITE ); |
|
|
|
// Save our information. |
|
m_loadContext.dwPacketSize = dwRequiredPacketSize; |
|
m_loadContext.pLoadingPacket = m_physicalBuffer; |
|
m_loadContext.pDecodingPacket = m_physicalBuffer + dwRequiredPacketSize; |
|
|
|
// Read the first packet. We wind up re-reading the first 4096 |
|
// bytes but it makes the logic for figuring out how much we read |
|
// a little bit easier... |
|
m_loadContext.Overlapped.Offset = 0; |
|
m_loadContext.Overlapped.OffsetHigh = 0; |
|
|
|
if( 0 == ReadFile( m_loadContext.hFile, m_physicalBuffer, dwThisPacketSize, NULL, |
|
&m_loadContext.Overlapped ) ) |
|
{ |
|
if( GetLastError() != ERROR_IO_PENDING ) |
|
{ |
|
Destroy(); |
|
return E_FAIL; |
|
} |
|
} |
|
|
|
// Note - at this point the preceding read has *not* necessarily completed. |
|
// Don't try reading anything from that buffer until GetNextPacket has been |
|
// successfully called. |
|
|
|
hr = FinishOpeningFile( format, pDevice, bAllocateTextures ); |
|
|
|
return hr; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: OpenMovieFromMemory() |
|
// Desc: Create an XMV decoder object that uses the packet reading interface to |
|
// read from a block of memory. To simplify the memory management this function |
|
// also allocates this block of memory and initializes it from a file. |
|
//----------------------------------------------------------------------------- |
|
HRESULT CXMVPlayer::OpenMovieFromMemory( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ) |
|
{ |
|
HRESULT hr = S_OK; |
|
|
|
m_bError = FALSE; |
|
|
|
// Read the entire file into memory. |
|
void* data; |
|
hr = XBUtil_LoadFile( lpFilename, &data, &m_loadContext.inputSize ); |
|
if ( FAILED( hr ) ) |
|
{ |
|
m_bError = TRUE; |
|
return hr; |
|
} |
|
|
|
m_loadContext.pInputBuffer = ( BYTE* )data; |
|
|
|
// Check size to make sure input is a valid XMV file. |
|
if( m_loadContext.inputSize < 4096 ) |
|
{ |
|
Destroy(); |
|
m_bError = TRUE; |
|
return E_FAIL; |
|
} |
|
|
|
// Get a DWORD pointer to the first 4K - needed by CreateDecoderForPackets |
|
DWORD* first4Kbytes = ( DWORD* )data; |
|
|
|
// Create an XMV decoder |
|
hr = XMVDecoder_CreateDecoderForPackets( XMVFLAG_SYNC_ON_NEXT_VBLANK, first4Kbytes, ( DWORD )&m_loadContext, GetNextMemoryPacket, |
|
ReleasePreviousMemoryPacket, &m_pXMVDecoder ); |
|
if ( FAILED( hr ) ) |
|
{ |
|
Destroy(); |
|
m_bError = TRUE; |
|
return E_FAIL; |
|
} |
|
|
|
// The size of the first packet and the minimum size of the two packet buffers are stored in the |
|
// second and third DWORDS of the file. From xmv.h: |
|
// * DWORD NextPacketSize // The size of the next packet |
|
// * DWORD ThisPacketSize // The size of this packet |
|
// * DWORD MaxPacketSize // The size of the largest packet in the file |
|
DWORD dwThisPacketSize = first4Kbytes[1]; |
|
DWORD dwRequiredPacketSize = first4Kbytes[2]; |
|
|
|
// Check for illegal parameters. |
|
if( dwThisPacketSize > dwRequiredPacketSize ) |
|
{ |
|
Destroy(); |
|
m_bError = TRUE; |
|
return E_FAIL; |
|
} |
|
|
|
// XPhysicalAlloc is used so that 5.1 or compressed audio streams can be played. |
|
m_physicalBuffer = ( BYTE* )XPhysicalAlloc( dwRequiredPacketSize * 2, MAXULONG_PTR, 0, PAGE_READWRITE ); |
|
|
|
// Save our information for the callback functions. |
|
// The size of our two memory blocks. |
|
m_loadContext.dwPacketSize = dwRequiredPacketSize; |
|
// The addresses of our two memory blocks. |
|
m_loadContext.pLoadingPacket = m_physicalBuffer; |
|
m_loadContext.pDecodingPacket = m_physicalBuffer + dwRequiredPacketSize; |
|
|
|
// Information about the block of memory the movie is stored in. |
|
m_loadContext.pInputBuffer = ( BYTE* )data; |
|
m_loadContext.inputSize = m_loadContext.inputSize; |
|
m_loadContext.readOffset = 0; |
|
m_loadContext.currentPacketSize = dwThisPacketSize; |
|
|
|
hr = FinishOpeningFile( format, pDevice, bAllocateTextures ); |
|
|
|
if ( FAILED( hr ) ) |
|
{ |
|
m_bError = TRUE; |
|
} |
|
|
|
return hr; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: AdvanceFrameForTexturing() |
|
// Desc: Unpack the appropriate frames of data for use as textures. |
|
//----------------------------------------------------------------------------- |
|
LPDIRECT3DTEXTURE8 CXMVPlayer::AdvanceFrameForTexturing( LPDIRECT3DDEVICE8 pDevice ) |
|
{ |
|
// You must pass bAllocateTextures==TRUE to Open if you're going to use GetTexture/AdvanceFrame. |
|
assert( m_pTextures[0] ); |
|
|
|
LPDIRECT3DSURFACE8 pSurface; |
|
pDecodingTexture->GetSurfaceLevel( 0, &pSurface ); |
|
|
|
// Decode some information to the current draw texture. |
|
XMVRESULT xr = XMV_NOFRAME; |
|
m_pXMVDecoder->GetNextFrame( pSurface, &xr, NULL ); |
|
|
|
switch ( xr ) |
|
{ |
|
case XMV_NOFRAME: |
|
// Do nothing - we didn't get a frame. |
|
break; |
|
|
|
case XMV_NEWFRAME: |
|
++m_dwCurrentFrame; |
|
// GetNextFrame produced a new frame. So, the texture we were decoding |
|
// to becomes available for drawing as a texture. |
|
pShowingTexture = pDecodingTexture; |
|
|
|
// Setup for decoding to the next texture. |
|
m_nDecodeTextureIndex = ( m_nDecodeTextureIndex + 1 ) % XMVPLAYER_NUMTEXTURES; |
|
pDecodingTexture = m_pTextures[ m_nDecodeTextureIndex ]; |
|
break; |
|
|
|
case XMV_ENDOFFILE: |
|
m_bPlaying = FALSE; |
|
break; |
|
|
|
case XMV_FAIL: |
|
// Data corruption or file read error. We'll treat that the same as |
|
// end of file. |
|
m_bPlaying = FALSE; |
|
m_bError = TRUE; |
|
break; |
|
} |
|
|
|
SAFE_RELEASE( pSurface ); |
|
|
|
// If we haven't decoded the first frame then return zero. |
|
if ( m_dwCurrentFrame < 0 ) |
|
return 0; |
|
|
|
return pShowingTexture; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: AdvanceFrameForOverlays() |
|
// Desc: Unpack the appropriate frames of data for use as an overlay. |
|
//----------------------------------------------------------------------------- |
|
LPDIRECT3DTEXTURE8 CXMVPlayer::AdvanceFrameForOverlays( LPDIRECT3DDEVICE8 pDevice ) |
|
{ |
|
// You must pass bAllocateTextures==TRUE to Open if you're going to use GetTexture/AdvanceFrame. |
|
assert( m_pTextures[0] ); |
|
|
|
// You have to call CXMVPlayer::EnableOverlays() if you are going to use overlays. |
|
assert( m_bOverlaysEnabled ); |
|
|
|
// If a texture has been submitted to be used as an overlay then we have to |
|
// wait for GetUpdateOverlayState() to return TRUE before we can assume that |
|
// the previous texture has *stopped* being displayed. Once GetUpdateOverlayState() |
|
// returns TRUE then we know that pSubmittedTexture is being displayed, which |
|
// means that, pShowingTexture is available as a decoding target. |
|
if ( pSubmittedTexture ) |
|
{ |
|
// If GetOverlayUpdateStatus() returns FALSE then we can still proceed and |
|
// call GetNextFrame(), but we will pass NULL for the surface parameter. |
|
// Some work will still be done, but none of the surfaces will be altered. |
|
if ( pDevice->GetOverlayUpdateStatus() ) |
|
{ |
|
// The call to UpdateOverlay() with pSubmittedTexture must have taken |
|
// effect now, so pShowingTexture is available as a decoding target. |
|
assert( !pDecodingTexture ); |
|
pDecodingTexture = pShowingTexture; |
|
pShowingTexture = pSubmittedTexture; |
|
pSubmittedTexture = NULL; |
|
} |
|
} |
|
|
|
LPDIRECT3DSURFACE8 pSurface = NULL; |
|
if ( pDecodingTexture ) |
|
pDecodingTexture->GetSurfaceLevel( 0, &pSurface ); |
|
|
|
// Decode some information to the current draw texture, which may be NULL. |
|
// pDecodingTexture will be NULL if one texture has been submitted as a new |
|
// overlay but the other one is still being displayed as an overlay. |
|
// If pSurface is NULL GetNextFrame() will still do some work. |
|
XMVRESULT xr = XMV_NOFRAME; |
|
m_pXMVDecoder->GetNextFrame( pSurface, &xr, NULL ); |
|
|
|
switch ( xr ) |
|
{ |
|
case XMV_NOFRAME: |
|
// Do nothing - we didn't get a frame. |
|
break; |
|
|
|
case XMV_NEWFRAME: |
|
++m_dwCurrentFrame; |
|
// GetNextFrame produced a new frame. So, the texture we were decoding |
|
// to becomes available for displaying as an overlay. |
|
|
|
// The other texture is not ready to be a decoding target. It is still |
|
// being displayed as an overlay. So, we assign the newly decoded |
|
// texture to pSubmittedTexture for the program to submit as an overlay, |
|
// but we don't yet move the previously submitted texture from pShowing |
|
// to pDecoding. That happens on a subsequent call to this function, after |
|
// GetOverlayUpdateStatus() returns TRUE to tell us that there are no |
|
// overlay swaps pending. |
|
assert( pDecodingTexture ); |
|
assert( !pSubmittedTexture ); |
|
pSubmittedTexture = pDecodingTexture; |
|
pDecodingTexture = NULL; |
|
break; |
|
|
|
case XMV_ENDOFFILE: |
|
m_bPlaying = FALSE; |
|
break; |
|
|
|
case XMV_FAIL: |
|
// Data corruption or file read error. We'll treat that the same as |
|
// end of file. |
|
m_bPlaying = FALSE; |
|
m_bError = TRUE; |
|
break; |
|
} |
|
|
|
SAFE_RELEASE( pSurface ); |
|
|
|
// If we just unpacked a new frame then we return that texture |
|
// and the program must call UpdateOverlay() with the surface |
|
// from that texture. |
|
// If we didn't unpack a frame then the program should do nothing - |
|
// the previous overlay will continue to be displayed. |
|
if ( XMV_NEWFRAME == xr ) |
|
return pSubmittedTexture; |
|
|
|
// No new frame to display. |
|
return 0; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: TerminatePlayback() |
|
// Desc: Calls XMVDecoder::TerminatePlayback() |
|
//----------------------------------------------------------------------------- |
|
void CXMVPlayer::TerminatePlayback() |
|
{ |
|
m_pXMVDecoder->TerminatePlayback(); |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: Play() |
|
// Desc: Calls XMVDecoder::Play() to play the entire movie. |
|
//----------------------------------------------------------------------------- |
|
HRESULT CXMVPlayer::Play( DWORD Flags, RECT* pRect ) |
|
{ |
|
// You have to call Open before calling Play. |
|
assert( m_pXMVDecoder ); |
|
|
|
// Don't pass bAllocateTextures==TRUE to Open if you're going to use Play. |
|
assert( !m_pTextures[0] ); |
|
|
|
return m_pXMVDecoder->Play( Flags, pRect ); |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: EnableOverlays() |
|
// Desc: Enable the overlay planes for playing the movie in them, and record |
|
// that the overlays should be disabled when Destroy() is called. |
|
//----------------------------------------------------------------------------- |
|
void CXMVPlayer::EnableOverlays( LPDIRECT3DDEVICE8 pDevice ) |
|
{ |
|
m_pDevice = pDevice; |
|
pDevice->EnableOverlay( TRUE ); |
|
m_bOverlaysEnabled = TRUE; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: GetNextPacket() |
|
// Desc: Callback function to get next packet from a file |
|
//----------------------------------------------------------------------------- |
|
static HRESULT CALLBACK GetNextPacket( DWORD dwContext, VOID** ppPacket, |
|
DWORD* pOffsetToNextPacket ) |
|
{ |
|
LOAD_CONTEXT* pContext = ( LOAD_CONTEXT* )dwContext; |
|
if( NULL == pContext ) |
|
return E_FAIL; |
|
|
|
// If the next packet is fully loaded then return it, |
|
// otherwise return NULL. |
|
DWORD dwBytesRead; |
|
if( GetOverlappedResult( pContext->hFile, &pContext->Overlapped, |
|
&dwBytesRead, FALSE ) ) |
|
{ |
|
// Make the old decoding packet pending. |
|
pContext->pPendingReleasePacket = pContext->pDecodingPacket; |
|
pContext->pDecodingPacket = pContext->pLoadingPacket; |
|
pContext->pLoadingPacket = NULL; |
|
|
|
// Offset to the next packet. |
|
*pOffsetToNextPacket = dwBytesRead; |
|
|
|
// Set *ppPacket to the data we just loaded. |
|
*ppPacket = pContext->pDecodingPacket; |
|
} |
|
else |
|
{ |
|
DWORD dwError = GetLastError(); |
|
|
|
// If we're waiting on the IO to finish, just do nothing. |
|
if( dwError != ERROR_IO_INCOMPLETE ) |
|
return HRESULT_FROM_WIN32( dwError ); |
|
|
|
*ppPacket = NULL; |
|
*pOffsetToNextPacket = 0; |
|
} |
|
|
|
return S_OK; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: ReleasePreviousPacket() |
|
// Desc: Callback function to release previous packet from a file |
|
//----------------------------------------------------------------------------- |
|
static HRESULT CALLBACK ReleasePreviousPacket( DWORD dwContext, LONGLONG llNextReadByteOffset, |
|
DWORD dwNextPacketSize ) |
|
{ |
|
LOAD_CONTEXT* pContext = ( LOAD_CONTEXT* )dwContext; |
|
if( NULL == pContext ) |
|
return E_FAIL; |
|
|
|
if( dwNextPacketSize != 0 ) |
|
{ |
|
// Start the next load. |
|
pContext->Overlapped.Offset = ( DWORD )( llNextReadByteOffset & 0xFFFFFFFF ); |
|
pContext->Overlapped.OffsetHigh = ( DWORD )( llNextReadByteOffset >> 32 ); |
|
|
|
// Check for bad input file - buffer overrun |
|
if( dwNextPacketSize > pContext->dwPacketSize ) |
|
return E_FAIL; |
|
|
|
pContext->pLoadingPacket = pContext->pPendingReleasePacket; |
|
pContext->pPendingReleasePacket = NULL; |
|
|
|
if( 0 == ReadFile( pContext->hFile, pContext->pLoadingPacket, |
|
dwNextPacketSize, NULL, &pContext->Overlapped ) ) |
|
{ |
|
if( GetLastError() != ERROR_IO_PENDING ) |
|
return HRESULT_FROM_WIN32( GetLastError() ); |
|
} |
|
} |
|
|
|
return S_OK; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: GetNextMemoryPacket() |
|
// Desc: Callback function to get next packet from a file, |
|
// and setup for the next packet. |
|
//----------------------------------------------------------------------------- |
|
static HRESULT CALLBACK GetNextMemoryPacket( DWORD dwContext, VOID** ppPacket, |
|
DWORD* pOffsetToNextPacket ) |
|
{ |
|
LOAD_CONTEXT* pContext = ( LOAD_CONTEXT* )dwContext; |
|
if( NULL == pContext ) |
|
return E_FAIL; |
|
|
|
DWORD dwBytesRead = pContext->inputSize - pContext->readOffset; |
|
if ( pContext->currentPacketSize < dwBytesRead ) |
|
dwBytesRead = pContext->currentPacketSize; |
|
|
|
memcpy( pContext->pLoadingPacket, pContext->pInputBuffer + pContext->readOffset , dwBytesRead ); |
|
pContext->readOffset +=dwBytesRead; |
|
|
|
// Swap pointers so that next time we load it goes into the other packet block. |
|
BYTE* temp = pContext->pLoadingPacket; |
|
pContext->pLoadingPacket = pContext->pDecodingPacket; |
|
pContext->pDecodingPacket = temp; |
|
|
|
// Offset to the next packet. |
|
*pOffsetToNextPacket = dwBytesRead; |
|
|
|
// Set *ppPacket to the data we just loaded. |
|
*ppPacket = pContext->pDecodingPacket; |
|
|
|
return S_OK; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Name: ReleasePreviousMemoryPacket() |
|
// Desc: Callback function to release previous packet from a block of memory, |
|
// and setup for the next packet. |
|
//----------------------------------------------------------------------------- |
|
static HRESULT CALLBACK ReleasePreviousMemoryPacket( DWORD dwContext, LONGLONG llNextReadByteOffset, |
|
DWORD dwNextPacketSize ) |
|
{ |
|
LOAD_CONTEXT* pContext = ( LOAD_CONTEXT* )dwContext; |
|
if( NULL == pContext ) |
|
return E_FAIL; |
|
|
|
// Check for bad input file - buffer overrun |
|
if( dwNextPacketSize > pContext->dwPacketSize ) |
|
return E_FAIL; |
|
|
|
// Record the size of the next packet we are supposed to read, for GetNextMemoryPacket. |
|
pContext->currentPacketSize = dwNextPacketSize; |
|
|
|
return S_OK; |
|
}
|
|
|