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 {
uint32_t size;
uint32_t stride;
bool y_invert;
uint64_t tv_sec;
uint32_t tv_nsec;
struct xdpw_frame_damage damage;
struct wl_buffer *buffer;
struct xdpw_buffer *xdpw_buffer;
struct pw_buffer *current_pw_buffer;
};
@ -68,6 +66,21 @@ struct xdpw_screencopy_frame_info {
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 {
// xdpw
@ -98,6 +111,7 @@ struct xdpw_screencast_instance {
bool initialized;
struct xdpw_frame current_frame;
enum xdpw_frame_state frame_state;
struct wl_list buffer_list;
// pipewire
struct pw_stream *stream;
@ -136,9 +150,9 @@ struct xdpw_wlr_output {
};
void randname(char *buf);
int anonymous_shm_open(void);
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);
struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast,
struct xdpw_screencopy_frame_info *frame_info);
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_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);
// 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) {
logprint(ERROR, "pipewire: unable to create anonymous filedescriptor");
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);
struct xdpw_buffer *xdpw_buffer = xdpw_buffer_create(cast, &cast->screencopy_frame_info);
if (xdpw_buffer == NULL) {
logprint(ERROR, "pipewire: failed to create xdpw buffer");
cast->err = 1;
return;
}
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) {
@ -154,20 +143,18 @@ static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer
logprint(TRACE, "pipewire: remove buffer event handle");
struct spa_data *d = buffer->buffer->datas;
if (cast->current_frame.current_pw_buffer == buffer) {
logprint(TRACE, "pipewire: remove buffer currently in use");
cast->current_frame.current_pw_buffer = NULL;
cast->current_frame.buffer = NULL;
cast->current_frame.xdpw_buffer = NULL;
}
switch (d[0].type) {
case SPA_DATA_MemFd:
wl_buffer_destroy(buffer->user_data);
close(d[0].fd);
break;
default:
break;
struct xdpw_buffer *xdpw_buffer = buffer->user_data;
if (xdpw_buffer) {
xdpw_buffer_destroy(xdpw_buffer);
buffer->user_data = NULL;
}
buffer->buffer->datas[0].fd = -1;
buffer->user_data = NULL;
}
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);
if ((cast->current_frame.current_pw_buffer = pw_stream_dequeue_buffer(cast->stream)) == NULL) {
logprint(WARN, "pipewire: out of buffers");
cast->current_frame.buffer = NULL;
cast->current_frame.xdpw_buffer = NULL;
return;
}
struct spa_buffer *spa_buf = cast->current_frame.current_pw_buffer->buffer;
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;
cast->current_frame.xdpw_buffer = cast->current_frame.current_pw_buffer->user_data;
}
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: size %d", d[0].maxsize);
logprint(TRACE, "pipewire: stride %d", d[0].chunk->stride);
logprint(TRACE, "pipewire: width %d", cast->screencopy_frame_info.width);
logprint(TRACE, "pipewire: height %d", cast->screencopy_frame_info.height);
logprint(TRACE, "pipewire: width %d", cast->current_frame.xdpw_buffer->width);
logprint(TRACE, "pipewire: height %d", cast->current_frame.xdpw_buffer->height);
logprint(TRACE, "pipewire: y_invert %d", cast->current_frame.y_invert);
logprint(TRACE, "********************");
pw_stream_queue_buffer(cast->stream, pw_buf);
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) {

View file

@ -70,6 +70,7 @@ void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx,
cast->with_cursor = with_cursor;
cast->refcount = 1;
cast->node_id = SPA_ID_INVALID;
wl_list_init(&cast->buffer_list);
logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount);
wl_list_insert(&ctx->screencast_instances, &cast->link);
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);
xdpw_pwr_stream_destroy(cast);
assert(wl_list_length(&cast->buffer_list) == 0);
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";
int retries = 100;
@ -38,7 +38,7 @@ int anonymous_shm_open(void) {
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) {
struct xdpw_screencast_context *ctx = cast->ctx;
int size = stride * height;
@ -55,6 +55,48 @@ struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, in
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) {
switch (format) {
case WL_SHM_FORMAT_ARGB8888:

View file

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