System: Switch to new capture file on video FPS change

This commit is contained in:
Stenzek 2024-08-18 12:34:22 +10:00
parent 44a12db931
commit b274bf4d57
No known key found for this signature in database
4 changed files with 76 additions and 22 deletions

View file

@ -2150,7 +2150,7 @@ bool GPU::RenderDisplay(GPUTexture* target, const GSVector4i display_rect, const
bool GPU::SendDisplayToMediaCapture(MediaCapture* cap) bool GPU::SendDisplayToMediaCapture(MediaCapture* cap)
{ {
GPUTexture* target = cap->GetRenderTexture(); GPUTexture* target = cap->GetRenderTexture();
if (!target) if (!target) [[unlikely]]
return false; return false;
const bool apply_aspect_ratio = const bool apply_aspect_ratio =
@ -2163,11 +2163,9 @@ bool GPU::SendDisplayToMediaCapture(MediaCapture* cap)
// Not cleared by RenderDisplay(). // Not cleared by RenderDisplay().
g_gpu_device->ClearRenderTarget(target, GPUDevice::DEFAULT_CLEAR_COLOR); g_gpu_device->ClearRenderTarget(target, GPUDevice::DEFAULT_CLEAR_COLOR);
if (!RenderDisplay(target, display_rect, draw_rect, postfx)) if (!RenderDisplay(target, display_rect, draw_rect, postfx)) [[unlikely]]
return false; return false;
// TODO: Check for frame rate change
return cap->DeliverVideoFrame(target); return cap->DeliverVideoFrame(target);
} }

View file

@ -2023,10 +2023,26 @@ void System::FrameDone()
// Kick off media capture early, might take a while. // Kick off media capture early, might take a while.
if (s_media_capture && s_media_capture->IsCapturingVideo()) [[unlikely]] if (s_media_capture && s_media_capture->IsCapturingVideo()) [[unlikely]]
{
if (s_media_capture->GetVideoFPS() != GetThrottleFrequency()) [[unlikely]]
{
const std::string next_capture_path = s_media_capture->GetNextCapturePath();
INFO_LOG("Video frame rate changed, switching to new capture file {}", Path::GetFileName(next_capture_path));
const bool was_capturing_audio = s_media_capture->IsCapturingAudio();
StopMediaCapture();
if (StartMediaCapture(std::move(next_capture_path), true, was_capturing_audio) &&
!g_gpu->SendDisplayToMediaCapture(s_media_capture.get())) [[unlikely]]
{
StopMediaCapture();
}
}
else
{ {
if (!g_gpu->SendDisplayToMediaCapture(s_media_capture.get())) [[unlikely]] if (!g_gpu->SendDisplayToMediaCapture(s_media_capture.get())) [[unlikely]]
StopMediaCapture(); StopMediaCapture();
} }
}
Common::Timer::Value current_time = Common::Timer::GetCurrentValue(); Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
@ -4991,7 +5007,7 @@ bool System::StartMediaCapture(std::string path, bool capture_video, bool captur
u32 capture_height = g_settings.media_capture_video_height; u32 capture_height = g_settings.media_capture_video_height;
const GPUTexture::Format capture_format = const GPUTexture::Format capture_format =
g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8; g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8;
const float fps = g_gpu->ComputeVerticalFrequency(); const float fps = System::GetThrottleFrequency();
if (capture_video) if (capture_video)
{ {
// TODO: This will be a mess with GPU thread. // TODO: This will be a mess with GPU thread.

View file

@ -89,21 +89,23 @@ public:
bool BeginCapture(float fps, float aspect, u32 width, u32 height, GPUTexture::Format texture_format, u32 sample_rate, bool BeginCapture(float fps, float aspect, u32 width, u32 height, GPUTexture::Format texture_format, u32 sample_rate,
std::string path, bool capture_video, std::string_view video_codec, u32 video_bitrate, std::string path, bool capture_video, std::string_view video_codec, u32 video_bitrate,
std::string_view video_codec_args, bool capture_audio, std::string_view audio_codec, std::string_view video_codec_args, bool capture_audio, std::string_view audio_codec,
u32 audio_bitrate, std::string_view audio_codec_args, Error* error) override; u32 audio_bitrate, std::string_view audio_codec_args, Error* error) override final;
const std::string& GetPath() const override; const std::string& GetPath() const override final;
u32 GetVideoWidth() const override; std::string GetNextCapturePath() const override final;
u32 GetVideoHeight() const override; u32 GetVideoWidth() const override final;
u32 GetVideoHeight() const override final;
float GetVideoFPS() const override final;
float GetCaptureThreadUsage() const override; float GetCaptureThreadUsage() const override final;
float GetCaptureThreadTime() const override; float GetCaptureThreadTime() const override final;
void UpdateCaptureThreadUsage(double pct_divider, double time_divider) override; void UpdateCaptureThreadUsage(double pct_divider, double time_divider) override final;
GPUTexture* GetRenderTexture() override; GPUTexture* GetRenderTexture() override final;
bool DeliverVideoFrame(GPUTexture* stex) override; bool DeliverVideoFrame(GPUTexture* stex) override final;
bool DeliverAudioFrames(const s16* frames, u32 num_frames) override; bool DeliverAudioFrames(const s16* frames, u32 num_frames) override final;
bool EndCapture(Error* error) override; bool EndCapture(Error* error) override final;
void Flush() override; void Flush() override final;
protected: protected:
struct PendingFrame struct PendingFrame
@ -147,9 +149,10 @@ protected:
std::atomic_bool m_capturing{false}; std::atomic_bool m_capturing{false};
std::atomic_bool m_encoding_error{false}; std::atomic_bool m_encoding_error{false};
GPUTexture::Format m_video_render_texture_format = GPUTexture::Format::Unknown;
u32 m_video_width = 0; u32 m_video_width = 0;
u32 m_video_height = 0; u32 m_video_height = 0;
GPUTexture::Format m_video_render_texture_format = GPUTexture::Format::Unknown; float m_video_fps = 0;
s64 m_next_video_pts = 0; s64 m_next_video_pts = 0;
std::unique_ptr<GPUTexture> m_render_texture; std::unique_ptr<GPUTexture> m_render_texture;
@ -185,17 +188,19 @@ bool MediaCaptureBase::BeginCapture(float fps, float aspect, u32 width, u32 heig
std::string_view audio_codec, u32 audio_bitrate, std::string_view audio_codec_args, std::string_view audio_codec, u32 audio_bitrate, std::string_view audio_codec_args,
Error* error) Error* error)
{ {
m_video_render_texture_format = texture_format;
m_video_width = width; m_video_width = width;
m_video_height = height; m_video_height = height;
m_video_render_texture_format = texture_format; m_video_fps = fps;
if (path.empty()) if (path.empty())
{ {
Error::SetStringView(error, "No path specified."); Error::SetStringView(error, "No path specified.");
return false; return false;
} }
else if (fps == 0.0f || m_video_width == 0 || !Common::IsAlignedPow2(m_video_width, VIDEO_WIDTH_ALIGNMENT) || else if (capture_video &&
m_video_height == 0 || !Common::IsAlignedPow2(m_video_height, VIDEO_HEIGHT_ALIGNMENT)) (fps == 0.0f || m_video_width == 0 || !Common::IsAlignedPow2(m_video_width, VIDEO_WIDTH_ALIGNMENT) ||
m_video_height == 0 || !Common::IsAlignedPow2(m_video_height, VIDEO_HEIGHT_ALIGNMENT)))
{ {
Error::SetStringView(error, "Invalid video dimensions/rate."); Error::SetStringView(error, "Invalid video dimensions/rate.");
return false; return false;
@ -506,6 +511,34 @@ const std::string& MediaCaptureBase::GetPath() const
return m_path; return m_path;
} }
std::string MediaCaptureBase::GetNextCapturePath() const
{
const std::string_view ext = Path::GetExtension(m_path);
std::string_view name = Path::GetFileTitle(m_path);
// Should end with a number.
u32 partnum = 2;
std::string_view::size_type pos = name.rfind("_part");
if (pos != std::string_view::npos)
{
std::string_view::size_type cpos = pos + 5;
for (; cpos < name.length(); cpos++)
{
if (name[cpos] < '0' || name[cpos] > '9')
break;
}
if (cpos == name.length())
{
// Has existing part number, so add to it.
partnum = StringUtil::FromChars<u32>(name.substr(pos + 5)).value_or(1) + 1;
name = name.substr(0, pos);
}
}
// If we haven't started a new file previously, add "_part2".
return Path::BuildRelativePath(m_path, fmt::format("{}_part{:03d}.{}", name, partnum, ext));
}
u32 MediaCaptureBase::GetVideoWidth() const u32 MediaCaptureBase::GetVideoWidth() const
{ {
return m_video_width; return m_video_width;
@ -516,6 +549,11 @@ u32 MediaCaptureBase::GetVideoHeight() const
return m_video_height; return m_video_height;
} }
float MediaCaptureBase::GetVideoFPS() const
{
return m_video_fps;
}
float MediaCaptureBase::GetCaptureThreadUsage() const float MediaCaptureBase::GetCaptureThreadUsage() const
{ {
return m_encoder_thread_usage; return m_encoder_thread_usage;

View file

@ -56,10 +56,12 @@ public:
// TODO: make non-virtual? // TODO: make non-virtual?
virtual const std::string& GetPath() const = 0; virtual const std::string& GetPath() const = 0;
virtual std::string GetNextCapturePath() const = 0;
virtual bool IsCapturingAudio() const = 0; virtual bool IsCapturingAudio() const = 0;
virtual bool IsCapturingVideo() const = 0; virtual bool IsCapturingVideo() const = 0;
virtual u32 GetVideoWidth() const = 0; virtual u32 GetVideoWidth() const = 0;
virtual u32 GetVideoHeight() const = 0; virtual u32 GetVideoHeight() const = 0;
virtual float GetVideoFPS() const = 0;
/// Returns the elapsed time in seconds. /// Returns the elapsed time in seconds.
virtual time_t GetElapsedTime() const = 0; virtual time_t GetElapsedTime() const = 0;