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:
columbarius 2021-06-16 19:17:07 +02:00
parent 78efdff460
commit 4d892f2a43
2 changed files with 19 additions and 103 deletions

View file

@ -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) {

View file

@ -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);
} }