Add DecodeNextFrame and DecodeMovie functions to populate the

FrameBuffer.
This commit is contained in:
Brandon W
2024-08-06 17:18:12 -07:00
committed by teejusb
parent 5d2667add1
commit 10e4323e3e
3 changed files with 90 additions and 0 deletions
@@ -10,6 +10,7 @@
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <thread>
static void FixLilEndian()
{
@@ -213,11 +214,71 @@ float MovieDecoder_FFMpeg::GetTimestamp() const
return m_fTimestamp - m_fTimestampOffset;
}
bool MovieDecoder_FFMpeg::IsCurrentFrameReady() {
std::lock_guard<std::mutex>(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<std::mutex>(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) {
@@ -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; }
@@ -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; }