fix(linux/wlgrab): add frame_timestamp using wayland's ready timestamp (#4787)
Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
This commit is contained in:
@@ -54,17 +54,17 @@ namespace wl {
|
||||
}
|
||||
|
||||
if (!display_name) {
|
||||
BOOST_LOG(error) << "Environment variable WAYLAND_DISPLAY has not been defined"sv;
|
||||
BOOST_LOG(error) << "[wayland] Environment variable WAYLAND_DISPLAY has not been defined"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
display_internal.reset(wl_display_connect(display_name));
|
||||
if (!display_internal) {
|
||||
BOOST_LOG(error) << "Couldn't connect to Wayland display: "sv << display_name;
|
||||
BOOST_LOG(error) << "[wayland] Couldn't connect to Wayland display: "sv << display_name;
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "Found display ["sv << display_name << ']';
|
||||
BOOST_LOG(info) << "[wayland] Found display ["sv << display_name << ']';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -127,26 +127,26 @@ namespace wl {
|
||||
inline void monitor_t::xdg_name(zxdg_output_v1 *, const char *name) {
|
||||
this->name = name;
|
||||
|
||||
BOOST_LOG(info) << "Name: "sv << this->name;
|
||||
BOOST_LOG(info) << "[wayland] Name: "sv << this->name;
|
||||
}
|
||||
|
||||
void monitor_t::xdg_description(zxdg_output_v1 *, const char *description) {
|
||||
this->description = description;
|
||||
|
||||
BOOST_LOG(info) << "Found monitor: "sv << this->description;
|
||||
BOOST_LOG(info) << "[wayland] Found monitor: "sv << this->description;
|
||||
}
|
||||
|
||||
void monitor_t::xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y) {
|
||||
viewport.offset_x = x;
|
||||
viewport.offset_y = y;
|
||||
|
||||
BOOST_LOG(info) << "Offset: "sv << x << 'x' << y;
|
||||
BOOST_LOG(info) << "[wayland] Offset: "sv << x << 'x' << y;
|
||||
}
|
||||
|
||||
void monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) {
|
||||
viewport.logical_width = width;
|
||||
viewport.logical_height = height;
|
||||
BOOST_LOG(info) << "Logical size: "sv << width << 'x' << height;
|
||||
BOOST_LOG(info) << "[wayland] Logical size: "sv << width << 'x' << height;
|
||||
}
|
||||
|
||||
void monitor_t::wl_mode(
|
||||
@@ -159,7 +159,7 @@ namespace wl {
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
|
||||
BOOST_LOG(info) << "Resolution: "sv << width << 'x' << height;
|
||||
BOOST_LOG(info) << "[wayland] Resolution: "sv << width << 'x' << height;
|
||||
}
|
||||
|
||||
void monitor_t::listen(zxdg_output_manager_v1 *output_manager) {
|
||||
@@ -189,27 +189,27 @@ namespace wl {
|
||||
const char *interface,
|
||||
std::uint32_t version
|
||||
) {
|
||||
BOOST_LOG(debug) << "Available interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
BOOST_LOG(debug) << "[wayland] Available interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
|
||||
if (!std::strcmp(interface, wl_output_interface.name)) {
|
||||
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
BOOST_LOG(info) << "[wayland] Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
monitors.emplace_back(
|
||||
std::make_unique<monitor_t>(
|
||||
(wl_output *) wl_registry_bind(registry, id, &wl_output_interface, 2)
|
||||
)
|
||||
);
|
||||
} else if (!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) {
|
||||
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
BOOST_LOG(info) << "[wayland] Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
output_manager = (zxdg_output_manager_v1 *) wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version);
|
||||
|
||||
this->interface[XDG_OUTPUT] = true;
|
||||
} else if (!std::strcmp(interface, zwlr_screencopy_manager_v1_interface.name)) {
|
||||
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
BOOST_LOG(info) << "[wayland] Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
screencopy_manager = (zwlr_screencopy_manager_v1 *) wl_registry_bind(registry, id, &zwlr_screencopy_manager_v1_interface, version);
|
||||
|
||||
this->interface[WLR_EXPORT_DMABUF] = true;
|
||||
} else if (!std::strcmp(interface, zwp_linux_dmabuf_v1_interface.name)) {
|
||||
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
BOOST_LOG(info) << "[wayland] Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
dmabuf_interface = (zwp_linux_dmabuf_v1 *) wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, version);
|
||||
|
||||
this->interface[LINUX_DMABUF] = true;
|
||||
@@ -217,7 +217,7 @@ namespace wl {
|
||||
}
|
||||
|
||||
void interface_t::del_interface(wl_registry *registry, uint32_t id) {
|
||||
BOOST_LOG(info) << "Delete: "sv << id;
|
||||
BOOST_LOG(info) << "[wayland] Delete: "sv << id;
|
||||
}
|
||||
|
||||
// Initialize GBM
|
||||
@@ -230,7 +230,7 @@ namespace wl {
|
||||
drmDevice *devices[16];
|
||||
int n = drmGetDevices2(0, devices, 16);
|
||||
if (n <= 0) {
|
||||
BOOST_LOG(error) << "No DRM devices found"sv;
|
||||
BOOST_LOG(error) << "[wayland] No DRM devices found"sv;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -246,14 +246,14 @@ namespace wl {
|
||||
drmFreeDevices(devices, n);
|
||||
|
||||
if (drm_fd < 0) {
|
||||
BOOST_LOG(error) << "Failed to open DRM render node"sv;
|
||||
BOOST_LOG(error) << "[wayland] Failed to open DRM render node"sv;
|
||||
return false;
|
||||
}
|
||||
|
||||
gbm_device = gbm_create_device(drm_fd);
|
||||
if (!gbm_device) {
|
||||
close(drm_fd);
|
||||
BOOST_LOG(error) << "Failed to create GBM device"sv;
|
||||
BOOST_LOG(error) << "[wayland] Failed to create GBM device"sv;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -343,8 +343,6 @@ namespace wl {
|
||||
shm_info.width = width;
|
||||
shm_info.height = height;
|
||||
shm_info.stride = stride;
|
||||
|
||||
BOOST_LOG(debug) << "Screencopy supports SHM format: "sv << format;
|
||||
}
|
||||
|
||||
// DMA-BUF format callback
|
||||
@@ -358,14 +356,12 @@ namespace wl {
|
||||
dmabuf_info.format = format;
|
||||
dmabuf_info.width = width;
|
||||
dmabuf_info.height = height;
|
||||
|
||||
BOOST_LOG(debug) << "Screencopy supports DMA-BUF format: "sv << format;
|
||||
}
|
||||
|
||||
// Flags callback
|
||||
void dmabuf_t::flags(zwlr_screencopy_frame_v1 *frame, std::uint32_t flags) {
|
||||
y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
|
||||
BOOST_LOG(debug) << "Frame flags: "sv << flags << (y_invert ? " (y_invert)" : "");
|
||||
BOOST_LOG(verbose) << "Frame flags: "sv << flags << (y_invert ? " (y_invert)" : "");
|
||||
}
|
||||
|
||||
// DMA-BUF creation helper
|
||||
@@ -433,11 +429,11 @@ namespace wl {
|
||||
create_and_copy_dmabuf(frame);
|
||||
} else if (shm_info.supported) {
|
||||
// SHM fallback would go here
|
||||
BOOST_LOG(warning) << "SHM capture not implemented"sv;
|
||||
BOOST_LOG(warning) << "[wayland] SHM capture not implemented"sv;
|
||||
zwlr_screencopy_frame_v1_destroy(frame);
|
||||
status = REINIT;
|
||||
} else {
|
||||
BOOST_LOG(error) << "No supported buffer types"sv;
|
||||
BOOST_LOG(error) << "[wayland] No supported buffer types"sv;
|
||||
zwlr_screencopy_frame_v1_destroy(frame);
|
||||
status = REINIT;
|
||||
}
|
||||
@@ -468,7 +464,7 @@ namespace wl {
|
||||
auto frame = static_cast<zwlr_screencopy_frame_v1 *>(data);
|
||||
auto self = static_cast<dmabuf_t *>(zwlr_screencopy_frame_v1_get_user_data(frame));
|
||||
|
||||
BOOST_LOG(error) << "Failed to create buffer from params"sv;
|
||||
BOOST_LOG(error) << "[wayland] Failed to create buffer from params"sv;
|
||||
self->cleanup_gbm();
|
||||
|
||||
zwp_linux_buffer_params_v1_destroy(params);
|
||||
@@ -483,12 +479,16 @@ namespace wl {
|
||||
std::uint32_t tv_sec_lo,
|
||||
std::uint32_t tv_nsec
|
||||
) {
|
||||
BOOST_LOG(debug) << "Frame ready"sv;
|
||||
|
||||
// Frame is ready for use, GBM buffer now contains screen content
|
||||
current_frame->destroy();
|
||||
current_frame = get_next_frame();
|
||||
|
||||
std::uint64_t sec = (std::uint64_t(tv_sec_hi) << 32) | tv_sec_lo;
|
||||
auto ready_ts = std::chrono::seconds(sec) + std::chrono::nanoseconds(tv_nsec);
|
||||
current_frame->frame_timestamp = std::chrono::steady_clock::time_point {
|
||||
std::chrono::duration_cast<std::chrono::steady_clock::duration>(ready_ts)
|
||||
};
|
||||
|
||||
// Keep the GBM buffer alive but destroy the Wayland objects
|
||||
if (current_wl_buffer) {
|
||||
wl_buffer_destroy(current_wl_buffer);
|
||||
@@ -503,7 +503,7 @@ namespace wl {
|
||||
|
||||
// Failed callback
|
||||
void dmabuf_t::failed(zwlr_screencopy_frame_v1 *frame) {
|
||||
BOOST_LOG(error) << "Frame capture failed"sv;
|
||||
BOOST_LOG(error) << "[wayland] Frame capture failed"sv;
|
||||
|
||||
// Clean up resources
|
||||
cleanup_gbm();
|
||||
@@ -514,6 +514,7 @@ namespace wl {
|
||||
status = REINIT;
|
||||
}
|
||||
|
||||
// Only called if using zwlr_screencopy_frame_v1_copy_with_damage()
|
||||
void dmabuf_t::damage(
|
||||
zwlr_screencopy_frame_v1 *frame,
|
||||
std::uint32_t x,
|
||||
@@ -550,7 +551,7 @@ namespace wl {
|
||||
display.roundtrip();
|
||||
|
||||
if (!interface[interface_t::XDG_OUTPUT]) {
|
||||
BOOST_LOG(error) << "Missing Wayland wire XDG_OUTPUT"sv;
|
||||
BOOST_LOG(error) << "[wayland] Missing Wayland wire XDG_OUTPUT"sv;
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace wl {
|
||||
void destroy();
|
||||
|
||||
egl::surface_descriptor_t sd;
|
||||
std::optional<std::chrono::steady_clock::time_point> frame_timestamp;
|
||||
};
|
||||
|
||||
class dmabuf_t {
|
||||
|
||||
@@ -29,7 +29,18 @@ namespace wl {
|
||||
class wlr_t: public platf::display_t {
|
||||
public:
|
||||
int init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
delay = std::chrono::nanoseconds {1s} / config.framerate;
|
||||
// calculate frame interval we should capture at
|
||||
if (config.framerateX100 > 0) {
|
||||
AVRational fps_strict = ::video::framerateX100_to_rational(config.framerateX100);
|
||||
delay = std::chrono::nanoseconds(
|
||||
(static_cast<int64_t>(fps_strict.den) * 1'000'000'000LL) / fps_strict.num
|
||||
);
|
||||
BOOST_LOG(info) << "[wlgrab] Requested frame rate [" << fps_strict.num << "/" << fps_strict.den << ", approx. " << av_q2d(fps_strict) << " fps]";
|
||||
} else {
|
||||
delay = std::chrono::nanoseconds {1s} / config.framerate;
|
||||
BOOST_LOG(info) << "[wlgrab] Requested frame rate [" << config.framerate << "fps]";
|
||||
}
|
||||
|
||||
mem_type = hwdevice_type;
|
||||
|
||||
if (display.init()) {
|
||||
@@ -41,12 +52,12 @@ namespace wl {
|
||||
display.roundtrip();
|
||||
|
||||
if (!interface[wl::interface_t::XDG_OUTPUT]) {
|
||||
BOOST_LOG(error) << "Missing Wayland wire for xdg_output"sv;
|
||||
BOOST_LOG(error) << "[wlgrab] Missing Wayland wire for xdg_output"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
|
||||
BOOST_LOG(error) << "Missing Wayland wire for wlr-export-dmabuf"sv;
|
||||
BOOST_LOG(error) << "[wlgrab] Missing Wayland wire for wlr-export-dmabuf"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -88,12 +99,12 @@ namespace wl {
|
||||
this->env_logical_width = desktop_logical_width;
|
||||
this->env_logical_height = desktop_logical_height;
|
||||
|
||||
BOOST_LOG(info) << "Selected monitor ["sv << monitor->description << "] for streaming"sv;
|
||||
BOOST_LOG(debug) << "Offset: "sv << offset_x << 'x' << offset_y;
|
||||
BOOST_LOG(debug) << "Resolution: "sv << width << 'x' << height;
|
||||
BOOST_LOG(debug) << "Logical Resolution: "sv << logical_width << 'x' << logical_height;
|
||||
BOOST_LOG(debug) << "Desktop Resolution: "sv << env_width << 'x' << env_height;
|
||||
BOOST_LOG(debug) << "Logical Desktop Resolution: "sv << env_logical_width << 'x' << env_logical_height;
|
||||
BOOST_LOG(info) << "[wlgrab] Selected monitor ["sv << monitor->description << "] for streaming"sv;
|
||||
BOOST_LOG(debug) << "[wlgrab] Offset: "sv << offset_x << 'x' << offset_y;
|
||||
BOOST_LOG(debug) << "[wlgrab] Resolution: "sv << width << 'x' << height;
|
||||
BOOST_LOG(debug) << "[wlgrab] Logical Resolution: "sv << logical_width << 'x' << logical_height;
|
||||
BOOST_LOG(debug) << "[wlgrab] Desktop Resolution: "sv << env_width << 'x' << env_height;
|
||||
BOOST_LOG(debug) << "[wlgrab] Logical Desktop Resolution: "sv << env_logical_width << 'x' << env_logical_height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -177,7 +188,7 @@ namespace wl {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
BOOST_LOG(error) << "[wlgrab] Unrecognized capture status ["sv << std::to_underlying(status) << ']';
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -210,11 +221,13 @@ namespace wl {
|
||||
int w;
|
||||
gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
|
||||
gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
|
||||
BOOST_LOG(debug) << "width and height: w "sv << w << " h "sv << h;
|
||||
BOOST_LOG(debug) << "[wlgrab] width and height: w "sv << w << " h "sv << h;
|
||||
|
||||
gl::ctx.GetTextureSubImage((*rgb_opt)->tex[0], 0, 0, 0, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out->height * img_out->row_pitch, img_out->data);
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
img_out->frame_timestamp = current_frame->frame_timestamp;
|
||||
|
||||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
@@ -308,7 +321,7 @@ namespace wl {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
BOOST_LOG(error) << "[wlgrab] Unrecognized capture status ["sv << std::to_underlying(status) << ']';
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -334,6 +347,7 @@ namespace wl {
|
||||
img->sequence = sequence;
|
||||
|
||||
img->sd = current_frame->sd;
|
||||
img->frame_timestamp = current_frame->frame_timestamp;
|
||||
|
||||
// Prevent dmabuf from closing the file descriptors.
|
||||
std::fill_n(current_frame->sd.fds, 4, -1);
|
||||
@@ -385,7 +399,7 @@ namespace wl {
|
||||
namespace platf {
|
||||
std::shared_ptr<display_t> wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) {
|
||||
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
|
||||
BOOST_LOG(error) << "[wlgrab] Could not initialize display with the given hw device type."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -420,12 +434,12 @@ namespace platf {
|
||||
display.roundtrip();
|
||||
|
||||
if (!interface[wl::interface_t::XDG_OUTPUT]) {
|
||||
BOOST_LOG(warning) << "Missing Wayland wire for xdg_output"sv;
|
||||
BOOST_LOG(warning) << "[wlgrab] Missing Wayland wire for xdg_output"sv;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
|
||||
BOOST_LOG(warning) << "Missing Wayland wire for wlr-export-dmabuf"sv;
|
||||
BOOST_LOG(warning) << "[wlgrab] Missing Wayland wire for wlr-export-dmabuf"sv;
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -438,20 +452,20 @@ namespace platf {
|
||||
|
||||
display.roundtrip();
|
||||
|
||||
BOOST_LOG(info) << "-------- Start of Wayland monitor list --------"sv;
|
||||
BOOST_LOG(info) << "[wlgrab] -------- Start of Wayland monitor list --------"sv;
|
||||
|
||||
for (int x = 0; x < interface.monitors.size(); ++x) {
|
||||
auto monitor = interface.monitors[x].get();
|
||||
|
||||
wl::env_width = std::max(wl::env_width, (int) (monitor->viewport.offset_x + monitor->viewport.width));
|
||||
wl::env_height = std::max(wl::env_height, (int) (monitor->viewport.offset_y + monitor->viewport.height));
|
||||
wl::env_width = std::max(wl::env_width, monitor->viewport.offset_x + monitor->viewport.width);
|
||||
wl::env_height = std::max(wl::env_height, monitor->viewport.offset_y + monitor->viewport.height);
|
||||
|
||||
BOOST_LOG(info) << "Monitor " << x << " is "sv << monitor->name << ": "sv << monitor->description;
|
||||
BOOST_LOG(info) << "[wlgrab] Monitor " << x << " is "sv << monitor->name << ": "sv << monitor->description;
|
||||
|
||||
display_names.emplace_back(std::to_string(x));
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "--------- End of Wayland monitor list ---------"sv;
|
||||
BOOST_LOG(info) << "[wlgrab] --------- End of Wayland monitor list ---------"sv;
|
||||
|
||||
return display_names;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user