mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-11-22 14:35:57 +01:00
screencast: use pipewire buffers directly for wlroots screencopy
Instead of using one wl_buffer to export buffers from wlroots and then copy the content into the buffer dequed from pipewire, we can create a wl_buffer for each pipewire buffer directly at allocation time and attach it to the data attribute. Those wl_buffers can be directly handed over to the wlroots screencopy protocol and so removing one copy.
This commit is contained in:
parent
78efdff460
commit
4d892f2a43
2 changed files with 19 additions and 103 deletions
|
@ -13,22 +13,6 @@
|
||||||
#include "xdpw.h"
|
#include "xdpw.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
static void writeFrameData(void *pwFramePointer, void *wlrFramePointer,
|
|
||||||
uint32_t height, uint32_t stride, bool inverted) {
|
|
||||||
if (!inverted) {
|
|
||||||
memcpy(pwFramePointer, wlrFramePointer, height * stride);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < (size_t)height; ++i) {
|
|
||||||
void *flippedWlrRowPointer = wlrFramePointer + ((height - i - 1) * stride);
|
|
||||||
void *pwRowPointer = pwFramePointer + (i * stride);
|
|
||||||
memcpy(pwRowPointer, flippedWlrRowPointer, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_format format,
|
static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_format format,
|
||||||
uint32_t width, uint32_t height, uint32_t framerate) {
|
uint32_t width, uint32_t height, uint32_t framerate) {
|
||||||
struct spa_pod_frame f[1];
|
struct spa_pod_frame f[1];
|
||||||
|
@ -143,6 +127,7 @@ static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) {
|
||||||
d[0].chunk->offset = 0;
|
d[0].chunk->offset = 0;
|
||||||
d[0].flags = 0;
|
d[0].flags = 0;
|
||||||
d[0].fd = anonymous_shm_open();
|
d[0].fd = anonymous_shm_open();
|
||||||
|
d[0].data = NULL;
|
||||||
|
|
||||||
if (d[0].fd == -1) {
|
if (d[0].fd == -1) {
|
||||||
logprint(ERROR, "pipewire: unable to create anonymous filedescriptor");
|
logprint(ERROR, "pipewire: unable to create anonymous filedescriptor");
|
||||||
|
@ -158,13 +143,9 @@ static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mmap buffer, so we can use the data_ptr in on_process
|
// create wl_buffer
|
||||||
d[0].data = mmap(NULL, d[0].maxsize, PROT_READ | PROT_WRITE, MAP_SHARED, d[0].fd, d[0].mapoffset);
|
buffer->user_data = import_wl_shm_buffer(cast, d[0].fd, cast->screencopy_frame.format,
|
||||||
if (d[0].data == MAP_FAILED) {
|
cast->screencopy_frame.width, cast->screencopy_frame.height, cast->screencopy_frame.stride);
|
||||||
logprint(ERROR, "pipewire: unable to mmap memory");
|
|
||||||
cast->err = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +155,7 @@ static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer
|
||||||
struct spa_data *d = buffer->buffer->datas;
|
struct spa_data *d = buffer->buffer->datas;
|
||||||
switch (d[0].type) {
|
switch (d[0].type) {
|
||||||
case SPA_DATA_MemFd:
|
case SPA_DATA_MemFd:
|
||||||
munmap(d[0].data, d[0].maxsize);
|
wl_buffer_destroy(buffer->user_data);
|
||||||
close(d[0].fd);
|
close(d[0].fd);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -196,6 +177,7 @@ void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast) {
|
||||||
assert(cast->current_frame.current_pw_buffer == NULL);
|
assert(cast->current_frame.current_pw_buffer == NULL);
|
||||||
if ((cast->current_frame.current_pw_buffer = pw_stream_dequeue_buffer(cast->stream)) == NULL) {
|
if ((cast->current_frame.current_pw_buffer = pw_stream_dequeue_buffer(cast->stream)) == NULL) {
|
||||||
logprint(WARN, "pipewire: out of buffers");
|
logprint(WARN, "pipewire: out of buffers");
|
||||||
|
cast->current_frame.buffer = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +185,7 @@ void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast) {
|
||||||
struct spa_data *d = spa_buf->datas;
|
struct spa_data *d = spa_buf->datas;
|
||||||
cast->current_frame.size = d[0].chunk->size;
|
cast->current_frame.size = d[0].chunk->size;
|
||||||
cast->current_frame.stride = d[0].chunk->stride;
|
cast->current_frame.stride = d[0].chunk->stride;
|
||||||
|
cast->current_frame.buffer = cast->current_frame.current_pw_buffer->user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) {
|
void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) {
|
||||||
|
@ -224,16 +207,14 @@ void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) {
|
||||||
h->seq = cast->seq++;
|
h->seq = cast->seq++;
|
||||||
h->dts_offset = 0;
|
h->dts_offset = 0;
|
||||||
}
|
}
|
||||||
if (d[0].data == NULL) {
|
|
||||||
logprint(TRACE, "pipewire: data pointer undefined");
|
if (cast->screencopy_frame.y_invert) {
|
||||||
goto queue;
|
//TODO: Flip buffer or set stride negative
|
||||||
|
cast->err = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFrameData(d[0].data, cast->screencopy_frame.data, cast->screencopy_frame.height,
|
|
||||||
cast->screencopy_frame.stride, cast->screencopy_frame.y_invert);
|
|
||||||
|
|
||||||
logprint(TRACE, "********************");
|
logprint(TRACE, "********************");
|
||||||
logprint(TRACE, "pipewire: pointer %p", d[0].data);
|
logprint(TRACE, "pipewire: fd %u", d[0].fd);
|
||||||
logprint(TRACE, "pipewire: size %d", d[0].maxsize);
|
logprint(TRACE, "pipewire: size %d", d[0].maxsize);
|
||||||
logprint(TRACE, "pipewire: stride %d", d[0].chunk->stride);
|
logprint(TRACE, "pipewire: stride %d", d[0].chunk->stride);
|
||||||
logprint(TRACE, "pipewire: width %d", cast->screencopy_frame.width);
|
logprint(TRACE, "pipewire: width %d", cast->screencopy_frame.width);
|
||||||
|
@ -241,10 +222,10 @@ void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) {
|
||||||
logprint(TRACE, "pipewire: y_invert %d", cast->screencopy_frame.y_invert);
|
logprint(TRACE, "pipewire: y_invert %d", cast->screencopy_frame.y_invert);
|
||||||
logprint(TRACE, "********************");
|
logprint(TRACE, "********************");
|
||||||
|
|
||||||
queue:
|
|
||||||
pw_stream_queue_buffer(cast->stream, pw_buf);
|
pw_stream_queue_buffer(cast->stream, pw_buf);
|
||||||
|
|
||||||
cast->current_frame.current_pw_buffer = NULL;
|
cast->current_frame.current_pw_buffer = NULL;
|
||||||
|
cast->current_frame.buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pwr_update_stream_param(struct xdpw_screencast_instance *cast) {
|
void pwr_update_stream_param(struct xdpw_screencast_instance *cast) {
|
||||||
|
|
|
@ -20,28 +20,9 @@
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "fps_limit.h"
|
#include "fps_limit.h"
|
||||||
|
|
||||||
static void wlr_frame_buffer_destroy(struct xdpw_screencast_instance *cast) {
|
|
||||||
// Even though this check may be deemed unnecessary,
|
|
||||||
// this has been found to cause SEGFAULTs, like this one:
|
|
||||||
// https://github.com/emersion/xdg-desktop-portal-wlr/issues/50
|
|
||||||
if (cast->screencopy_frame.data != NULL) {
|
|
||||||
munmap(cast->screencopy_frame.data, cast->screencopy_frame.size);
|
|
||||||
cast->screencopy_frame.data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cast->screencopy_frame.buffer != NULL) {
|
|
||||||
wl_buffer_destroy(cast->screencopy_frame.buffer);
|
|
||||||
cast->screencopy_frame.buffer = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast) {
|
void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast) {
|
||||||
zwlr_screencopy_frame_v1_destroy(cast->wlr_frame);
|
zwlr_screencopy_frame_v1_destroy(cast->wlr_frame);
|
||||||
cast->wlr_frame = NULL;
|
cast->wlr_frame = NULL;
|
||||||
if (cast->quit || cast->err) {
|
|
||||||
wlr_frame_buffer_destroy(cast);
|
|
||||||
logprint(TRACE, "xdpw: screencopy_frame buffer destroyed");
|
|
||||||
}
|
|
||||||
logprint(TRACE, "wlroots: frame destroyed");
|
logprint(TRACE, "wlroots: frame destroyed");
|
||||||
|
|
||||||
if (cast->quit || cast->err) {
|
if (cast->quit || cast->err) {
|
||||||
|
@ -63,44 +44,6 @@ void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wl_buffer *create_shm_buffer(struct xdpw_screencast_instance *cast,
|
|
||||||
enum wl_shm_format fmt, int width, int height, int stride,
|
|
||||||
void **data_out) {
|
|
||||||
struct xdpw_screencast_context *ctx = cast->ctx;
|
|
||||||
int size = stride * height;
|
|
||||||
|
|
||||||
int fd = anonymous_shm_open();
|
|
||||||
if (fd < 0) {
|
|
||||||
logprint(ERROR, "wlroots: shm_open failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret;
|
|
||||||
while ((ret = ftruncate(fd, size)) == EINTR);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
close(fd);
|
|
||||||
logprint(ERROR, "wlroots: ftruncate failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (data == MAP_FAILED) {
|
|
||||||
logprint(ERROR, "wlroots: mmap failed: %m");
|
|
||||||
close(fd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_shm_pool *pool = wl_shm_create_pool(ctx->shm, fd, size);
|
|
||||||
close(fd);
|
|
||||||
struct wl_buffer *buffer =
|
|
||||||
wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt);
|
|
||||||
wl_shm_pool_destroy(pool);
|
|
||||||
|
|
||||||
*data_out = data;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wlr_frame_linux_dmabuf(void *data,
|
static void wlr_frame_linux_dmabuf(void *data,
|
||||||
struct zwlr_screencopy_frame_v1 *frame,
|
struct zwlr_screencopy_frame_v1 *frame,
|
||||||
uint32_t format, uint32_t width, uint32_t height) {
|
uint32_t format, uint32_t width, uint32_t height) {
|
||||||
|
@ -146,7 +89,12 @@ static void wlr_frame_buffer_done(void *data,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->screencopy_frame.buffer);
|
if (cast->current_frame.buffer == NULL) {
|
||||||
|
logprint(ERROR, "wlroots: imported buffer doesn't contain a wlr_buffer");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->current_frame.buffer);
|
||||||
logprint(TRACE, "wlroots: frame copied");
|
logprint(TRACE, "wlroots: frame copied");
|
||||||
|
|
||||||
fps_limit_measure_start(&cast->fps_limit, cast->framerate);
|
fps_limit_measure_start(&cast->fps_limit, cast->framerate);
|
||||||
|
@ -165,19 +113,6 @@ static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame,
|
||||||
cast->screencopy_frame.size = stride * height;
|
cast->screencopy_frame.size = stride * height;
|
||||||
cast->screencopy_frame.format = format;
|
cast->screencopy_frame.format = format;
|
||||||
|
|
||||||
if (cast->screencopy_frame.buffer == NULL) {
|
|
||||||
logprint(DEBUG, "wlroots: create shm buffer");
|
|
||||||
cast->screencopy_frame.buffer = create_shm_buffer(cast, format, width, height,
|
|
||||||
stride, &cast->screencopy_frame.data);
|
|
||||||
} else {
|
|
||||||
logprint(TRACE,"wlroots: shm buffer exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cast->screencopy_frame.buffer == NULL) {
|
|
||||||
logprint(ERROR, "wlroots: failed to create buffer");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zwlr_screencopy_manager_v1_get_version(cast->ctx->screencopy_manager) < 3) {
|
if (zwlr_screencopy_manager_v1_get_version(cast->ctx->screencopy_manager) < 3) {
|
||||||
wlr_frame_buffer_done(cast,frame);
|
wlr_frame_buffer_done(cast,frame);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue