diff --git a/neo/renderer/Cinematic.cpp b/neo/renderer/Cinematic.cpp index 891d739e..3d7a5baf 100644 --- a/neo/renderer/Cinematic.cpp +++ b/neo/renderer/Cinematic.cpp @@ -130,9 +130,8 @@ private: cinData_t ImageForTimeFFMPEG( int milliseconds, nvrhi::ICommandList* commandList ); bool InitFromFFMPEGFile( const char* qpath, bool looping, nvrhi::ICommandList* commandList ); void FFMPEGReset(); - uint8_t* lagBuffer[NUM_LAG_FRAMES] = {}; - int lagBufSize[NUM_LAG_FRAMES] = {}; - int lagIndex; + std::queue lagBuffer; + std::queue lagBufSize; bool skipLag; #endif #ifdef USE_BINKDEC @@ -469,7 +468,6 @@ idCinematicLocal::idCinematicLocal() img_convert_ctx = NULL; hasFrame = false; framePos = -1; - lagIndex = 0; skipLag = false; #endif @@ -842,14 +840,11 @@ void idCinematicLocal::FFMPEGReset() { cinematicAudio->ResetAudio(); - lagIndex = 0; - for( int i = 0; i < NUM_LAG_FRAMES; i++ ) + while( !lagBuffer.empty() ) { - lagBufSize[ i ] = 0; - if( lagBuffer[ i ] ) - { - av_freep( &lagBuffer[ i ] ); - } + av_freep( &lagBuffer.front() ); + lagBuffer.pop(); + lagBufSize.pop(); } } @@ -910,7 +905,7 @@ bool idCinematicLocal::InitFromBinkDecFile( const char* qpath, bool amilooping, } else { - common->Warning( "idCinematic: Cannot open BinkDec video file: '%s', %d\n", qpath, looping ); + common->Warning( "idCinematic: Cannot open Bink video file: '%s', %d\n", qpath, looping ); return false; } } @@ -918,7 +913,7 @@ bool idCinematicLocal::InitFromBinkDecFile( const char* qpath, bool amilooping, binkHandle = Bink_Open( fullpath ); if( !binkHandle.isValid ) { - common->Warning( "idCinematic: Cannot open BinkDec video file: '%s', %d\n", qpath, looping ); + common->Warning( "idCinematic: Cannot open Bink video file: '%s', %d\n", qpath, looping ); return false; } @@ -948,7 +943,7 @@ bool idCinematicLocal::InitFromBinkDecFile( const char* qpath, bool amilooping, numFrames = Bink_GetNumFrames( binkHandle ); float durationSec = numFrames / frameRate; // SRS - fixed Bink durationSec calculation animationLength = durationSec * 1000; // SRS - animationLength is in milliseconds - common->Printf( "Loaded BinkDec file: '%s', looping=%d, %dx%d, %3.2f FPS, %4.1f sec\n", qpath, looping, CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec ); + common->Printf( "Loaded Bink file: '%s', looping=%d, %dx%d, %3.2f FPS, %4.1f sec\n", qpath, looping, CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec ); memset( yuvBuffer, 0, sizeof( yuvBuffer ) ); @@ -1174,14 +1169,11 @@ void idCinematicLocal::Close() swr_free( &swr_ctx ); } - lagIndex = 0; - for( int i = 0; i < NUM_LAG_FRAMES; i++ ) + while( !lagBuffer.empty() ) { - lagBufSize[ i ] = 0; - if( lagBuffer[ i ] ) - { - av_freep( &lagBuffer[ i ] ); - } + av_freep( &lagBuffer.front() ); + lagBuffer.pop(); + lagBufSize.pop(); } } @@ -1378,6 +1370,7 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis char error[64]; uint8_t* audioBuffer = NULL; int num_bytes = 0; + bool syncLost = false; memset( &cinData, 0, sizeof( cinData ) ); if( !fmt_ctx ) @@ -1491,8 +1484,8 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis common->Warning( "idCinematic: Failed to receive audio frame from decoding with error: %s\n", error ); } } - // SRS - For the final (desired) frame only: allocate audio buffers, convert to packed format, and play the synced audio frames - else if( framePos + 1 == desiredFrame ) + // SRS - Allocate audio buffer, convert to packed format, save in queue, and play synced audio for desired frame + else { // SRS - Since destination sample format is packed (non-planar), returned bufflinesize equals num_bytes #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59,37,100) @@ -1521,28 +1514,48 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis memcpy( audioBuffer, frame3->extended_data[0], num_bytes ); } } - // SRS - If we have cinematic audio data, play a lagged frame (for bik video sync) and save the current frame + + // SRS - If we have cinematic audio data, save the current frame onto the back of the queue if( num_bytes > 0 ) { - // SRS - If we have a lagged cinematic audio frame, then play it now - if( lagBufSize[ lagIndex ] > 0 ) + // SRS - If queue is at max size we have lost a/v sync: drop frame and set syncLost flag + if( lagBuffer.size() == ( skipLag ? 1 : NUM_LAG_FRAMES ) ) { - // SRS - Note that PlayAudio() is responsible for releasing any audio buffers sent to it - cinematicAudio->PlayAudio( lagBuffer[ lagIndex ], lagBufSize[ lagIndex ] ); + av_freep( &lagBuffer.front() ); + lagBuffer.pop(); + lagBufSize.pop(); + + syncLost = true; } - // SRS - Save the current (new) audio buffer and its size to play in the future - lagBuffer[ lagIndex ] = audioBuffer; - lagBufSize[ lagIndex ] = num_bytes; - - // SRS - If skipLag is true (e.g. RoQ, mp4, webm), reduce lag to 1 frame since AV playback is already synced - lagIndex = ( lagIndex + 1 ) % ( skipLag ? 1 : NUM_LAG_FRAMES ); + // SRS - Save the current (new) audio buffer and its size to play during the desired frame + lagBuffer.push( audioBuffer ); + lagBufSize.push( num_bytes ); } // SRS - Not sure if an audioBuffer can ever be allocated on failure, but check and free just in case else if( audioBuffer ) { av_freep( &audioBuffer ); } + + // SRS - If we have any synced audio frames available for the desired frame, play now and drain queue + if( framePos + 1 == desiredFrame ) + { + if( syncLost ) + { + // SRS - If we have lost sync, reset / resync audio stream before starting to play again + cinematicAudio->ResetAudio(); + syncLost = false; + } + + while( !lagBuffer.empty() ) + { + // SRS - Note that PlayAudio() is responsible for releasing any audio buffers sent to it + cinematicAudio->PlayAudio( lagBuffer.front(), lagBufSize.front() ); + lagBuffer.pop(); + lagBufSize.pop(); + } + } //common->Printf( "idCinematic: video pts = %7.3f, audio pts = %7.3f, samples = %4d, num_bytes = %5d\n", static_cast( frame->pts ) * av_q2d( dec_ctx->pkt_timebase ), static_cast( frame3->pts ) * av_q2d( dec_ctx2->pkt_timebase ), frame3->nb_samples, num_bytes ); } }