491 lines
11 KiB
C++
491 lines
11 KiB
C++
/*
|
|
* This version of BinkVideo.cpp now ONLY works on Xbox.
|
|
* GCN support is hosed.
|
|
*/
|
|
#include "snd_local_console.h"
|
|
#include "../renderer/tr_local.h"
|
|
#include "BinkVideo.h"
|
|
#include "RAD.h"
|
|
#include "../win32/xbox_texture_man.h"
|
|
#include <xgraphics.h>
|
|
|
|
#include "../client/client.h"
|
|
|
|
char *binkSndMem = NULL;
|
|
|
|
// Taste the hackery!
|
|
extern void *BonePoolTempAlloc( unsigned long size );
|
|
extern void BonePoolTempFree( void *p );
|
|
extern void *TempAlloc( unsigned long size );
|
|
extern void TempFree( void );
|
|
|
|
extern void SP_DrawSPLoadScreen( void );
|
|
|
|
// Allocation wrappers, that go to our static 2.5MB buffer:
|
|
static void PTR4* RADEXPLINK AllocWrapper(U32 size)
|
|
{
|
|
// Give bink pre-initialized sound mem on xbox
|
|
if(size == XBOX_BINK_SND_MEM) {
|
|
return binkSndMem;
|
|
}
|
|
|
|
return BinkVideo::Allocate(size);
|
|
}
|
|
|
|
static void RADEXPLINK FreeWrapper(void PTR4* ptr)
|
|
{
|
|
BinkVideo::Free(ptr);
|
|
}
|
|
|
|
|
|
/*********
|
|
BinkVideo
|
|
*********/
|
|
BinkVideo::BinkVideo()
|
|
{
|
|
bink = NULL;
|
|
buffer = NULL;
|
|
x1 = 0.0f;
|
|
y1 = 0.0f;
|
|
x2 = 0.0f;
|
|
y2 = 0.0f;
|
|
status = NS_BV_STOPPED;
|
|
looping = false;
|
|
alpha = false;
|
|
initialized = false;
|
|
loadScreenOnStop = false;
|
|
|
|
Image[0].surface = NULL;
|
|
Image[0].texture = NULL;
|
|
Image[1].surface = NULL;
|
|
Image[1].texture = NULL;
|
|
|
|
stopNextFrame = false;
|
|
}
|
|
|
|
/*********
|
|
~BinkVideo
|
|
*********/
|
|
BinkVideo::~BinkVideo()
|
|
{
|
|
Free(buffer);
|
|
BinkClose(bink);
|
|
}
|
|
|
|
/*********
|
|
AllocateXboxMem
|
|
Pre-Allocates sound memory for xbox to avoid fragmenting
|
|
*********/
|
|
void BinkVideo::AllocateXboxMem(void)
|
|
{
|
|
// binkSndMem = (char*)Allocate(XBOX_BINK_SND_MEM);
|
|
// Force the sound memory to come from the Zone:
|
|
binkSndMem = (char *) Z_Malloc(XBOX_BINK_SND_MEM, TAG_BINK, qfalse, 32);
|
|
initialized = true;
|
|
}
|
|
|
|
/*********
|
|
FreeXboxMem
|
|
*********/
|
|
void BinkVideo::FreeXboxMem(void)
|
|
{
|
|
initialized = false;
|
|
Z_Free(binkSndMem);
|
|
}
|
|
|
|
|
|
/*********
|
|
Start
|
|
Opens a bink file and gets it ready to play
|
|
*********/
|
|
bool BinkVideo::Start(const char *filename, float xOrigin, float yOrigin, float width, float height)
|
|
{
|
|
assert(initialized);
|
|
|
|
// Check to see if a video is being played.
|
|
if(status == NS_BV_PLAYING)
|
|
{
|
|
// stop
|
|
this->Stop();
|
|
}
|
|
|
|
// Hack! Remember if this was the logo movies, so that we can show the load screen later:
|
|
if( strstr( filename, "logos" ) )
|
|
loadScreenOnStop = true;
|
|
else
|
|
loadScreenOnStop = false;
|
|
|
|
// Blow away all sounds that aren't playing - this helps prevent crashing:
|
|
SND_FreeOldestSound();
|
|
|
|
// Just use the zone for bink allocations:
|
|
RADSetMemory( AllocWrapper, FreeWrapper );
|
|
|
|
// Set up sound for consoles
|
|
|
|
// We are on XBox, tell Bink to play all of the 5.1 tracks
|
|
// U32 TrackIDsToPlay[ 4 ] = { 0, 1, 2, 3 };
|
|
// BinkSetSoundTrack( 4, TrackIDsToPlay );
|
|
|
|
// Now route the sound tracks to the correct speaker
|
|
// U32 bins[ 2 ];
|
|
|
|
// bins[ 0 ] = DSMIXBIN_FRONT_LEFT;
|
|
// bins[ 1 ] = DSMIXBIN_FRONT_RIGHT;
|
|
// BinkSetMixBins( bink, 0, bins, 2 );
|
|
// bins[ 0 ] = DSMIXBIN_FRONT_CENTER;
|
|
// BinkSetMixBins( bink, 1, bins, 1 );
|
|
// bins[ 0 ] = DSMIXBIN_LOW_FREQUENCY;
|
|
// BinkSetMixBins( bink, 2, bins, 1 );
|
|
// bins[ 0 ] = DSMIXBIN_BACK_LEFT;
|
|
// bins[ 1 ] = DSMIXBIN_BACK_RIGHT;
|
|
// BinkSetMixBins( bink, 3, bins, 2 );
|
|
|
|
// Try to open the Bink file.
|
|
// bink = BinkOpen( filename, BINKSNDTRACK | BINKALPHA );
|
|
bink = BinkOpen( filename, BINKALPHA );
|
|
if(!bink)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
assert(bink->Width <= MAX_WIDTH && bink->Height <=MAX_HEIGHT);
|
|
|
|
// Did the source .bik file have an alpha plane?
|
|
alpha = (bool)(bink->OpenFlags & BINKALPHA);
|
|
|
|
// set the height, width, etc...
|
|
x1 = xOrigin;
|
|
y1 = yOrigin;
|
|
x2 = x1 + width;
|
|
y2 = y1 + height;
|
|
|
|
// flush any background sound reads
|
|
extern void S_DrainRawSoundData(void);
|
|
S_DrainRawSoundData();
|
|
|
|
// Full-screen movies (without alpha) need a pair of YUV2 textures:
|
|
if( !alpha )
|
|
{
|
|
// YUY2 is 16 bpp? But we need two:
|
|
gTextures.SwapTextureMemory( (bink->Width * bink->Height * 4) + 1024 );
|
|
|
|
// Make our two textures:
|
|
Image[0].texture = new IDirect3DTexture9;
|
|
Image[1].texture = new IDirect3DTexture9;
|
|
|
|
// Fill in the texture headers:
|
|
DWORD pixelSize =
|
|
XGSetTextureHeader( bink->Width,
|
|
bink->Height,
|
|
1,
|
|
0,
|
|
D3DFMT_YUY2,
|
|
0,
|
|
Image[0].texture,
|
|
0,
|
|
0 );
|
|
|
|
XGSetTextureHeader( bink->Width,
|
|
bink->Height,
|
|
1,
|
|
0,
|
|
D3DFMT_YUY2,
|
|
0,
|
|
Image[1].texture,
|
|
0,
|
|
0 );
|
|
|
|
// texNum is unused:
|
|
Image[0].texture->Register( gTextures.Allocate( pixelSize, 0 ) );
|
|
Image[1].texture->Register( gTextures.Allocate( pixelSize, 0 ) );
|
|
|
|
// Turn on overlays:
|
|
glw_state->device->EnableOverlay( TRUE );
|
|
|
|
// Get surface pointers:
|
|
Image[0].texture->GetSurfaceLevel( 0, &Image[0].surface );
|
|
Image[1].texture->GetSurfaceLevel( 0, &Image[1].surface );
|
|
|
|
// Just to be safe:
|
|
currentImage = 0;
|
|
buffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Planet movies (with alpha) re-use tr.binkPlanetImage, so no texture setup
|
|
// is needed. But we do need a temporary buffer to decompress into. Let's steal
|
|
// from the bone pool.
|
|
buffer = BonePoolTempAlloc( bink->Width * bink->Height * 4 );
|
|
}
|
|
|
|
status = NS_BV_PLAYING;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*********
|
|
Run
|
|
Decompresses a frame, renders it to the screen, and advances to
|
|
the next frame. Only used for full-screen movies (no alpha).
|
|
*********/
|
|
bool BinkVideo::Run(void)
|
|
{
|
|
// Make sure movie is running:
|
|
if( status == NS_BV_STOPPED )
|
|
return false;
|
|
|
|
// Wait for proper frame timing:
|
|
while(BinkWait(bink));
|
|
|
|
// Are we supposed to stop now?
|
|
if( stopNextFrame )
|
|
{
|
|
stopNextFrame = false;
|
|
Stop();
|
|
return false;
|
|
}
|
|
|
|
// Try to decompress the frame:
|
|
if( DecompressFrame( &Image[currentImage ^ 1] ) == 0 )
|
|
{
|
|
// The blt succeeded, update our current image index.
|
|
currentImage ^= 1;
|
|
|
|
// Draw the next frame.
|
|
Draw( &Image[currentImage] );
|
|
}
|
|
|
|
// Are we done? Set a flag, we don't want to stop until next frame, so the
|
|
// last frame stays up for the right amount of time!
|
|
if( bink->FrameNum == bink->Frames && !looping )
|
|
{
|
|
stopNextFrame = true;
|
|
}
|
|
|
|
// Keep playing:
|
|
BinkNextFrame( bink );
|
|
|
|
// Are we done?
|
|
/*
|
|
if( bink->FrameNum == (bink->Frames - 1) && !looping )
|
|
{
|
|
Stop();
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
|
|
/*********
|
|
GetBinkData
|
|
Returns the buffer data for the next frame of the video - only used for
|
|
movies with alpha (the planets).
|
|
|
|
This doesn't follow Bink guidelines. They suggest that you call BinkWait()
|
|
very frequently, something like 4 to 5 times as fast as the framerate of
|
|
the movie. We're technically coming close to that, but this code won't work
|
|
if we have videoMap shaders with higher framerates than the planets (8).
|
|
*********/
|
|
void* BinkVideo::GetBinkData(void)
|
|
{
|
|
assert( alpha );
|
|
|
|
if (!BinkWait(bink))
|
|
{
|
|
BinkDoFrame(bink);
|
|
|
|
BinkCopyToBuffer( bink,
|
|
buffer,
|
|
bink->Width * 4, // Pitch
|
|
bink->Height,
|
|
0,
|
|
0,
|
|
BINKCOPYALL | BINKSURFACE32A );
|
|
|
|
BinkNextFrame(bink);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/********
|
|
Draw
|
|
Draws the current movie full-screen
|
|
********/
|
|
void BinkVideo::Draw( OVERLAYINFO * oi )
|
|
{
|
|
// Draw the image on the screen (centered)...
|
|
RECT dst_rect = { 0, 0, 640, 480 };
|
|
RECT src_rect = { 0, 0, bink->Width, bink->Height };
|
|
|
|
// Update this bugger.
|
|
glw_state->device->UpdateOverlay( oi->surface, &src_rect, &dst_rect, FALSE, 0 );
|
|
}
|
|
|
|
/*********
|
|
Stop
|
|
Stops the current movie, and clears it from memory
|
|
*********/
|
|
void BinkVideo::Stop(void)
|
|
{
|
|
if( bink ) {
|
|
BinkClose( bink );
|
|
}
|
|
|
|
if( alpha )
|
|
{
|
|
// Release all the temp space we grabbed, no texture cleanup to do:
|
|
if( buffer )
|
|
BonePoolTempFree( buffer );
|
|
}
|
|
else
|
|
{
|
|
// We wrap all this in a single check - it should be all or nothing:
|
|
if( Image[0].surface )
|
|
{
|
|
// Release surfaces:
|
|
Image[0].surface->Release();
|
|
Image[0].surface = NULL;
|
|
|
|
Image[1].surface->Release();
|
|
Image[1].surface = NULL;
|
|
|
|
// Clean up the textures we made for the overlay stuff:
|
|
Image[0].texture->BlockUntilNotBusy();
|
|
delete Image[0].texture;
|
|
Image[0].texture = NULL;
|
|
|
|
Image[1].texture->BlockUntilNotBusy();
|
|
delete Image[1].texture;
|
|
Image[1].texture = NULL;
|
|
|
|
// We're going to be stripping the overlay off, leave a black screen,
|
|
// unless it was the logo movies that we just played, in which case we
|
|
// draw the loading screen!
|
|
if( loadScreenOnStop )
|
|
{
|
|
SP_DrawSPLoadScreen();
|
|
glw_state->device->BlockUntilVerticalBlank();
|
|
|
|
// Filth. Don't call Present until this gets cleared.
|
|
extern bool connectSwapOverride;
|
|
connectSwapOverride = true;
|
|
}
|
|
else
|
|
{
|
|
glw_state->device->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_COLORVALUE(0, 0, 0, 0), 0, 0 );
|
|
glw_state->device->Present( NULL, NULL, NULL, NULL );
|
|
glw_state->device->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_COLORVALUE(0, 0, 0, 0), 0, 0 );
|
|
glw_state->device->Present( NULL, NULL, NULL, NULL );
|
|
glw_state->device->BlockUntilVerticalBlank();
|
|
}
|
|
|
|
// Turn overlays back off:
|
|
glw_state->device->EnableOverlay( FALSE );
|
|
|
|
// Restore the textures that we dumped to disk
|
|
gTextures.UnswapTextureMemory();
|
|
}
|
|
}
|
|
|
|
x1 = 0.0f;
|
|
y1 = 0.0f;
|
|
x2 = 0.0f;
|
|
y2 = 0.0f;
|
|
buffer = NULL;
|
|
bink = NULL;
|
|
status = NS_BV_STOPPED;
|
|
|
|
// Now free all the temp memory that Bink took with it's internal allocations:
|
|
TempFree();
|
|
|
|
if( !alpha && (cls.state == CA_CINEMATIC || cls.state == CA_ACTIVE) )
|
|
re.InitDissolve(qfalse);
|
|
}
|
|
|
|
/*********
|
|
SetExtents
|
|
Sets dimmension variables
|
|
*********/
|
|
|
|
void BinkVideo::SetExtents(float xOrigin, float yOrigin, float width, float height)
|
|
{
|
|
x1 = xOrigin;
|
|
y1 = yOrigin;
|
|
x2 = x1 + width;
|
|
y2 = y1 + height;
|
|
}
|
|
|
|
/*********
|
|
SetMasterVolume
|
|
Sets the volume of the specified track
|
|
*********/
|
|
void BinkVideo::SetMasterVolume(s32 volume)
|
|
{
|
|
int i;
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
BinkSetVolume(bink,i,volume);
|
|
}
|
|
}
|
|
|
|
/*********
|
|
DecompressFrame
|
|
Decompresses current frame and copies the data to
|
|
the buffer
|
|
*********/
|
|
S32 BinkVideo::DecompressFrame( OVERLAYINFO *oi )
|
|
{
|
|
s32 copy_skipped;
|
|
D3DLOCKED_RECT lock_rect;
|
|
|
|
// Decompress the Bink frame.
|
|
BinkDoFrame( bink );
|
|
|
|
// Lock the 3D image so that we can copy the decompressed frame into it.
|
|
oi->texture->LockRect( 0, &lock_rect, 0, 0 );
|
|
|
|
// Copy the decompressed frame into the 3D image.
|
|
copy_skipped = BinkCopyToBuffer( bink,
|
|
lock_rect.pBits,
|
|
lock_rect.Pitch,
|
|
bink->Height,
|
|
0, 0,
|
|
BINKSURFACEYUY2 | BINKCOPYALL );
|
|
|
|
// Unlock the 3D image.
|
|
oi->texture->UnlockRect( 0 );
|
|
|
|
return copy_skipped;
|
|
}
|
|
|
|
/*********
|
|
Allocate
|
|
Allocates memory for the frame buffer
|
|
*********/
|
|
void *BinkVideo::Allocate(U32 size)
|
|
{
|
|
void *retVal = TempAlloc( size );
|
|
|
|
// Fall back to Zone if we didn't get it
|
|
if( !retVal )
|
|
retVal = Z_Malloc(size, TAG_BINK, qfalse, 32);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/*********
|
|
FreeBuffer
|
|
Releases the frame buffer memory
|
|
*********/
|
|
void BinkVideo::Free(void* ptr)
|
|
{
|
|
// Did this pointer come from the Zone up above?
|
|
if( !Z_IsFromTempPool( ptr ) )
|
|
Z_Free(ptr);
|
|
|
|
// Else, do nothing - we don't free temp allocations until movie is done
|
|
}
|