mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-14 22:50:45 +00:00
Use libbinkdec to decode bink videos in neo/renderer/Cinematic.cpp
Similar to the ffmpeg integration, but less code (and gets rid of an external dependency) :) Also needed a tiny modification of RB_BindVariableStageImage() to make sure the right shader is used (otherwise it's rendered in greyscale) Also added license note for libbinkdec to README.txt
This commit is contained in:
parent
d4b2ac6667
commit
6d816579a1
3 changed files with 320 additions and 1 deletions
33
README.txt
33
README.txt
|
@ -619,3 +619,36 @@ select the most convenient license for your needs from (1) the
|
|||
GNU GPL, (2) the GNU LGPL, or (3) the Perl Artistic License.
|
||||
|
||||
|
||||
libbinkdec
|
||||
---------------------------------------------------------------------------
|
||||
neo/libs/libbinkdec/*
|
||||
|
||||
Copyright (C) 2011 Barry Duncan
|
||||
|
||||
Based on Bink video decoder from FFmpeg
|
||||
|
||||
Copyright (C) 2009 Konstantin Shishkov
|
||||
Copyright (C) 2011 Peter Ross <pross@xvid.org>
|
||||
|
||||
And generic Code from FFmpeg
|
||||
|
||||
Copyright (C) 2000-2011 FFmpeg team, http://www.ffmpeg.org
|
||||
|
||||
libbinkdec and (the used parts of) FFmpeg are released under LGPL v2.1:
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
(You can find the whole license text on https://www.gnu.org/licenses/lgpl-2.1.html
|
||||
or in neo/libs/libbinkdec/COPYING)
|
||||
|
|
|
@ -66,6 +66,17 @@ extern "C"
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINKDEC
|
||||
// DG: not sure how to use FFMPEG and BINKDEC at the same time.. it might be useful if someone wants to
|
||||
// use binkdec for bink and FFMPEG for other formats in custom code so I didn't just rip FFMPEG out
|
||||
// But right now it's unsupported, if you need this adjust the video loading code etc yourself
|
||||
#ifdef USE_FFMPEG
|
||||
#error "Currently, only one of FFMPEG and BINKDEC is supported at a time!"
|
||||
#endif
|
||||
|
||||
#include <BinkDecoder.h>
|
||||
#endif // USE_BINKDEC
|
||||
|
||||
class idCinematicLocal : public idCinematic
|
||||
{
|
||||
public:
|
||||
|
@ -97,6 +108,19 @@ private:
|
|||
cinData_t ImageForTimeFFMPEG( int milliseconds );
|
||||
bool InitFromFFMPEGFile( const char* qpath, bool looping );
|
||||
void FFMPEGReset();
|
||||
#endif
|
||||
#ifdef USE_BINKDEC
|
||||
BinkHandle binkHandle;
|
||||
cinData_t ImageForTimeBinkDec( int milliseconds );
|
||||
bool InitFromBinkDecFile( const char* qpath, bool looping );
|
||||
void BinkDecReset();
|
||||
|
||||
YUVbuffer yuvBuffer;
|
||||
int framePos;
|
||||
int numFrames;
|
||||
idImage* imgY;
|
||||
idImage* imgCr;
|
||||
idImage* imgCb;
|
||||
#endif
|
||||
idImage* img;
|
||||
bool isRoQ;
|
||||
|
@ -400,6 +424,38 @@ idCinematicLocal::idCinematicLocal()
|
|||
hasFrame = false;
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINKDEC
|
||||
binkHandle.isValid = false;
|
||||
binkHandle.instanceIndex = -1; // whatever this is, it now has a deterministic value
|
||||
framePos = -1;
|
||||
numFrames = 0;
|
||||
|
||||
imgY = globalImages->AllocStandaloneImage( "_cinematicY" );
|
||||
imgCr = globalImages->AllocStandaloneImage( "_cinematicCr" );
|
||||
imgCb = globalImages->AllocStandaloneImage( "_cinematicCb" );
|
||||
{
|
||||
idImageOpts opts;
|
||||
opts.format = FMT_LUM8;
|
||||
opts.colorFormat = CFM_DEFAULT;
|
||||
opts.width = 32;
|
||||
opts.height = 32;
|
||||
opts.numLevels = 1;
|
||||
if( imgY != NULL )
|
||||
{
|
||||
imgY->AllocImage( opts, TF_LINEAR, TR_REPEAT );
|
||||
}
|
||||
if( imgCr != NULL )
|
||||
{
|
||||
imgCr->AllocImage( opts, TF_LINEAR, TR_REPEAT );
|
||||
}
|
||||
if( imgCb != NULL )
|
||||
{
|
||||
imgCb->AllocImage( opts, TF_LINEAR, TR_REPEAT );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Carl: Original Doom 3 RoQ files:
|
||||
image = NULL;
|
||||
status = FMV_EOF;
|
||||
|
@ -457,6 +513,20 @@ idCinematicLocal::~idCinematicLocal()
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINKDEC
|
||||
if( binkHandle.isValid )
|
||||
{
|
||||
Bink_Close( binkHandle );
|
||||
}
|
||||
|
||||
delete imgY;
|
||||
imgY = NULL;
|
||||
delete imgCr;
|
||||
imgCr = NULL;
|
||||
delete imgCr;
|
||||
imgCb = NULL;
|
||||
#endif
|
||||
|
||||
delete img;
|
||||
img = NULL;
|
||||
}
|
||||
|
@ -599,6 +669,80 @@ void idCinematicLocal::FFMPEGReset()
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINKDEC
|
||||
bool idCinematicLocal::InitFromBinkDecFile( const char* qpath, bool amilooping )
|
||||
{
|
||||
int ret;
|
||||
looping = amilooping;
|
||||
startTime = 0;
|
||||
isRoQ = false;
|
||||
CIN_HEIGHT = DEFAULT_CIN_HEIGHT;
|
||||
CIN_WIDTH = DEFAULT_CIN_WIDTH;
|
||||
|
||||
idStr fullpath;
|
||||
idFile* testFile = fileSystem->OpenFileRead( qpath );
|
||||
if( testFile )
|
||||
{
|
||||
fullpath = testFile->GetFullPath();
|
||||
fileSystem->CloseFile( testFile );
|
||||
}
|
||||
// RB: case sensitivity HACK for Linux
|
||||
else if( idStr::Cmpn( qpath, "sound/vo", 8 ) == 0 )
|
||||
{
|
||||
idStr newPath( qpath );
|
||||
newPath.Replace( "sound/vo", "sound/VO" );
|
||||
|
||||
testFile = fileSystem->OpenFileRead( newPath );
|
||||
if( testFile )
|
||||
{
|
||||
fullpath = testFile->GetFullPath();
|
||||
fileSystem->CloseFile( testFile );
|
||||
}
|
||||
else
|
||||
{
|
||||
common->Warning( "idCinematic: Cannot open BinkDec video file: '%s', %d\n", qpath, looping );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
binkHandle = Bink_Open( fullpath );
|
||||
if( !binkHandle.isValid )
|
||||
{
|
||||
common->Warning( "idCinematic: Cannot open BinkDec video file: '%s', %d\n", qpath, looping );
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t w = 0, h = 0;
|
||||
Bink_GetFrameSize( binkHandle, w, h );
|
||||
CIN_WIDTH = w;
|
||||
CIN_HEIGHT = h;
|
||||
}
|
||||
|
||||
frameRate = Bink_GetFrameRate( binkHandle );
|
||||
numFrames = Bink_GetNumFrames( binkHandle );
|
||||
float durationSec = frameRate * numFrames;
|
||||
animationLength = durationSec;
|
||||
buf = NULL;
|
||||
|
||||
common->Printf( "Loaded BinkDec file: '%s', looping=%d%dx%d, %f FPS, %f sec\n", qpath, looping, CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec );
|
||||
|
||||
status = FMV_PLAY;
|
||||
|
||||
startTime = Sys_Milliseconds();
|
||||
memset( yuvBuffer, 0, sizeof( yuvBuffer ) );
|
||||
framePos = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void idCinematicLocal::BinkDecReset()
|
||||
{
|
||||
framePos = -1;
|
||||
Bink_GotoFrame( binkHandle, 0 );
|
||||
status = FMV_LOOPED;
|
||||
}
|
||||
#endif // USE_BINKDEC
|
||||
|
||||
/*
|
||||
==============
|
||||
|
@ -646,6 +790,13 @@ bool idCinematicLocal::InitFromFile( const char* qpath, bool amilooping )
|
|||
fileName = temp;
|
||||
//idLib::Warning( "New filename: '%s'\n", fileName.c_str() );
|
||||
return InitFromFFMPEGFile( fileName.c_str(), amilooping );
|
||||
#elif defined(USE_BINKDEC)
|
||||
idStr temp = fileName.StripFileExtension() + ".bik";
|
||||
animationLength = 0;
|
||||
RoQShutdown();
|
||||
fileName = temp;
|
||||
//idLib::Warning( "New filename: '%s'\n", fileName.c_str() );
|
||||
return InitFromBinkDecFile( fileName.c_str(), amilooping );
|
||||
#else
|
||||
animationLength = 0;
|
||||
return false;
|
||||
|
@ -727,6 +878,14 @@ void idCinematicLocal::Close()
|
|||
status = FMV_EOF;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BINKDEC
|
||||
if( !isRoQ && binkHandle.isValid )
|
||||
{
|
||||
memset( yuvBuffer, 0 , sizeof( yuvBuffer ) );
|
||||
Bink_Close( binkHandle );
|
||||
status = FMV_EOF;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -769,6 +928,10 @@ cinData_t idCinematicLocal::ImageForTime( int thisTime )
|
|||
if( !isRoQ )
|
||||
return ImageForTimeFFMPEG( thisTime );
|
||||
#endif
|
||||
#ifdef USE_BINKDEC // DG: libbinkdec support
|
||||
if( !isRoQ )
|
||||
return ImageForTimeBinkDec( thisTime );
|
||||
#endif
|
||||
|
||||
// Carl: Handle original Doom 3 RoQ video files
|
||||
cinData_t cinData;
|
||||
|
@ -997,6 +1160,128 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime )
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_BINKDEC
|
||||
cinData_t idCinematicLocal::ImageForTimeBinkDec( int thisTime )
|
||||
{
|
||||
cinData_t cinData = {0};
|
||||
|
||||
if( thisTime <= 0 )
|
||||
{
|
||||
thisTime = Sys_Milliseconds();
|
||||
}
|
||||
|
||||
if( r_skipDynamicTextures.GetBool() || status == FMV_EOF || status == FMV_IDLE )
|
||||
{
|
||||
return cinData;
|
||||
}
|
||||
|
||||
if( !binkHandle.isValid )
|
||||
{
|
||||
// RB: .bik requested but not found
|
||||
return cinData;
|
||||
}
|
||||
|
||||
if( startTime == -1 )
|
||||
{
|
||||
BinkDecReset();
|
||||
startTime = thisTime;
|
||||
}
|
||||
|
||||
int desiredFrame = ( ( thisTime - startTime ) * frameRate ) / 1000.0f;
|
||||
if( desiredFrame < 0 )
|
||||
{
|
||||
desiredFrame = 0;
|
||||
}
|
||||
|
||||
if( desiredFrame >= numFrames )
|
||||
{
|
||||
status = FMV_EOF;
|
||||
if( looping )
|
||||
{
|
||||
desiredFrame = 0;
|
||||
BinkDecReset();
|
||||
framePos = -1;
|
||||
startTime = thisTime;
|
||||
status = FMV_PLAY;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = FMV_IDLE;
|
||||
return cinData;
|
||||
}
|
||||
}
|
||||
|
||||
if( desiredFrame == framePos )
|
||||
{
|
||||
cinData.imageWidth = CIN_WIDTH;
|
||||
cinData.imageHeight = CIN_HEIGHT;
|
||||
cinData.status = status;
|
||||
|
||||
cinData.imageY = imgY;
|
||||
cinData.imageCr = imgCr;
|
||||
cinData.imageCb = imgCb;
|
||||
return cinData;
|
||||
}
|
||||
|
||||
// Bink_GotoFrame(binkHandle, desiredFrame);
|
||||
// apparently Bink_GotoFrame() doesn't work super well, so skip frames
|
||||
// (if necessary) by calling Bink_GetNextFrame()
|
||||
while( framePos < desiredFrame )
|
||||
{
|
||||
framePos = Bink_GetNextFrame( binkHandle, yuvBuffer );
|
||||
}
|
||||
|
||||
cinData.imageWidth = CIN_WIDTH;
|
||||
cinData.imageHeight = CIN_HEIGHT;
|
||||
cinData.status = status;
|
||||
|
||||
double invAspRat = double( CIN_HEIGHT ) / double( CIN_WIDTH );
|
||||
|
||||
idImage* imgs[3] = {imgY, imgCb, imgCr}; // that's the order of the channels in yuvBuffer[]
|
||||
for( int i = 0; i < 3; ++i )
|
||||
{
|
||||
// Note: img->UploadScratch() seems to assume 32bit per pixel data, but this is 8bit/pixel
|
||||
// so uploading is a bit more manual here (compared to ffmpeg or RoQ)
|
||||
idImage* img = imgs[i];
|
||||
int w = yuvBuffer[i].width;
|
||||
int h = yuvBuffer[i].height;
|
||||
// some videos, including the logo video and the main menu background,
|
||||
// seem to have superfluous rows in at least some of the channels,
|
||||
// leading to a black or glitchy bar at the bottom of the video.
|
||||
// cut that off by reducing the height to the expected height
|
||||
if( h > CIN_HEIGHT )
|
||||
{
|
||||
h = CIN_HEIGHT;
|
||||
}
|
||||
else if( h < CIN_HEIGHT )
|
||||
{
|
||||
// the U and V channels have a lower resolution than the Y channel
|
||||
// (or the logical video resolution), so use the aspect ratio to
|
||||
// calculate the real height
|
||||
int hExp = invAspRat * w + 0.5;
|
||||
if( h > hExp )
|
||||
h = hExp;
|
||||
}
|
||||
|
||||
if( img->GetUploadWidth() != w || img->GetUploadHeight() != h )
|
||||
{
|
||||
idImageOpts opts = img->GetOpts();
|
||||
opts.width = w;
|
||||
opts.height = h;
|
||||
img->AllocImage( opts, TF_LINEAR, TR_REPEAT );
|
||||
}
|
||||
img->SubImageUpload( 0, 0, 0, 0, w, h, yuvBuffer[i].data );
|
||||
}
|
||||
|
||||
cinData.imageY = imgY;
|
||||
cinData.imageCr = imgCr;
|
||||
cinData.imageCb = imgCb;
|
||||
|
||||
return cinData;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
==============
|
||||
idCinematicLocal::move8_32
|
||||
|
|
|
@ -433,7 +433,8 @@ static void RB_BindVariableStageImage( const textureStage_t* texture, const floa
|
|||
cin.imageCr->Bind();
|
||||
GL_SelectTexture( 2 );
|
||||
cin.imageCb->Bind();
|
||||
|
||||
// DG: imageY is only used for bink videos (with libbinkdec), so the bink shader must be used
|
||||
renderProgManager.BindShader_Bink();
|
||||
}
|
||||
else if( cin.image != NULL )
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue