Fixed FFmpeg video timings and crashes. Also fixes the black screen problem with Ubuntu 14.04 #92

This commit is contained in:
Robert Beckebans 2014-04-21 23:56:46 +02:00
parent e47429f532
commit 8943a6d4a2
6 changed files with 180 additions and 23 deletions

View file

@ -16,7 +16,7 @@ option(OPENAL
"Use OpenAL soft instead of XAudio2" OFF)
option(FFMPEG
"Use FMPEG to render Bink videos" OFF)
"Use FMPEG to render Bink videos" ON)
if(UNIX)
set(OPENAL TRUE)

View file

@ -113,8 +113,8 @@ idGameEdit* gameEdit = NULL;
idCommonLocal commonLocal;
idCommon* common = &commonLocal;
idCVar com_skipIntroVideos( "com_skipIntroVideos", "0", CVAR_BOOL , "skips intro videos" );
// RB: defaulted this to 1 because we don't have a sound for the intro .bik video
idCVar com_skipIntroVideos( "com_skipIntroVideos", "1", CVAR_BOOL , "skips intro videos" );
// For doom classic
struct Globals;
@ -900,14 +900,101 @@ void idCommonLocal::RenderBink( const char* path )
material->Parse( materialText.c_str(), materialText.Length(), false );
material->ResetCinematicTime( Sys_Milliseconds() );
while( Sys_Milliseconds() <= material->GetCinematicStartTime() + material->CinematicLength() )
// RB: FFmpeg might return the wrong play length so I changed the intro video to play max 30 seconds until finished
int cinematicLength = 30000; //material->CinematicLength();
int mouseEvents[MAX_MOUSE_EVENTS][2];
bool escapeEvent = false;
while( ( Sys_Milliseconds() <= ( material->GetCinematicStartTime() + cinematicLength ) ) && material->CinematicIsPlaying() )
{
renderSystem->DrawStretchPic( chop, 0, imageWidth, renderSystem->GetVirtualHeight(), 0, 0, 1, 1, material );
const emptyCommand_t* cmd = renderSystem->SwapCommandBuffers( &time_frontend, &time_backend, &time_shadows, &time_gpu );
renderSystem->RenderCommandBuffers( cmd );
Sys_GenerateEvents();
// queue system events ready for polling
Sys_GetEvent();
// RB: allow to escape video by pressing anything
int numKeyEvents = Sys_PollKeyboardInputEvents();
if( numKeyEvents > 0 )
{
for( int i = 0; i < numKeyEvents; i++ )
{
int key;
bool state;
if( Sys_ReturnKeyboardInputEvent( i, key, state ) )
{
if( key == K_ESCAPE && state == true )
{
escapeEvent = true;
}
break;
}
}
Sys_EndKeyboardInputEvents();
}
int numMouseEvents = Sys_PollMouseInputEvents( mouseEvents );
if( numMouseEvents > 0 )
{
for( int i = 0; i < numMouseEvents; i++ )
{
int action = mouseEvents[i][0];
switch( action )
{
case M_ACTION1:
case M_ACTION2:
case M_ACTION3:
case M_ACTION4:
case M_ACTION5:
case M_ACTION6:
case M_ACTION7:
case M_ACTION8:
escapeEvent = true;
break;
default: // some other undefined button
break;
}
}
}
int numJoystickEvents = Sys_PollJoystickInputEvents( 0 );
if( numJoystickEvents > 0 )
{
for( int i = 0; i < numJoystickEvents; i++ )
{
int action;
int value;
if( Sys_ReturnJoystickInputEvent( i, action, value ) )
{
if( action >= J_ACTION1 && action <= J_ACTION_MAX )
{
if( value != 0 )
{
escapeEvent = true;
break;
}
}
}
}
Sys_EndJoystickInputEvents();
}
if( escapeEvent )
{
break;
}
Sys_Sleep( 10 );
}
// RB end
material->MakeDefault();
}

View file

@ -75,6 +75,9 @@ public:
virtual bool InitFromFile( const char* qpath, bool looping );
virtual cinData_t ImageForTime( int milliseconds );
virtual int AnimationLength();
// RB begin
bool IsPlaying() const;
// RB end
virtual void Close();
virtual void ResetTime( int time );
@ -89,7 +92,7 @@ private:
AVCodecContext* dec_ctx;
SwsContext* img_convert_ctx;
bool hasFrame;
long FramePos;
long framePos;
cinData_t ImageForTimeFFMPEG( int milliseconds );
bool InitFromFFMPEGFile( const char* qpath, bool looping );
@ -359,6 +362,13 @@ void idCinematic::Close()
{
}
// RB begin
bool idCinematic::IsPlaying() const
{
return false;
}
// RB end
//===========================================
@ -511,19 +521,25 @@ bool idCinematicLocal::InitFromFFMPEGFile( const char* qpath, bool amilooping )
float durationSec = static_cast<double>( fmt_ctx->streams[video_stream_index]->duration ) * static_cast<double>( ticksPerFrame ) / static_cast<double>( avr.den );
animationLength = durationSec * 1000;
frameRate = av_q2d( fmt_ctx->streams[video_stream_index]->r_frame_rate );
startTime = 0;
buf = NULL;
hasFrame = false;
FramePos = -1;
framePos = -1;
common->Warning( "%dx%d, %f FPS, %f sec", CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec );
image = ( byte* )Mem_Alloc( CIN_WIDTH * CIN_HEIGHT * 4 * 2, TAG_CINEMATIC );
avpicture_fill( ( AVPicture* )frame2, image, PIX_FMT_BGR32, CIN_WIDTH, CIN_HEIGHT );
if( img_convert_ctx )
{
sws_freeContext( img_convert_ctx );
}
img_convert_ctx = sws_getContext( dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, CIN_WIDTH, CIN_HEIGHT, PIX_FMT_BGR32, SWS_BICUBIC, NULL, NULL, NULL );
status = FMV_PLAY;
startTime = 0;
ImageForTime( 0 );
status = ( looping ) ? FMV_PLAY : FMV_IDLE;
//startTime = Sys_Milliseconds();
return true;
}
#endif
@ -536,10 +552,19 @@ idCinematicLocal::FFMPEGReset
#if defined(USE_FFMPEG)
void idCinematicLocal::FFMPEGReset()
{
startTime = 0;
FramePos = -1;
av_seek_frame( fmt_ctx, video_stream_index, 0, 0 );
status = FMV_LOOPED;
// RB: don't reset startTime here because that breaks video replays in the PDAs
//startTime = 0;
framePos = -1;
if( av_seek_frame( fmt_ctx, video_stream_index, 0, 0 ) >= 0 )
{
status = FMV_LOOPED;
}
else
{
status = FMV_EOF;
}
}
#endif
@ -683,6 +708,13 @@ int idCinematicLocal::AnimationLength()
return animationLength;
}
// RB begin
bool idCinematicLocal::IsPlaying() const
{
return ( status == FMV_PLAY );
}
// RB end
/*
==============
idCinematicLocal::ResetTime
@ -840,6 +872,12 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime )
return cinData;
}
if( !fmt_ctx )
{
// RB: .bik requested but not found
return cinData;
}
if( ( !hasFrame ) || startTime == -1 )
{
if( startTime == -1 )
@ -849,10 +887,18 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime )
startTime = thisTime;
}
long DesiredFrame = ( ( thisTime - startTime ) * frameRate ) / 1000;
if( DesiredFrame < 0 ) DesiredFrame = 0;
if( DesiredFrame < FramePos ) FFMPEGReset();
if( hasFrame && DesiredFrame == FramePos )
long desiredFrame = ( ( thisTime - startTime ) * frameRate ) / 1000;
if( desiredFrame < 0 )
{
desiredFrame = 0;
}
if( desiredFrame < framePos )
{
FFMPEGReset();
}
if( hasFrame && desiredFrame == framePos )
{
cinData.imageWidth = CIN_WIDTH;
cinData.imageHeight = CIN_HEIGHT;
@ -862,22 +908,23 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime )
}
AVPacket packet;
while( FramePos < DesiredFrame )
while( framePos < desiredFrame )
{
int frameFinished = 0;
//Do a single frame by getting packets until we have a full frame
// Do a single frame by getting packets until we have a full frame
while( !frameFinished )
{
//if we got to the end or failed
// if we got to the end or failed
if( av_read_frame( fmt_ctx, &packet ) < 0 )
{
//can't read any more, set to EOF
// can't read any more, set to EOF
status = FMV_EOF;
if( looping )
{
DesiredFrame = 0;
desiredFrame = 0;
FFMPEGReset();
FramePos = -1;
framePos = -1;
startTime = thisTime;
if( av_read_frame( fmt_ctx, &packet ) < 0 )
{
@ -901,8 +948,10 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime )
// Free the packet that was allocated by av_read_frame
av_free_packet( &packet );
}
FramePos++;
framePos++;
}
// We have reached the desired frame
// Convert the image from its native format to RGB
sws_scale( img_convert_ctx, frame->data, frame->linesize, 0, dec_ctx->height, frame2->data, frame2->linesize );
@ -912,6 +961,7 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime )
img->UploadScratch( image, CIN_WIDTH, CIN_HEIGHT );
hasFrame = true;
cinData.image = img;
return cinData;
}
#endif

View file

@ -89,6 +89,10 @@ public:
// returns the length of the animation in milliseconds
virtual int AnimationLength();
// RB: let us know wether this video went EOF or is still active
virtual bool IsPlaying() const;
// RB end
// the pointers in cinData_t will remain valid until the next UpdateForTime() call
virtual cinData_t ImageForTime( int milliseconds );

View file

@ -3157,6 +3157,18 @@ int idMaterial::GetCinematicStartTime() const
return -1;
}
// RB: added because we can't rely on the FFmpeg feedback how long a video really is
bool idMaterial::CinematicIsPlaying() const
{
if( !stages || !stages[0].texture.cinematic )
{
return 0;
}
return stages[0].texture.cinematic->IsPlaying();
}
// RB end
/*
==================
idMaterial::CheckForConstantRegisters

View file

@ -739,6 +739,10 @@ public:
void UpdateCinematic( int time ) const;
// RB: added because we can't rely on the FFmpeg feedback how long a video really is
bool CinematicIsPlaying() const;
// RB end
//------------------------------------------------------------------
// gets an image for the editor to use