mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-03 13:31:27 +12:00
cellRec: fix pausing and resuming
This commit is contained in:
parent
966def13c5
commit
23316d4e1e
7 changed files with 98 additions and 52 deletions
|
@ -178,11 +178,30 @@ public:
|
||||||
|
|
||||||
std::lock_guard lock(m_mtx);
|
std::lock_guard lock(m_mtx);
|
||||||
m_flush = flush;
|
m_flush = flush;
|
||||||
|
m_paused = false;
|
||||||
m_frames_to_encode.clear();
|
m_frames_to_encode.clear();
|
||||||
m_samples_to_encode.clear();
|
m_samples_to_encode.clear();
|
||||||
has_error = false;
|
has_error = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pause(bool flush = true) override
|
||||||
|
{
|
||||||
|
cellRec.notice("Pausing video sink. flush=%d", flush);
|
||||||
|
|
||||||
|
std::lock_guard lock(m_mtx);
|
||||||
|
m_flush = flush;
|
||||||
|
m_paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume() override
|
||||||
|
{
|
||||||
|
cellRec.notice("Resuming video sink");
|
||||||
|
|
||||||
|
std::lock_guard lock(m_mtx);
|
||||||
|
m_flush = false;
|
||||||
|
m_paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
encoder_frame get_frame()
|
encoder_frame get_frame()
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mtx);
|
std::lock_guard lock(m_mtx);
|
||||||
|
@ -563,7 +582,7 @@ void rec_info::start_video_provider()
|
||||||
const u64 pause_time_end = get_system_time();
|
const u64 pause_time_end = get_system_time();
|
||||||
ensure(pause_time_end > pause_time_start);
|
ensure(pause_time_end > pause_time_start);
|
||||||
pause_time_total += (pause_time_end - pause_time_start);
|
pause_time_total += (pause_time_end - pause_time_start);
|
||||||
video_provider.set_pause_time(pause_time_total / 1000);
|
video_provider.set_pause_time_us(pause_time_total);
|
||||||
cellRec.notice("Resuming video provider.");
|
cellRec.notice("Resuming video provider.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -573,7 +592,7 @@ void rec_info::start_video_provider()
|
||||||
recording_time_start = get_system_time();
|
recording_time_start = get_system_time();
|
||||||
pause_time_start = 0;
|
pause_time_start = 0;
|
||||||
pause_time_total = 0;
|
pause_time_total = 0;
|
||||||
video_provider.set_pause_time(0);
|
video_provider.set_pause_time_us(0);
|
||||||
|
|
||||||
video_provider_thread = std::make_unique<named_thread<std::function<void()>>>("cellRec video provider", [this]()
|
video_provider_thread = std::make_unique<named_thread<std::function<void()>>>("cellRec video provider", [this]()
|
||||||
{
|
{
|
||||||
|
@ -785,7 +804,7 @@ void rec_info::start_video_provider()
|
||||||
|
|
||||||
void rec_info::pause_video_provider()
|
void rec_info::pause_video_provider()
|
||||||
{
|
{
|
||||||
cellRec.notice("Pausing image provider.");
|
cellRec.notice("Pausing video provider.");
|
||||||
|
|
||||||
if (video_provider_thread)
|
if (video_provider_thread)
|
||||||
{
|
{
|
||||||
|
@ -1393,20 +1412,14 @@ error_code cellRecStop()
|
||||||
|
|
||||||
sysutil_register_cb([&rec](ppu_thread& ppu) -> s32
|
sysutil_register_cb([&rec](ppu_thread& ppu) -> s32
|
||||||
{
|
{
|
||||||
// Disable video sink if it was used
|
|
||||||
if (rec.param.use_internal_video() || rec.param.use_internal_audio())
|
|
||||||
{
|
|
||||||
const recording_mode old_mode = g_recording_mode.exchange(recording_mode::stopped);
|
|
||||||
|
|
||||||
if (old_mode != recording_mode::cell && old_mode != recording_mode::stopped)
|
|
||||||
{
|
|
||||||
cellRec.error("cellRecStop: Unexpected recording mode %s found while stopping video capture. (ring_sec=%d)", old_mode, rec.param.ring_sec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cellRecStop actually just pauses the recording
|
// cellRecStop actually just pauses the recording
|
||||||
rec.pause_video_provider();
|
rec.pause_video_provider();
|
||||||
|
|
||||||
|
if (rec.sink)
|
||||||
|
{
|
||||||
|
rec.sink->pause(true);
|
||||||
|
}
|
||||||
|
|
||||||
ensure(!!rec.encoder);
|
ensure(!!rec.encoder);
|
||||||
rec.encoder->pause(true);
|
rec.encoder->pause(true);
|
||||||
|
|
||||||
|
@ -1465,6 +1478,11 @@ error_code cellRecStart()
|
||||||
|
|
||||||
rec.start_video_provider();
|
rec.start_video_provider();
|
||||||
|
|
||||||
|
if (rec.sink)
|
||||||
|
{
|
||||||
|
rec.sink->resume();
|
||||||
|
}
|
||||||
|
|
||||||
if (rec.encoder->has_error)
|
if (rec.encoder->has_error)
|
||||||
{
|
{
|
||||||
rec.cb(ppu, CELL_REC_STATUS_ERR, CELL_REC_ERROR_FILE_OPEN, rec.cbUserData);
|
rec.cb(ppu, CELL_REC_STATUS_ERR, CELL_REC_ERROR_FILE_OPEN, rec.cbUserData);
|
||||||
|
|
|
@ -525,7 +525,7 @@ void gs_frame::toggle_recording()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
video_provider.set_pause_time(0);
|
video_provider.set_pause_time_us(0);
|
||||||
|
|
||||||
g_recording_mode = recording_mode::rpcs3;
|
g_recording_mode = recording_mode::rpcs3;
|
||||||
|
|
||||||
|
|
|
@ -790,13 +790,20 @@ namespace utils
|
||||||
m_running = false;
|
m_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void video_encoder::resume()
|
||||||
|
{
|
||||||
|
media_log.notice("video_encoder: Resuming video encoder");
|
||||||
|
|
||||||
|
m_flush = false;
|
||||||
|
m_paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
void video_encoder::encode()
|
void video_encoder::encode()
|
||||||
{
|
{
|
||||||
if (m_running)
|
if (m_running)
|
||||||
{
|
{
|
||||||
// Resume
|
// Resume
|
||||||
m_flush = false;
|
resume();
|
||||||
m_paused = false;
|
|
||||||
media_log.success("video_encoder: resuming recording of '%s'", m_path);
|
media_log.success("video_encoder: resuming recording of '%s'", m_path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,8 +120,9 @@ namespace utils
|
||||||
void set_audio_channels(u32 channels);
|
void set_audio_channels(u32 channels);
|
||||||
void set_audio_bitrate(u32 bitrate);
|
void set_audio_bitrate(u32 bitrate);
|
||||||
void set_audio_codec(s32 codec_id);
|
void set_audio_codec(s32 codec_id);
|
||||||
void pause(bool flush = true);
|
void pause(bool flush = true) override;
|
||||||
void stop(bool flush = true) override;
|
void stop(bool flush = true) override;
|
||||||
|
void resume() override;
|
||||||
void encode();
|
void encode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -132,7 +133,6 @@ namespace utils
|
||||||
// Thread control
|
// Thread control
|
||||||
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
|
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
|
||||||
atomic_t<bool> m_running = false;
|
atomic_t<bool> m_running = false;
|
||||||
atomic_t<bool> m_paused = false;
|
|
||||||
|
|
||||||
// Video parameters
|
// Video parameters
|
||||||
u32 m_video_bitrate_bps = 0;
|
u32 m_video_bitrate_bps = 0;
|
||||||
|
|
|
@ -74,22 +74,10 @@ namespace utils
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void video_provider::set_pause_time(usz pause_time_ms)
|
void video_provider::set_pause_time_us(usz pause_time_us)
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
std::lock_guard lock(m_mutex);
|
||||||
m_pause_time_ms = pause_time_ms;
|
m_pause_time_us = pause_time_us;
|
||||||
}
|
|
||||||
|
|
||||||
bool video_provider::can_consume_frame()
|
|
||||||
{
|
|
||||||
std::lock_guard lock(m_mutex);
|
|
||||||
|
|
||||||
if (!m_video_sink || !m_video_sink->use_internal_video)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const usz timestamp_ms = std::chrono::duration_cast<std::chrono::milliseconds>(steady_clock::now() - m_encoder_start).count() - m_pause_time_ms;
|
|
||||||
const s64 pts = m_video_sink->get_pts(timestamp_ms);
|
|
||||||
return pts > m_last_video_pts_incoming;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recording_mode video_provider::check_mode()
|
recording_mode video_provider::check_mode()
|
||||||
|
@ -122,6 +110,21 @@ namespace utils
|
||||||
return g_recording_mode;
|
return g_recording_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool video_provider::can_consume_frame()
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
if (!m_video_sink || !m_video_sink->use_internal_video)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const usz elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - m_encoder_start).count();
|
||||||
|
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)
|
void video_provider::present_frame(std::vector<u8>& data, u32 pitch, u32 width, u32 height, bool is_bgra)
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
std::lock_guard lock(m_mutex);
|
||||||
|
@ -132,7 +135,10 @@ namespace utils
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate presentation timestamp.
|
// 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;
|
const usz elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - m_encoder_start).count();
|
||||||
|
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);
|
const s64 pts = m_video_sink->get_pts(timestamp_ms);
|
||||||
|
|
||||||
// We can just skip this frame if it has the same timestamp.
|
// We can just skip this frame if it has the same timestamp.
|
||||||
|
@ -141,9 +147,11 @@ namespace utils
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_last_video_pts_incoming = pts;
|
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_current_encoder_frame++;
|
{
|
||||||
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;
|
||||||
|
m_current_encoder_frame++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool video_provider::can_consume_sample()
|
bool video_provider::can_consume_sample()
|
||||||
|
@ -153,7 +161,10 @@ namespace utils
|
||||||
if (!m_video_sink || !m_video_sink->use_internal_audio)
|
if (!m_video_sink || !m_video_sink->use_internal_audio)
|
||||||
return false;
|
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 usz elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - m_encoder_start).count();
|
||||||
|
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);
|
const s64 pts = m_video_sink->get_audio_pts(timestamp_us);
|
||||||
return pts > m_last_audio_pts_incoming;
|
return pts > m_last_audio_pts_incoming;
|
||||||
}
|
}
|
||||||
|
@ -173,7 +184,10 @@ namespace utils
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate presentation timestamp.
|
// 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 usz elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - m_encoder_start).count();
|
||||||
|
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);
|
const s64 pts = m_video_sink->get_audio_pts(timestamp_us);
|
||||||
|
|
||||||
// We can just skip this sample if it has the same timestamp.
|
// We can just skip this sample if it has the same timestamp.
|
||||||
|
@ -182,8 +196,10 @@ namespace utils
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_last_audio_pts_incoming = pts;
|
if (m_video_sink->add_audio_samples(buf, sample_count, channels, timestamp_us))
|
||||||
m_current_encoder_sample += sample_count;
|
{
|
||||||
m_video_sink->add_audio_samples(buf, sample_count, channels, timestamp_us);
|
m_last_audio_pts_incoming = pts;
|
||||||
|
m_current_encoder_sample += sample_count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace utils
|
||||||
~video_provider();
|
~video_provider();
|
||||||
|
|
||||||
bool set_video_sink(std::shared_ptr<video_sink> sink, recording_mode type);
|
bool set_video_sink(std::shared_ptr<video_sink> sink, recording_mode type);
|
||||||
void set_pause_time(usz pause_time_ms);
|
void set_pause_time_us(usz pause_time_us);
|
||||||
|
|
||||||
bool can_consume_frame();
|
bool can_consume_frame();
|
||||||
void present_frame(std::vector<u8>& data, u32 pitch, u32 width, u32 height, bool is_bgra);
|
void present_frame(std::vector<u8>& data, u32 pitch, u32 width, u32 height, bool is_bgra);
|
||||||
|
@ -38,7 +38,7 @@ namespace utils
|
||||||
steady_clock::time_point m_encoder_start{};
|
steady_clock::time_point m_encoder_start{};
|
||||||
s64 m_last_video_pts_incoming = -1;
|
s64 m_last_video_pts_incoming = -1;
|
||||||
s64 m_last_audio_pts_incoming = -1;
|
s64 m_last_audio_pts_incoming = -1;
|
||||||
usz m_pause_time_ms = 0;
|
usz m_pause_time_us = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|
|
@ -15,26 +15,30 @@ namespace utils
|
||||||
video_sink() = default;
|
video_sink() = default;
|
||||||
|
|
||||||
virtual void stop(bool flush = true) = 0;
|
virtual void stop(bool flush = true) = 0;
|
||||||
|
virtual void pause(bool flush = true) = 0;
|
||||||
|
virtual void resume() = 0;
|
||||||
|
|
||||||
void add_frame(std::vector<u8>& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms)
|
bool add_frame(std::vector<u8>& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms)
|
||||||
{
|
{
|
||||||
// Do not allow new frames while flushing
|
// Do not allow new frames while flushing or paused
|
||||||
if (m_flush)
|
if (m_flush || m_paused)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
std::lock_guard lock(m_mtx);
|
std::lock_guard lock(m_mtx);
|
||||||
m_frames_to_encode.emplace_back(timestamp_ms, pitch, width, height, pixel_format, std::move(frame));
|
m_frames_to_encode.emplace_back(timestamp_ms, pitch, width, height, pixel_format, std::move(frame));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_audio_samples(const u8* buf, u32 sample_count, u16 channels, usz timestamp_us)
|
bool add_audio_samples(const u8* buf, u32 sample_count, u16 channels, usz timestamp_us)
|
||||||
{
|
{
|
||||||
// Do not allow new samples while flushing
|
// Do not allow new samples while flushing or paused
|
||||||
if (m_flush || !buf || !sample_count || !channels)
|
if (m_flush || m_paused || !buf || !sample_count || !channels)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
std::vector<u8> sample(buf, buf + sample_count * channels * sizeof(f32));
|
std::vector<u8> sample(buf, buf + sample_count * channels * sizeof(f32));
|
||||||
std::lock_guard lock(m_audio_mtx);
|
std::lock_guard lock(m_audio_mtx);
|
||||||
m_samples_to_encode.emplace_back(timestamp_us, sample_count, channels, std::move(sample));
|
m_samples_to_encode.emplace_back(timestamp_us, sample_count, channels, std::move(sample));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
s64 get_pts(usz timestamp_ms) const
|
s64 get_pts(usz timestamp_ms) const
|
||||||
|
@ -102,6 +106,7 @@ namespace utils
|
||||||
std::deque<encoder_frame> m_frames_to_encode;
|
std::deque<encoder_frame> m_frames_to_encode;
|
||||||
shared_mutex m_audio_mtx;
|
shared_mutex m_audio_mtx;
|
||||||
std::deque<encoder_sample> m_samples_to_encode;
|
std::deque<encoder_sample> m_samples_to_encode;
|
||||||
|
atomic_t<bool> m_paused = false;
|
||||||
atomic_t<bool> m_flush = false;
|
atomic_t<bool> m_flush = false;
|
||||||
u32 m_framerate = 30;
|
u32 m_framerate = 30;
|
||||||
u32 m_sample_rate = 48000;
|
u32 m_sample_rate = 48000;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue