System: Switch to new capture file on video FPS change
This commit is contained in:
parent
44a12db931
commit
b274bf4d57
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue