remove synchronous encode

This commit is contained in:
pigeatgarlic 2026-04-07 15:40:27 +07:00
parent c326f9761e
commit 7caa9b7ea9

View File

@ -263,9 +263,7 @@ public:
};
enum flag_e : uint32_t {
DEFAULT = 0, ///< Default flags
PARALLEL_ENCODING = 1 << 1, ///< Capture and encoding can run concurrently on separate threads
H264_ONLY = 1 << 2, ///< When HEVC is too heavy
DEFAULT = 0, ///< Default flags
LIMITED_GOP_SIZE =
1 << 3, ///< Some encoders don't like it when you have an infinite GOP_SIZE. e.g. VAAPI
SINGLE_SLICE_ONLY =
@ -390,27 +388,6 @@ private:
bool force_idr = false;
};
struct sync_session_ctx_t {
safe::signal_t *join_event;
safe::mail_raw_t::event_t<bool> shutdown_event;
safe::mail_raw_t::queue_t<packet_t> packets;
safe::mail_raw_t::event_t<bool> idr_events;
safe::mail_raw_t::event_t<hdr_info_t> hdr_events;
safe::mail_raw_t::event_t<input::touch_port_t> touch_port_events;
config_t *config;
int frame_nr;
void *channel_data;
};
struct sync_session_t {
sync_session_ctx_t *ctx;
std::unique_ptr<encode_session_t> session;
};
using encode_session_ctx_queue_t = safe::queue_t<sync_session_ctx_t>;
using encode_e = platf::capture_e;
struct capture_ctx_t {
img_event_t images;
config_t *config;
@ -424,13 +401,6 @@ struct capture_thread_async_ctx_t {
const encoder_t *encoder_p;
sync_util::sync_t<std::weak_ptr<platf::display_t>> display_wp;
};
struct capture_thread_sync_ctx_t {
encode_session_ctx_queue_t encode_session_ctx_queue{30};
};
int start_capture_sync(capture_thread_sync_ctx_t &ctx);
void end_capture_sync(capture_thread_sync_ctx_t &ctx);
int start_capture_async(capture_thread_async_ctx_t &ctx);
void end_capture_async(capture_thread_async_ctx_t &ctx);
@ -438,8 +408,6 @@ void end_capture_async(capture_thread_async_ctx_t &ctx);
// reference to the capture thread
auto capture_thread_async =
safe::make_shared<capture_thread_async_ctx_t>(start_capture_async, end_capture_async);
auto capture_thread_sync =
safe::make_shared<capture_thread_sync_ctx_t>(start_capture_sync, end_capture_sync);
encoder_t nvenc{
"nvenc"sv,
@ -472,7 +440,7 @@ encoder_t nvenc{
{}, // Fallback options
"h264_nvenc"s,
},
PARALLEL_ENCODING | REF_FRAMES_INVALIDATION // flags
REF_FRAMES_INVALIDATION // flags
};
encoder_t quicksync{
@ -571,7 +539,7 @@ encoder_t quicksync{
},
"h264_qsv"s,
},
PARALLEL_ENCODING | CBR_WITH_VBR | RELAXED_COMPLIANCE | NO_RC_BUF_LIMIT | YUV444_SUPPORT};
CBR_WITH_VBR | RELAXED_COMPLIANCE | NO_RC_BUF_LIMIT | YUV444_SUPPORT};
encoder_t amdvce{
"amdvce"sv,
@ -650,7 +618,7 @@ encoder_t amdvce{
},
"h264_amf"s,
},
PARALLEL_ENCODING};
0};
encoder_t software{
"software"sv,
@ -712,7 +680,7 @@ encoder_t software{
{}, // Fallback options
"libx264"s,
},
H264_ONLY | PARALLEL_ENCODING | ALWAYS_REPROBE | YUV444_SUPPORT};
ALWAYS_REPROBE | YUV444_SUPPORT};
static const std::vector<encoder_t *> encoders{&nvenc, &quicksync, &amdvce, &software};
@ -1757,241 +1725,6 @@ make_encode_device(platf::display_t &disp, const encoder_t &encoder, const confi
return result;
}
std::optional<sync_session_t> make_synced_session(platf::display_t *disp, const encoder_t &encoder,
platf::img_t &img, sync_session_ctx_t &ctx) {
sync_session_t encode_session;
encode_session.ctx = &ctx;
auto encode_device = make_encode_device(*disp, encoder, *ctx.config);
if (!encode_device) {
return std::nullopt;
}
// absolute mouse coordinates require that the dimensions of the screen are known
ctx.touch_port_events->raise(make_port(disp, *ctx.config));
// Update client with our current HDR display state
hdr_info_t hdr_info = std::make_unique<hdr_info_raw_t>(false);
if (colorspace_is_hdr(encode_device->colorspace)) {
if (disp->get_hdr_metadata(hdr_info->metadata)) {
hdr_info->enabled = true;
} else {
BOOST_LOG(error) << "Couldn't get display hdr metadata when colorspace selection indicates "
"it should have one";
}
}
ctx.hdr_events->raise(std::move(hdr_info));
auto session = make_encode_session(disp, encoder, *ctx.config, img.width, img.height,
std::move(encode_device));
if (!session) {
return std::nullopt;
}
// Load the initial image to prepare for encoding
if (session->convert(img)) {
BOOST_LOG(error) << "Could not convert initial image"sv;
return std::nullopt;
}
encode_session.session = std::move(session);
return encode_session;
}
encode_e encode_run_sync(std::vector<std::unique_ptr<sync_session_ctx_t>> &synced_session_ctxs,
encode_session_ctx_queue_t &encode_session_ctx_queue,
std::vector<std::string> &display_names, int &display_p) {
const auto &encoder = *chosen_encoder;
std::shared_ptr<platf::display_t> disp;
if (synced_session_ctxs.empty()) {
auto ctx = encode_session_ctx_queue.pop();
if (!ctx) {
return encode_e::ok;
}
synced_session_ctxs.emplace_back(std::make_unique<sync_session_ctx_t>(std::move(*ctx)));
}
while (encode_session_ctx_queue.running()) {
// Refresh display names since a display removal might have caused the reinitialization
refresh_displays(encoder.platform_formats->dev_type, display_names, display_p);
// Process any pending display switch with the new list of displays
if (synced_session_ctxs.front()->config->display.has_value() &&
synced_session_ctxs.front()->config->display.value() != display_names[display_p]) {
for (int i = 0; i < display_names.size(); i++) {
if (synced_session_ctxs.front()->config->display.value() == display_names[i]) {
BOOST_LOG(info) << "Switched to display " << display_names[i];
display_p = i;
goto next;
}
}
BOOST_LOG(info) << "Display " << synced_session_ctxs.front()->config->display.value()
<< " not found";
synced_session_ctxs.front()->config->display = std::nullopt;
}
next:
// reset_display() will sleep between retries
reset_display(disp, encoder.platform_formats->dev_type, display_names[display_p],
synced_session_ctxs.front()->config);
if (disp) {
break;
}
}
if (!disp) {
return encode_e::error;
}
auto img = disp->alloc_img();
if (!img || disp->dummy_img(img.get())) {
return encode_e::error;
}
std::vector<sync_session_t> synced_sessions;
for (auto &ctx : synced_session_ctxs) {
auto synced_session = make_synced_session(disp.get(), encoder, *img, *ctx);
if (!synced_session) {
return encode_e::error;
}
synced_sessions.emplace_back(std::move(*synced_session));
}
auto ec = platf::capture_e::ok;
while (encode_session_ctx_queue.running()) {
auto push_captured_image_callback = [&](std::shared_ptr<platf::img_t> &&img,
bool frame_captured) -> bool {
while (encode_session_ctx_queue.peek()) {
auto encode_session_ctx = encode_session_ctx_queue.pop();
if (!encode_session_ctx) {
return false;
}
synced_session_ctxs.emplace_back(
std::make_unique<sync_session_ctx_t>(std::move(*encode_session_ctx)));
auto encode_session =
make_synced_session(disp.get(), encoder, *img, *synced_session_ctxs.back());
if (!encode_session) {
ec = platf::capture_e::error;
return false;
}
synced_sessions.emplace_back(std::move(*encode_session));
}
KITTY_WHILE_LOOP(auto pos = std::begin(synced_sessions), pos != std::end(synced_sessions), {
auto ctx = pos->ctx;
if (ctx->shutdown_event->peek()) {
// Let waiting thread know it can delete shutdown_event
ctx->join_event->raise(true);
pos = synced_sessions.erase(pos);
synced_session_ctxs.erase(
std::find_if(std::begin(synced_session_ctxs), std::end(synced_session_ctxs),
[&ctx_p = ctx](auto &ctx) { return ctx.get() == ctx_p; }));
if (synced_sessions.empty()) {
return false;
}
continue;
}
if (ctx->config->display.has_value() &&
ctx->config->display.value() != display_names[display_p]) {
ec = platf::capture_e::reinit;
return false;
}
if (ctx->idr_events->peek()) {
pos->session->request_idr_frame();
ctx->idr_events->pop();
}
if (frame_captured && pos->session->convert(*img)) {
BOOST_LOG(error) << "Could not convert image"sv;
ctx->shutdown_event->raise(true);
continue;
}
std::optional<std::chrono::steady_clock::time_point> frame_timestamp;
if (img) {
frame_timestamp = img->frame_timestamp;
}
if (encode(ctx->frame_nr++, *pos->session, ctx->packets, ctx->channel_data,
frame_timestamp)) {
BOOST_LOG(error) << "Could not encode video packet"sv;
ctx->shutdown_event->raise(true);
continue;
}
pos->session->request_normal_frame();
++pos;
})
return true;
};
auto pull_free_image_callback = [&img](std::shared_ptr<platf::img_t> &img_out) -> bool {
img_out = img;
img_out->frame_timestamp.reset();
return true;
};
auto status =
disp->capture(push_captured_image_callback, pull_free_image_callback, &display_cursor);
switch (status) {
case platf::capture_e::reinit:
case platf::capture_e::error:
case platf::capture_e::ok:
case platf::capture_e::timeout:
case platf::capture_e::interrupted:
return ec != platf::capture_e::ok ? ec : status;
}
}
return encode_e::ok;
}
void captureThreadSync() {
auto ref = capture_thread_sync.ref();
std::vector<std::unique_ptr<sync_session_ctx_t>> synced_session_ctxs;
auto &ctx = ref->encode_session_ctx_queue;
auto lg = util::fail_guard([&]() {
ctx.stop();
for (auto &ctx : synced_session_ctxs) {
ctx->shutdown_event->raise(true);
ctx->join_event->raise(true);
}
for (auto &ctx : ctx.unsafe()) {
ctx.shutdown_event->raise(true);
ctx.join_event->raise(true);
}
});
// Encoding and capture takes place on this thread
platf::adjust_thread_priority(platf::thread_priority_e::high);
std::vector<std::string> display_names;
int display_p = -1;
while (encode_run_sync(synced_session_ctxs, ctx, display_names, display_p) == encode_e::reinit) {
}
}
void capture_async(safe::mail_t mail, config_t &config, void *channel_data) {
auto shutdown_event = mail->event<bool>(mail::shutdown);
@ -2069,26 +1802,7 @@ void capture(safe::mail_t mail, config_t config, void *channel_data) {
auto idr_events = mail->event<bool>(mail::idr);
idr_events->raise(true);
if (chosen_encoder->flags & PARALLEL_ENCODING) {
capture_async(std::move(mail), config, channel_data);
} else {
safe::signal_t join_event;
auto ref = capture_thread_sync.ref();
ref->encode_session_ctx_queue.raise(sync_session_ctx_t{
&join_event,
mail->event<bool>(mail::shutdown),
mail->queue<packet_t>(mail::video_packets),
std::move(idr_events),
mail->event<hdr_info_t>(mail::hdr),
mail->event<input::touch_port_t>(mail::touch_port),
&config,
1,
channel_data,
});
// Wait for join signal
join_event.view();
}
capture_async(std::move(mail), config, channel_data);
}
enum validate_flag_e {
@ -2157,8 +1871,8 @@ bool validate_encoder(encoder_t &encoder, bool expect_failure) {
auto fg =
util::fail_guard([&]() { BOOST_LOG(info) << "Encoder ["sv << encoder.name << "] failed"sv; });
auto test_hevc = active_hevc_mode >= 2 || (active_hevc_mode == 0 && !(encoder.flags & H264_ONLY));
auto test_av1 = active_av1_mode >= 2 || (active_av1_mode == 0 && !(encoder.flags & H264_ONLY));
auto test_hevc = active_hevc_mode >= 2 || active_hevc_mode == 0;
auto test_av1 = active_av1_mode >= 2 || active_av1_mode == 0;
encoder.h264.capabilities.set();
encoder.hevc.capabilities.set();
@ -2592,13 +2306,6 @@ void end_capture_async(capture_thread_async_ctx_t &capture_thread_ctx) {
capture_thread_ctx.capture_thread.join();
}
int start_capture_sync(capture_thread_sync_ctx_t &ctx) {
std::thread{&captureThreadSync}.detach();
return 0;
}
void end_capture_sync(capture_thread_sync_ctx_t &ctx) {
}
platf::mem_type_e map_base_dev_type(AVHWDeviceType type) {
switch (type) {
case AV_HWDEVICE_TYPE_D3D11VA: