From 90bbb6ba46162dbc57b927c0f7397b185cae3ec4 Mon Sep 17 00:00:00 2001 From: AP-NJA Date: Sat, 12 Sep 2026 21:53:27 -0400 Subject: [PATCH 1/2] Add logs to track PTS variable for frame dumping --- Source/Core/VideoCommon/FrameDumpFFMpeg.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp b/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp index 52d41549dd8e..299cba34bae4 100644 --- a/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp +++ b/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp @@ -38,6 +38,7 @@ extern "C" { #include "Core/HW/SystemTimers.h" #include "Core/HW/VideoInterface.h" #include "Core/System.h" +#include "Core/CoreTiming.h" #include "VideoCommon/OnScreenDisplay.h" @@ -356,6 +357,10 @@ bool FFMpegFrameDump::IsFirstFrameInCurrentFile() const void FFMpegFrameDump::AddFrame(const FrameData& frame) { + + auto& system = Core::System::GetInstance(); + SystemTimers::SystemTimersManager& systemTimers = system.GetSystemTimers(); + CoreTiming::CoreTimingManager& coreTiming = system.GetCoreTiming(); // Are we even dumping? if (!IsStarted()) return; @@ -370,9 +375,12 @@ void FFMpegFrameDump::AddFrame(const FrameData& frame) const s64 pts = av_rescale_q( frame.state.ticks - m_context->start_ticks, // TODO: GetTicksPerSecond is not safe from GPU thread. - AVRational{1, int(Core::System::GetInstance().GetSystemTimers().GetTicksPerSecond())}, + AVRational{1, int(systemTimers.GetTicksPerSecond())}, m_context->codec->time_base); + INFO_LOG_FMT(FRAMEDUMP, "Last PTS: {} | PTS: {} | Stream PTS: {}", m_context->last_pts, pts, m_context->scaled_frame->pts); + INFO_LOG_FMT(FRAMEDUMP, "Ticks: {} | Ticks per Second: {}", coreTiming.GetTicks(), systemTimers.GetTicksPerSecond()); + if (!IsFirstFrameInCurrentFile()) { if (pts <= m_context->last_pts) @@ -384,7 +392,7 @@ void FFMpegFrameDump::AddFrame(const FrameData& frame) { WARN_LOG_FMT(FRAMEDUMP, "PTS delta > 1. Resulting file will have variable frame rate. " "Subsequent occurrences will not be reported."); - m_context->gave_vfr_warning = true; + // m_context->gave_vfr_warning = true; } } From 1f302989f0f7869bf29d4ccf593af42c7ffb2c13 Mon Sep 17 00:00:00 2001 From: AP-NJA Date: Wed, 23 Sep 2026 21:22:05 -0400 Subject: [PATCH 2/2] Temporarily revert changes to fix frame dumping --- Source/Core/VideoCommon/FrameDumper.cpp | 62 +++++++++++++++++-------- Source/Core/VideoCommon/FrameDumper.h | 9 ++-- 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp index 8bb98833f254..a0b673d4aa87 100644 --- a/Source/Core/VideoCommon/FrameDumper.cpp +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -27,16 +27,16 @@ static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name) Config::Get(Config::GFX_PNG_COMPRESSION_LEVEL)); } -FrameDumper::FrameDumper() -{ - m_frame_end_handle = - GetVideoEvents().after_frame_event.Register([this](Core::System&) { FlushFrameDump(); }); -} - -FrameDumper::~FrameDumper() -{ - ShutdownFrameDumping(); -} +// FrameDumper::FrameDumper() +// { +// m_frame_end_handle = +// GetVideoEvents().after_frame_event.Register([this](Core::System&) { FlushFrameDump(); }); +// } +// +// FrameDumper::~FrameDumper() +// { +// ShutdownFrameDumping(); +// } void FrameDumper::DumpCurrentFrame(const AbstractTexture* src_texture, const MathUtil::Rectangle& src_rect, @@ -68,6 +68,16 @@ void FrameDumper::DumpCurrentFrame(const AbstractTexture* src_texture, m_frame_dump_readback_texture->GetRect()); m_last_frame_state = m_ffmpeg_dump.FetchState(ticks, frame_number); m_frame_dump_needs_flush = true; + + std::swap(m_frame_dump_readback_texture, m_frame_dump_output_texture); + + auto& output = m_frame_dump_output_texture; + output->Flush(); + + if (output->Map()) + { + DumpFrameData(reinterpret_cast(output->GetMappedPointer()), output->GetConfig().width, output->GetConfig().height, static_cast(output->GetMappedStride())); + } } bool FrameDumper::CheckFrameDumpRenderTexture(u32 target_width, u32 target_height) @@ -172,17 +182,20 @@ void FrameDumper::DumpFrameData(const u8* data, int w, int h, int stride) { m_frame_dump_data = FrameData{data, w, h, stride, m_last_frame_state}; - if (!m_frame_dump_thread_running.IsSet()) + if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES)) { - if (m_frame_dump_thread.joinable()) - m_frame_dump_thread.join(); - m_frame_dump_thread_running.Set(); - m_frame_dump_thread = std::thread(&FrameDumper::FrameDumpThreadFunc, this); - } + if (!mLastFrameDumped) + { + mIsAVIDumping = m_ffmpeg_dump.Start(w, h, m_last_frame_state.ticks); + } - // Wake worker thread up. - m_frame_dump_start.Set(); - m_frame_dump_frame_running = true; + if (mIsAVIDumping) + { + m_ffmpeg_dump.AddFrame(m_frame_dump_data); + } + + mLastFrameDumped = true; + } } void FrameDumper::FinishFrameData() @@ -345,7 +358,7 @@ void FrameDumper::SaveScreenshot(std::string filename) m_screenshot_request.Set(); } -bool FrameDumper::IsFrameDumping() const +bool FrameDumper::IsFrameDumping() { if (m_screenshot_request.IsSet()) return true; @@ -353,6 +366,15 @@ bool FrameDumper::IsFrameDumping() const if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES)) return true; + if (mIsAVIDumping && mLastFrameDumped) + { + m_ffmpeg_dump.Stop(); + mIsAVIDumping = false; + OSD::AddMessage("Stopped dumping frames"); + } + + mLastFrameDumped = false; + return false; } diff --git a/Source/Core/VideoCommon/FrameDumper.h b/Source/Core/VideoCommon/FrameDumper.h index abf3bac38a58..7df181e40268 100644 --- a/Source/Core/VideoCommon/FrameDumper.h +++ b/Source/Core/VideoCommon/FrameDumper.h @@ -19,8 +19,8 @@ class AbstractFramebuffer; class FrameDumper { public: - FrameDumper(); - ~FrameDumper(); + // FrameDumper(); + // ~FrameDumper(); // Ensures all rendered frames are queued for encoding. void FlushFrameDump(); @@ -32,11 +32,14 @@ class FrameDumper void SaveScreenshot(std::string filename); - bool IsFrameDumping() const; + bool IsFrameDumping(); int GetRequiredResolutionLeastCommonMultiple() const; void DoState(PointerWrap& p); + bool mLastFrameDumped = false; + bool mIsAVIDumping = false; + private: // NOTE: The methods below are called on the framedumping thread. void FrameDumpThreadFunc();