rpcs3/rpcs3/util/video_provider.cpp
Megamouse d72f95677e
Some checks failed
Generate Translation Template / Generate Translation Template (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux-aarch64.sh, gcc, rpcs3/rpcs3-ci-jammy-aarch64:1.6, ubuntu-24.04-arm) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux.sh, gcc, rpcs3/rpcs3-ci-jammy:1.6, ubuntu-24.04) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1, rpcs3/rpcs3-binaries-linux-arm64, /rpcs3/.ci/build-linux-aarch64.sh, clang, rpcs3/rpcs3-ci-jammy-aarch64:1.6, ubuntu-24.04-arm) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (d812f1254a1157c80fd402f94446310560f54e5f, rpcs3/rpcs3-binaries-linux, /rpcs3/.ci/build-linux.sh, clang, rpcs3/rpcs3-ci-jammy:1.6, ubuntu-24.04) (push) Has been cancelled
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (51ae32f468089a8169aaf1567de355ff4a3e0842, rpcs3/rpcs3-binaries-mac, arch -X86_64 .ci/build-mac.sh, Intel) (push) Has been cancelled
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (8e21bdbc40711a3fccd18fbf17b742348b0f4281, rpcs3/rpcs3-binaries-mac-arm64, .ci/build-mac-arm64.sh, Apple Silicon) (push) Has been cancelled
Build RPCS3 / RPCS3 Windows (push) Has been cancelled
Build RPCS3 / RPCS3 Windows Clang (win64, clang, clang64) (push) Has been cancelled
Build RPCS3 / RPCS3 FreeBSD (push) Has been cancelled
overlays: add settings for screenshot and recording hints
2025-08-21 08:27:12 +02:00

215 lines
4.9 KiB
C++

#include "stdafx.h"
#include "video_provider.h"
#include "Emu/RSX/Overlays/overlay_message.h"
#include "Emu/Cell/timers.hpp"
#include "Emu/system_config.h"
extern "C"
{
#include <libavutil/pixfmt.h>
}
LOG_CHANNEL(media_log, "Media");
atomic_t<recording_mode> g_recording_mode = recording_mode::stopped;
template <>
void fmt_class_string<recording_mode>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](recording_mode value)
{
switch (value)
{
case recording_mode::stopped: return "stopped";
case recording_mode::rpcs3: return "rpcs3";
case recording_mode::cell: return "cell";
}
return unknown;
});
}
namespace utils
{
video_provider::~video_provider()
{
g_recording_mode = recording_mode::stopped;
}
bool video_provider::set_video_sink(std::shared_ptr<video_sink> sink, recording_mode type)
{
media_log.notice("video_provider: setting new video sink. sink=%d, type=%s", !!sink, type);
if (type == recording_mode::stopped)
{
// Prevent misuse. type is supposed to be a valid state.
media_log.error("video_provider: cannot set video sink with type %s", type);
return false;
}
std::scoped_lock lock(m_video_mutex, m_audio_mutex);
if (m_video_sink)
{
// cell has preference
if (m_type == recording_mode::cell && m_type != type)
{
media_log.warning("video_provider: cannot set video sink with type %s if type %s is active", type, m_type);
return false;
}
if (m_type != type || m_video_sink != sink)
{
media_log.warning("video_provider: stopping current video sink of type %s", m_type);
m_video_sink->stop();
}
}
m_type = sink ? type : recording_mode::stopped;
m_video_sink = sink;
m_active = (m_type != recording_mode::stopped);
if (!m_active)
{
m_last_video_pts_incoming = -1;
m_last_audio_pts_incoming = -1;
m_start_time_us.store(umax);
}
return true;
}
void video_provider::set_pause_time_us(usz pause_time_us)
{
std::scoped_lock lock(m_video_mutex, m_audio_mutex);
m_pause_time_us = pause_time_us;
}
recording_mode video_provider::check_mode()
{
if (!m_video_sink || m_video_sink->has_error)
{
g_recording_mode = recording_mode::stopped;
if (g_cfg.misc.show_capture_hints)
{
rsx::overlays::queue_message(localized_string_id::RECORDING_ABORTED);
}
}
if (g_recording_mode == recording_mode::stopped)
{
m_active = false;
}
return g_recording_mode;
}
bool video_provider::can_consume_frame()
{
if (!m_active)
{
return false;
}
std::lock_guard lock_video(m_video_mutex);
if (!m_video_sink || !m_video_sink->use_internal_video)
{
return false;
}
const usz elapsed_us = get_system_time() - m_start_time_us;
ensure(elapsed_us >= m_pause_time_us);
const usz timestamp_ms = (elapsed_us - m_pause_time_us) / 1000;
const s64 pts = m_video_sink->get_pts(timestamp_ms);
return pts > m_last_video_pts_incoming;
}
void video_provider::present_frame(std::vector<u8>& data, u32 pitch, u32 width, u32 height, bool is_bgra)
{
if (!m_active)
{
return;
}
std::lock_guard lock_video(m_video_mutex);
if (check_mode() == recording_mode::stopped)
{
return;
}
const u64 current_time_us = get_system_time();
if (m_start_time_us.compare_and_swap_test(umax, current_time_us))
{
media_log.notice("video_provider: start time = %d", current_time_us);
}
// Calculate presentation timestamp.
const usz elapsed_us = current_time_us - m_start_time_us;
ensure(elapsed_us >= m_pause_time_us);
const usz timestamp_ms = (elapsed_us - m_pause_time_us) / 1000;
const s64 pts = m_video_sink->get_pts(timestamp_ms);
// We can just skip this frame if it has the same timestamp.
if (pts <= m_last_video_pts_incoming)
{
return;
}
if (m_video_sink->add_frame(data, pitch, width, height, is_bgra ? AVPixelFormat::AV_PIX_FMT_BGRA : AVPixelFormat::AV_PIX_FMT_RGBA, timestamp_ms))
{
m_last_video_pts_incoming = pts;
}
}
void video_provider::present_samples(const u8* buf, u32 sample_count, u16 channels)
{
if (!buf || !sample_count || !channels || !m_active)
{
return;
}
std::lock_guard lock_audio(m_audio_mutex);
if (!m_video_sink || !m_video_sink->use_internal_audio)
{
return;
}
if (check_mode() == recording_mode::stopped)
{
return;
}
const u64 current_time_us = get_system_time();
if (m_start_time_us.compare_and_swap_test(umax, current_time_us))
{
media_log.notice("video_provider: start time = %d", current_time_us);
}
// Calculate presentation timestamp.
const usz elapsed_us = current_time_us - m_start_time_us;
ensure(elapsed_us >= m_pause_time_us);
const usz timestamp_us = elapsed_us - m_pause_time_us;
const s64 pts = m_video_sink->get_audio_pts(timestamp_us);
// We can just skip this sample if it has the same timestamp.
if (pts <= m_last_audio_pts_incoming)
{
return;
}
if (m_video_sink->add_audio_samples(buf, sample_count, channels, timestamp_us))
{
m_last_audio_pts_incoming = pts;
}
}
}