screencast: introduce xdpw_buffer

It showed that handling self contained buffers is much easier then have
the metadata of the buffer seperated from the actual buffer attached to
the screencast instance.

The goal of the following changes is to separate the meta informations
like requested buffer attributes and wlr_screencast data from the actual
buffers.

This enables us to:
* Simplify the flow between the PipeWire loop and the wlroots one
* Track and apply damage to each used buffer (ext_screencopy)
This commit is contained in:
columbarius 2022-03-02 01:57:11 +01:00
parent e28f5e06e6
commit 058bd97815
5 changed files with 99 additions and 58 deletions

View file

@ -50,13 +50,11 @@ struct xdpw_frame_damage {
}; };
struct xdpw_frame { struct xdpw_frame {
uint32_t size;
uint32_t stride;
bool y_invert; bool y_invert;
uint64_t tv_sec; uint64_t tv_sec;
uint32_t tv_nsec; uint32_t tv_nsec;
struct xdpw_frame_damage damage; struct xdpw_frame_damage damage;
struct wl_buffer *buffer; struct xdpw_buffer *xdpw_buffer;
struct pw_buffer *current_pw_buffer; struct pw_buffer *current_pw_buffer;
}; };
@ -68,6 +66,21 @@ struct xdpw_screencopy_frame_info {
enum wl_shm_format format; enum wl_shm_format format;
}; };
struct xdpw_buffer {
struct wl_list link;
uint32_t width;
uint32_t height;
enum wl_shm_format format;
int fd;
uint32_t size;
uint32_t stride;
uint32_t offset;
struct wl_buffer *buffer;
};
struct xdpw_screencast_context { struct xdpw_screencast_context {
// xdpw // xdpw
@ -98,6 +111,7 @@ struct xdpw_screencast_instance {
bool initialized; bool initialized;
struct xdpw_frame current_frame; struct xdpw_frame current_frame;
enum xdpw_frame_state frame_state; enum xdpw_frame_state frame_state;
struct wl_list buffer_list;
// pipewire // pipewire
struct pw_stream *stream; struct pw_stream *stream;
@ -136,9 +150,9 @@ struct xdpw_wlr_output {
}; };
void randname(char *buf); void randname(char *buf);
int anonymous_shm_open(void); struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast,
struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, int fd, struct xdpw_screencopy_frame_info *frame_info);
enum wl_shm_format fmt, int width, int height, int stride); void xdpw_buffer_destroy(struct xdpw_buffer *buffer);
enum spa_video_format xdpw_format_pw_from_wl_shm(enum wl_shm_format format); enum spa_video_format xdpw_format_pw_from_wl_shm(enum wl_shm_format format);
enum spa_video_format xdpw_format_pw_strip_alpha(enum spa_video_format format); enum spa_video_format xdpw_format_pw_strip_alpha(enum spa_video_format format);

View file

