mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-12-23 01:59:49 +01:00
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:
parent
e28f5e06e6
commit
058bd97815
5 changed files with 99 additions and 58 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue