From 10e4323e3e99b2de7d43474ca22ce12d5f1c4f2d Mon Sep 17 00:00:00 2001 From: Brandon W Date: Sun, 28 Jul 2024 16:03:53 -0400 Subject: [PATCH] Add DecodeNextFrame and DecodeMovie functions to populate the FrameBuffer. --- src/arch/MovieTexture/MovieTexture_FFMpeg.cpp | 61 +++++++++++++++++++ src/arch/MovieTexture/MovieTexture_FFMpeg.h | 17 ++++++ src/arch/MovieTexture/MovieTexture_Generic.h | 12 ++++ 3 files changed, 90 insertions(+) diff --git a/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp b/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp index 46ec2e7b46..c6a693efc8 100644 --- a/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp +++ b/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp @@ -10,6 +10,7 @@ #include #include #include +#include static void FixLilEndian() { @@ -213,11 +214,71 @@ float MovieDecoder_FFMpeg::GetTimestamp() const return m_fTimestamp - m_fTimestampOffset; } +bool MovieDecoder_FFMpeg::IsCurrentFrameReady() { + std::lock_guard(m_FrameBuffer[m_iFrameNumber].lock); + if (!m_FrameBuffer[m_iFrameNumber].decoded) { + LOG->Info("Frame %i not decoded, total frames: %i", m_iFrameNumber, m_totalFrames); + } + return m_FrameBuffer[m_iFrameNumber].decoded; +} + float MovieDecoder_FFMpeg::GetFrameDuration() const { return m_fLastFrameDelay; } +int MovieDecoder_FFMpeg::DecodeNextFrame() +{ + // Add in a new FrameBuffer entry, and lock it immediately. + m_FrameBuffer.push_back(FrameHolder()); + std::lock_guard(m_FrameBuffer.back().lock); + int status = SendPacketToBuffer(); + if (status < 0) { + return status; + } + status = DecodePacketInBuffer(); + if (firstFrame) { + firstFrame = false; + } + return status; +} + +int MovieDecoder_FFMpeg::DecodeMovie() +{ + using std::chrono::operator""ms; + + // The first frame expected to be decoded and drawn already, + // that is handled by MovieTexture_Generic::Init(). + int frameNum = 0; + while (!m_iEOF) { + // This wake up time could be tied to the RageTimer, but as it doesn't + // need to sync with other parts of ITGm, using chrono is fine. + // The 1ms time here is arbitrary, and means that the game will decode + // at a maximum speed of 1 frame per ms (or 1000 fps). + auto wake_up = std::chrono::steady_clock::now() + 1ms; + + int status = DecodeNextFrame(); + + // If cancelled (quitting a song, scrolling the banner), or fatal error, + // stop decoding. + if (status < 0) { + return status; + } + + frameNum++; + + // This means when opening the file, less frames were detected than + // there actually are. Increment to keep up so we don't end the video + // early during display. + if (frameNum - 1 > m_totalFrames) { + m_totalFrames++; + } + + std::this_thread::sleep_until(wake_up); + } + return 0; +} + int MovieDecoder_FFMpeg::SendPacketToBuffer() { if (cancel) { diff --git a/src/arch/MovieTexture/MovieTexture_FFMpeg.h b/src/arch/MovieTexture/MovieTexture_FFMpeg.h index 3af9c676e0..d9b9aa3e0f 100644 --- a/src/arch/MovieTexture/MovieTexture_FFMpeg.h +++ b/src/arch/MovieTexture/MovieTexture_FFMpeg.h @@ -73,6 +73,23 @@ public: void GetFrame( RageSurface *pOut ); int DecodeFrame( float fTargetTime ); + // Decode a single frame. Return -2 on cancel, -1 on error, 0 on EOF, 1 if we have a frame. + int DecodeNextFrame(); + + // Decode the entire movie. + // If we let this decode as fast as possible, it could come at the expense + // of gameplay performance. Since dropping frames is undesirable, an + // artificial rate limit is introduced. + // + // Given that most movies will display at 30fps or 60fps, decoding at a + // speed of 1000fps should be more than sufficient to ensure we never + // run behind. This rate limiting is only needed in case itgmania is + // running on a low performance machine, or the movie is REALLY long. + // + // Returns 0 on success, -1 on fatal error, -2 on cancel. + int DecodeMovie(); + bool IsCurrentFrameReady(); + int GetWidth() const { return m_pStreamCodec->width; } int GetHeight() const { return m_pStreamCodec->height; } diff --git a/src/arch/MovieTexture/MovieTexture_Generic.h b/src/arch/MovieTexture/MovieTexture_Generic.h index 76080f5360..2dfec1c999 100644 --- a/src/arch/MovieTexture/MovieTexture_Generic.h +++ b/src/arch/MovieTexture/MovieTexture_Generic.h @@ -28,6 +28,14 @@ public: virtual void Close() = 0; virtual void Rewind() = 0; + // Decode the next frame. + // Return 1 on success, 0 on EOF, -1 on fatal error, -2 on cancel. + virtual int DecodeNextFrame() = 0; + virtual int DecodeMovie() = 0; + + // Returns true if the frame we want to display has been decoded already. + virtual bool IsCurrentFrameReady() = 0; + /* * Decode a frame. Return 1 on success, 0 on EOF, -1 on fatal error. * @@ -95,6 +103,10 @@ public: virtual void Reload(); virtual void SetPosition( float fSeconds ); + + // UpdateMovie tells the MovieTexture to update the displayed frame based + // on fSeconds passed in. (e.g., 5.9 input means show the frame that should + // be displayed 5.9 seconds into the movie). virtual void UpdateMovie( float fSeconds ); virtual void SetPlaybackRate( float fRate ) { m_fRate = fRate; } void SetLooping( bool bLooping=true ) { m_bLoop = bLooping; }