2022-11-05 10:53:26 +01:00
|
|
|
#include "stdafx.h"
|
|
|
|
|
#include "video_provider.h"
|
|
|
|
|
#include "Emu/RSX/Overlays/overlay_message.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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 11:19:24 +01:00
|
|
|
bool video_provider::set_video_sink(std::shared_ptr<video_sink> sink, recording_mode type)
|
2022-11-05 10:53:26 +01:00
|
|
|
{
|
2022-11-06 11:19:24 +01:00
|
|
|
media_log.notice("video_provider: setting new video sink. sink=%d, type=%s", !!sink, type);
|
2022-11-05 10:53:26 +01:00
|
|
|
|
|
|
|
|
if (type == recording_mode::stopped)
|
|
|
|
|
{
|
|
|
|
|
// Prevent misuse. type is supposed to be a valid state.
|
2022-11-06 11:19:24 +01:00
|
|
|
media_log.error("video_provider: cannot set video sink with type %s", type);
|
2022-11-05 10:53:26 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
2022-11-06 11:19:24 +01:00
|
|
|
if (m_video_sink)
|
2022-11-05 10:53:26 +01:00
|
|
|
{
|
|
|
|
|
// cell has preference
|
|
|
|
|
if (m_type == recording_mode::cell && m_type != type)
|
|
|
|
|
{
|
2022-11-06 11:19:24 +01:00
|
|
|
media_log.warning("video_provider: cannot set video sink with type %s if type %s is active", type, m_type);
|
2022-11-05 10:53:26 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 11:19:24 +01:00
|
|
|
if (m_type != type || m_video_sink != sink)
|
2022-11-05 10:53:26 +01:00
|
|
|
{
|
2022-11-06 11:19:24 +01:00
|
|
|
media_log.warning("video_provider: stopping current video sink of type %s", m_type);
|
|
|
|
|
m_video_sink->stop();
|
2022-11-05 10:53:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_type = sink ? type : recording_mode::stopped;
|
2022-11-06 11:19:24 +01:00
|
|
|
m_video_sink = sink;
|
2022-11-05 10:53:26 +01:00
|
|
|
|
|
|
|
|
if (m_type == recording_mode::stopped)
|
|
|
|
|
{
|
|
|
|
|
m_active = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void video_provider::set_pause_time(usz pause_time_ms)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
m_pause_time_ms = pause_time_ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool video_provider::can_consume_frame()
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
2023-11-14 21:51:42 +01:00
|
|
|
if (!m_video_sink || !m_video_sink->use_internal_video)
|
2022-11-05 10:53:26 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const usz timestamp_ms = std::chrono::duration_cast<std::chrono::milliseconds>(steady_clock::now() - m_encoder_start).count() - m_pause_time_ms;
|
2022-11-06 11:19:24 +01:00
|
|
|
const s64 pts = m_video_sink->get_pts(timestamp_ms);
|
|
|
|
|
return pts > m_last_video_pts_incoming;
|
2022-11-05 10:53:26 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-14 21:51:42 +01:00
|
|
|
recording_mode video_provider::check_mode()
|
2022-11-05 10:53:26 +01:00
|
|
|
{
|
2022-11-06 11:19:24 +01:00
|
|
|
if (!m_video_sink || m_video_sink->has_error)
|
2022-11-05 10:53:26 +01:00
|
|
|
{
|
|
|
|
|
g_recording_mode = recording_mode::stopped;
|
|
|
|
|
rsx::overlays::queue_message(localized_string_id::RECORDING_ABORTED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_recording_mode == recording_mode::stopped)
|
|
|
|
|
{
|
|
|
|
|
m_active = false;
|
2022-11-06 11:19:24 +01:00
|
|
|
return g_recording_mode;
|
2022-11-05 10:53:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_active.exchange(true))
|
|
|
|
|
{
|
|
|
|
|
m_current_encoder_frame = 0;
|
2023-11-17 22:16:12 +01:00
|
|
|
m_current_encoder_sample = 0;
|
2022-11-06 11:19:24 +01:00
|
|
|
m_last_video_pts_incoming = -1;
|
|
|
|
|
m_last_audio_pts_incoming = -1;
|
2022-11-05 10:53:26 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-06 11:19:24 +01:00
|
|
|
if (m_current_encoder_frame == 0 && m_current_encoder_sample == 0)
|
2022-11-05 10:53:26 +01:00
|
|
|
{
|
|
|
|
|
m_encoder_start = steady_clock::now();
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 11:19:24 +01:00
|
|
|
return g_recording_mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void video_provider::present_frame(std::vector<u8>& data, u32 pitch, u32 width, u32 height, bool is_bgra)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
2023-11-14 21:51:42 +01:00
|
|
|
if (check_mode() == recording_mode::stopped)
|
2022-11-06 11:19:24 +01:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-05 10:53:26 +01:00
|
|
|
// Calculate presentation timestamp.
|
|
|
|
|
const usz timestamp_ms = std::chrono::duration_cast<std::chrono::milliseconds>(steady_clock::now() - m_encoder_start).count() - m_pause_time_ms;
|
2022-11-06 11:19:24 +01:00
|
|
|
const s64 pts = m_video_sink->get_pts(timestamp_ms);
|
2022-11-05 10:53:26 +01:00
|
|
|
|
|
|
|
|
// We can just skip this frame if it has the same timestamp.
|
2022-11-06 11:19:24 +01:00
|
|
|
if (pts <= m_last_video_pts_incoming)
|
2022-11-05 10:53:26 +01:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 11:19:24 +01:00
|
|
|
m_last_video_pts_incoming = pts;
|
2022-11-05 10:53:26 +01:00
|
|
|
m_current_encoder_frame++;
|
2022-11-06 11:19:24 +01:00
|
|
|
m_video_sink->add_frame(data, pitch, width, height, is_bgra ? AVPixelFormat::AV_PIX_FMT_BGRA : AVPixelFormat::AV_PIX_FMT_RGBA, timestamp_ms);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool video_provider::can_consume_sample()
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
2023-11-14 21:51:42 +01:00
|
|
|
if (!m_video_sink || !m_video_sink->use_internal_audio)
|
2022-11-06 11:19:24 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const usz timestamp_us = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - m_encoder_start).count() - (m_pause_time_ms * 1000ull);
|
|
|
|
|
const s64 pts = m_video_sink->get_audio_pts(timestamp_us);
|
|
|
|
|
return pts > m_last_audio_pts_incoming;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void video_provider::present_samples(u8* buf, u32 sample_count, u16 channels)
|
|
|
|
|
{
|
|
|
|
|
if (!buf || !sample_count || !channels)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
2023-11-14 21:51:42 +01:00
|
|
|
if (check_mode() == recording_mode::stopped)
|
2022-11-06 11:19:24 +01:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate presentation timestamp.
|
|
|
|
|
const usz timestamp_us = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - m_encoder_start).count() - (m_pause_time_ms * 1000ull);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_last_audio_pts_incoming = pts;
|
|
|
|
|
m_current_encoder_sample += sample_count;
|
|
|
|
|
m_video_sink->add_audio_samples(buf, sample_count, channels, timestamp_us);
|
2022-11-05 10:53:26 +01:00
|
|
|
}
|
|
|
|
|
}
|