@ -118,35 +118,24 @@ static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) {
} }
logprint(TRACE, "pipewire: selected buffertype %u", d[0].type); logprint(TRACE, "pipewire: selected buffertype %u", d[0].type);
// Prepare buffer for choosen type
if (d[0].type == SPA_DATA_MemFd) {
d[0].maxsize = cast->screencopy_frame_info.size;
d[0].mapoffset = 0;
d[0].chunk->size = cast->screencopy_frame_info.size;
d[0].chunk->stride = cast->screencopy_frame_info.stride;
d[0].chunk->offset = 0;
d[0].flags = 0;
d[0].fd = anonymous_shm_open();
d[0].data = NULL;
if (d[0].fd == -1) { struct xdpw_buffer *xdpw_buffer = xdpw_buffer_create(cast, &cast->screencopy_frame_info);
logprint(ERROR, "pipewire: unable to create anonymous filedescriptor"); if (xdpw_buffer == NULL) {
cast->err = 1; logprint(ERROR, "pipewire: failed to create xdpw buffer");
return; cast->err = 1;
} return;
if (ftruncate(d[0].fd, d[0].maxsize) < 0) {
logprint(ERROR, "pipewire: unable to truncate filedescriptor");
close(d[0].fd);
d[0].fd = -1;
cast->err = 1;
return;
}
// create wl_buffer
buffer->user_data = import_wl_shm_buffer(cast, d[0].fd, cast->screencopy_frame_info.format,
cast->screencopy_frame_info.width, cast->screencopy_frame_info.height, cast->screencopy_frame_info.stride);
} }
wl_list_insert(&cast->buffer_list, &xdpw_buffer->link);
buffer->user_data = xdpw_buffer;
d[0].maxsize = xdpw_buffer->size;
d[0].mapoffset = 0;
d[0].chunk->size = xdpw_buffer->size;
d[0].chunk->stride = xdpw_buffer->stride;
d[0].chunk->offset = xdpw_buffer->offset;
d[0].flags = 0;
d[0].fd = xdpw_buffer->fd;
d[0].data = NULL;
} }
static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer) { static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer) {
@ -154,20 +143,18 @@ static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer
logprint(TRACE, "pipewire: remove buffer event handle"); logprint(TRACE, "pipewire: remove buffer event handle");
struct spa_data *d = buffer->buffer->datas;
if (cast->current_frame.current_pw_buffer == buffer) { if (cast->current_frame.current_pw_buffer == buffer) {
logprint(TRACE, "pipewire: remove buffer currently in use"); logprint(TRACE, "pipewire: remove buffer currently in use");
cast->current_frame.current_pw_buffer = NULL; cast->current_frame.current_pw_buffer = NULL;
cast->current_frame.buffer = NULL; cast->current_frame.xdpw_buffer = NULL;
} }
switch (d[0].type) { struct xdpw_buffer *xdpw_buffer = buffer->user_data;
case SPA_DATA_MemFd: if (xdpw_buffer) {
wl_buffer_destroy(buffer->user_data); xdpw_buffer_destroy(xdpw_buffer);
close(d[0].fd); buffer->user_data = NULL;
break;
default:
break;
} }
buffer->buffer->datas[0].fd = -1;
buffer->user_data = NULL;
} }
static const struct pw_stream_events pwr_stream_events = { static const struct pw_stream_events pwr_stream_events = {
@ -184,15 +171,11 @@ 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; cast->current_frame.xdpw_buffer = NULL;
return; return;
} }
struct spa_buffer *spa_buf = cast->current_frame.current_pw_buffer->buffer; cast->current_frame.xdpw_buffer = cast->current_frame.current_pw_buffer->user_data;
struct spa_data *d = spa_buf->datas;
cast->current_frame.size = d[0].chunk->size;
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) {
@ -230,15 +213,15 @@ void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) {
logprint(TRACE, "pipewire: fd %u", d[0].fd); 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_info.width); logprint(TRACE, "pipewire: width %d", cast->current_frame.xdpw_buffer->width);
logprint(TRACE, "pipewire: height %d", cast->screencopy_frame_info.height); logprint(TRACE, "pipewire: height %d", cast->current_frame.xdpw_buffer->height);
logprint(TRACE, "pipewire: y_invert %d", cast->current_frame.y_invert); logprint(TRACE, "pipewire: y_invert %d", cast->current_frame.y_invert);
logprint(TRACE, "********************"); logprint(TRACE, "********************");
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; cast->current_frame.xdpw_buffer = NULL;
} }
void pwr_update_stream_param(struct xdpw_screencast_instance *cast) { void pwr_update_stream_param(struct xdpw_screencast_instance *cast) {

View file

@ -70,6 +70,7 @@ void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx,
cast->with_cursor = with_cursor; cast->with_cursor = with_cursor;
cast->refcount = 1; cast->refcount = 1;
cast->node_id = SPA_ID_INVALID; cast->node_id = SPA_ID_INVALID;
wl_list_init(&cast->buffer_list);
logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount); logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount);
wl_list_insert(&ctx->screencast_instances, &cast->link); wl_list_insert(&ctx->screencast_instances, &cast->link);
logprint(INFO, "xdpw: %d active screencast instances", logprint(INFO, "xdpw: %d active screencast instances",
@ -91,6 +92,7 @@ void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast) {
wl_list_remove(&cast->link); wl_list_remove(&cast->link);
xdpw_pwr_stream_destroy(cast); xdpw_pwr_stream_destroy(cast);
assert(wl_list_length(&cast->buffer_list) == 0);
free(cast); free(cast);
} }

