Skip to content

Commit

Permalink
cellRec: fix pausing and resuming
Browse files Browse the repository at this point in the history
  • Loading branch information
Megamouse committed Nov 22, 2023
1 parent 1f47dbf commit 2fc1a12
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 52 deletions.
46 changes: 32 additions & 14 deletions rpcs3/Emu/Cell/Modules/cellRec.cpp
Expand Up @@ -178,11 +178,30 @@ class rec_video_sink : public utils::video_sink

std::lock_guard lock(m_mtx);
m_flush = flush;
m_paused = false;
m_frames_to_encode.clear();
m_samples_to_encode.clear();
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()
{
std::lock_guard lock(m_mtx);
Expand Down Expand Up @@ -563,7 +582,7 @@ void rec_info::start_video_provider()
const u64 pause_time_end = get_system_time();
ensure(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.");
return;
}
Expand All @@ -573,7 +592,7 @@ void rec_info::start_video_provider()
recording_time_start = get_system_time();
pause_time_start = 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]()
{
Expand Down Expand Up @@ -785,7 +804,7 @@ void rec_info::start_video_provider()

void rec_info::pause_video_provider()
{
cellRec.notice("Pausing image provider.");
cellRec.notice("Pausing video provider.");

if (video_provider_thread)
{
Expand Down Expand Up @@ -1393,20 +1412,14 @@ error_code cellRecStop()

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
rec.pause_video_provider();

if (rec.sink)
{
rec.sink->pause(true);
}

ensure(!!rec.encoder);
rec.encoder->pause(true);

Expand Down Expand Up @@ -1465,6 +1478,11 @@ error_code cellRecStart()

rec.start_video_provider();

if (rec.sink)
{
rec.sink->resume();
}

if (rec.encoder->has_error)
{
rec.cb(ppu, CELL_REC_STATUS_ERR, CELL_REC_ERROR_FILE_OPEN, rec.cbUserData);
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/rpcs3qt/gs_frame.cpp
Expand Up @@ -525,7 +525,7 @@ void gs_frame::toggle_recording()
return;
}

video_provider.set_pause_time(0);
video_provider.set_pause_time_us(0);

g_recording_mode = recording_mode::rpcs3;

Expand Down
11 changes: 9 additions & 2 deletions rpcs3/util/media_utils.cpp
Expand Up @@ -790,13 +790,20 @@ namespace utils
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()
{
if (m_running)
{
// Resume
m_flush = false;
m_paused = false;
resume();
media_log.success("video_encoder: resuming recording of '%s'", m_path);
return;
}
Expand Down
4 changes: 2 additions & 2 deletions rpcs3/util/media_utils.h
Expand Up @@ -120,8 +120,9 @@ namespace utils
void set_audio_channels(u32 channels);
void set_audio_bitrate(u32 bitrate);
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 resume() override;
void encode();

private:
Expand All @@ -132,7 +133,6 @@ namespace utils
// Thread control
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
atomic_t<bool> m_running = false;
atomic_t<bool> m_paused = false;

// Video parameters
u32 m_video_bitrate_bps = 0;
Expand Down
62 changes: 39 additions & 23 deletions rpcs3/util/video_provider.cpp
Expand Up @@ -74,22 +74,10 @@ namespace utils
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);
m_pause_time_ms = pause_time_ms;
}

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;
m_pause_time_us = pause_time_us;
}

recording_mode video_provider::check_mode()
Expand Down Expand Up @@ -122,6 +110,21 @@ namespace utils
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)
{
std::lock_guard lock(m_mutex);
Expand All @@ -132,7 +135,10 @@ namespace utils
}

// 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);

// We can just skip this frame if it has the same timestamp.
Expand All @@ -141,9 +147,11 @@ namespace utils
return;
}

m_last_video_pts_incoming = pts;
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);
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;
m_current_encoder_frame++;
}
}

bool video_provider::can_consume_sample()
Expand All @@ -153,7 +161,10 @@ namespace utils
if (!m_video_sink || !m_video_sink->use_internal_audio)
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);
return pts > m_last_audio_pts_incoming;
}
Expand All @@ -173,7 +184,10 @@ namespace utils
}

// 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);

// We can just skip this sample if it has the same timestamp.
Expand All @@ -182,8 +196,10 @@ namespace utils
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);
if (m_video_sink->add_audio_samples(buf, sample_count, channels, timestamp_us))
{
m_last_audio_pts_incoming = pts;
m_current_encoder_sample += sample_count;
}
}
}
4 changes: 2 additions & 2 deletions rpcs3/util/video_provider.h
Expand Up @@ -18,7 +18,7 @@ namespace utils
~video_provider();

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();
void present_frame(std::vector<u8>& data, u32 pitch, u32 width, u32 height, bool is_bgra);
Expand All @@ -38,7 +38,7 @@ namespace utils
steady_clock::time_point m_encoder_start{};
s64 m_last_video_pts_incoming = -1;
s64 m_last_audio_pts_incoming = -1;
usz m_pause_time_ms = 0;
usz m_pause_time_us = 0;
};

} // namespace utils
21 changes: 13 additions & 8 deletions rpcs3/util/video_sink.h
Expand Up @@ -15,26 +15,30 @@ namespace utils
video_sink() = default;

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
if (m_flush)
return;
// Do not allow new frames while flushing or paused
if (m_flush || m_paused)
return false;

std::lock_guard lock(m_mtx);
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
if (m_flush || !buf || !sample_count || !channels)
return;
// Do not allow new samples while flushing or paused
if (m_flush || m_paused || !buf || !sample_count || !channels)
return false;

std::vector<u8> sample(buf, buf + sample_count * channels * sizeof(f32));
std::lock_guard lock(m_audio_mtx);
m_samples_to_encode.emplace_back(timestamp_us, sample_count, channels, std::move(sample));
return true;
}

s64 get_pts(usz timestamp_ms) const
Expand Down Expand Up @@ -102,6 +106,7 @@ namespace utils
std::deque<encoder_frame> m_frames_to_encode;
shared_mutex m_audio_mtx;
std::deque<encoder_sample> m_samples_to_encode;
atomic_t<bool> m_paused = false;
atomic_t<bool> m_flush = false;
u32 m_framerate = 30;
u32 m_sample_rate = 48000;
Expand Down

0 comments on commit 2fc1a12

Please sign in to comment.