View file

@ -19,7 +19,7 @@ void randname(char *buf) {
} }
} }
int anonymous_shm_open(void) { static int anonymous_shm_open(void) {
char name[] = "/xdpw-shm-XXXXXX"; char name[] = "/xdpw-shm-XXXXXX";
int retries = 100; int retries = 100;
@ -38,7 +38,7 @@ int anonymous_shm_open(void) {
return -1; return -1;
} }
struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, int fd, static struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, int fd,
enum wl_shm_format fmt, int width, int height, int stride) { enum wl_shm_format fmt, int width, int height, int stride) {
struct xdpw_screencast_context *ctx = cast->ctx; struct xdpw_screencast_context *ctx = cast->ctx;
int size = stride * height; int size = stride * height;
@ -55,6 +55,48 @@ struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, in
return buffer; return buffer;
} }
struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast,
struct xdpw_screencopy_frame_info *frame_info) {
struct xdpw_buffer *buffer = calloc(1, sizeof(struct xdpw_buffer));
buffer->width = frame_info->width;
buffer->height = frame_info->height;
buffer->format = frame_info->format;
buffer->size = frame_info->size;
buffer->stride = frame_info->stride;
buffer->offset = 0;
buffer->fd = anonymous_shm_open();
if (buffer->fd == -1) {
logprint(ERROR, "xdpw: unable to create anonymous filedescriptor");
free(buffer);
return NULL;
}
if (ftruncate(buffer->fd, buffer->size) < 0) {
logprint(ERROR, "xdpw: unable to truncate filedescriptor");
close(buffer->fd);
free(buffer);
return NULL;
}
buffer->buffer = import_wl_shm_buffer(cast, buffer->fd, frame_info->format,
frame_info->width, frame_info->height, frame_info->stride);
if (buffer->buffer == NULL) {
logprint(ERROR, "xdpw: unable to create wl_buffer");
close(buffer->fd);
free(buffer);
return NULL;
}
return buffer;
}
void xdpw_buffer_destroy(struct xdpw_buffer *buffer) {
wl_buffer_destroy(buffer->buffer);
close(buffer->fd);
wl_list_remove(&buffer->link);
free(buffer);
}
enum spa_video_format xdpw_format_pw_from_wl_shm(enum wl_shm_format format) { enum spa_video_format xdpw_format_pw_from_wl_shm(enum wl_shm_format format) {
switch (format) { switch (format) {
case WL_SHM_FORMAT_ARGB8888: case WL_SHM_FORMAT_ARGB8888:

View file

@ -134,18 +134,18 @@ static void wlr_frame_buffer_done(void *data,
return; return;
} }
assert(cast->current_frame.xdpw_buffer);
// Check if dequeued buffer is compatible with announced buffer // Check if dequeued buffer is compatible with announced buffer
if (cast->current_frame.size != cast->screencopy_frame_info.size || if (cast->current_frame.xdpw_buffer->size != cast->screencopy_frame_info.size ||
cast->current_frame.stride != cast->screencopy_frame_info.stride) { cast->current_frame.xdpw_buffer->stride != cast->screencopy_frame_info.stride) {
logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions"); logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions");
cast->frame_state = XDPW_FRAME_STATE_FAILED; cast->frame_state = XDPW_FRAME_STATE_FAILED;
xdpw_wlr_frame_finish(cast); xdpw_wlr_frame_finish(cast);
return; return;
} }
assert(cast->current_frame.buffer); zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->current_frame.xdpw_buffer->buffer);
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);