screencast: introduce buffer_type

We want to support WL_SHM and DMABUFS based buffers. The buffer_type
member tracks the type of a xdpw_buffer and screencopy_frame_info of the
screencast_instance will be an array with an element for each buffer type
indexed by the value of the buffer_type enum. Only members of the
xdpw_screencopy_frame_info relevant to the buffer type should be used.
This commit is contained in:
columbarius 2021-06-17 21:51:16 +02:00
parent 88a8a9cb7e
commit 12de0cd144
5 changed files with 70 additions and 42 deletions

View File

@ -22,6 +22,11 @@ enum source_types {
WINDOW = 2, WINDOW = 2,
}; };
enum buffer_type {
WL_SHM = 0,
DMABUF = 1,
};
enum xdpw_chooser_types { enum xdpw_chooser_types {
XDPW_CHOOSER_DEFAULT, XDPW_CHOOSER_DEFAULT,
XDPW_CHOOSER_NONE, XDPW_CHOOSER_NONE,
@ -68,6 +73,7 @@ struct xdpw_screencopy_frame_info {
struct xdpw_buffer { struct xdpw_buffer {
struct wl_list link; struct wl_list link;
enum buffer_type buffer_type;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
@ -127,11 +133,12 @@ struct xdpw_screencast_instance {
struct xdpw_wlr_output *target_output; struct xdpw_wlr_output *target_output;
uint32_t max_framerate; uint32_t max_framerate;
struct zwlr_screencopy_frame_v1 *wlr_frame; struct zwlr_screencopy_frame_v1 *wlr_frame;
struct xdpw_screencopy_frame_info screencopy_frame_info; struct xdpw_screencopy_frame_info screencopy_frame_info[2];
bool with_cursor; bool with_cursor;
int err; int err;
bool quit; bool quit;
bool need_buffer; bool need_buffer;
enum buffer_type buffer_type;
// fps limit // fps limit
struct fps_limit_state fps_limit; struct fps_limit_state fps_limit;
@ -152,7 +159,7 @@ struct xdpw_wlr_output {
void randname(char *buf); void randname(char *buf);
struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast, struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast,
struct xdpw_screencopy_frame_info *frame_info); enum buffer_type buffer_type, struct xdpw_screencopy_frame_info *frame_info);
void xdpw_buffer_destroy(struct xdpw_buffer *buffer); void xdpw_buffer_destroy(struct xdpw_buffer *buffer);
enum wl_shm_format xdpw_format_wl_shm_from_drm_fourcc(uint32_t format); enum wl_shm_format xdpw_format_wl_shm_from_drm_fourcc(uint32_t format);
uint32_t xdpw_format_drm_fourcc_from_wl_shm(enum wl_shm_format format); uint32_t xdpw_format_drm_fourcc_from_wl_shm(enum wl_shm_format format);

View File

@ -68,8 +68,8 @@ static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_fo
static uint32_t build_formats(struct spa_pod_builder *b, struct xdpw_screencast_instance *cast, static uint32_t build_formats(struct spa_pod_builder *b, struct xdpw_screencast_instance *cast,
const struct spa_pod *params[static 1]) { const struct spa_pod *params[static 1]) {
uint32_t param_count = 1; uint32_t param_count = 1;
params[0] = build_format(b, xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info.format), params[0] = build_format(b, xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[WL_SHM].format),
cast->screencopy_frame_info.width, cast->screencopy_frame_info.height, cast->framerate); cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height, cast->framerate);
return param_count; return param_count;
} }
@ -122,6 +122,8 @@ static void pwr_handle_stream_param_changed(void *data, uint32_t id,
struct spa_pod_builder b = struct spa_pod_builder b =
SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
const struct spa_pod *params[2]; const struct spa_pod *params[2];
uint32_t blocks;
uint32_t data_type;
if (!param || id != SPA_PARAM_Format) { if (!param || id != SPA_PARAM_Format) {
return; return;
@ -130,9 +132,16 @@ static void pwr_handle_stream_param_changed(void *data, uint32_t id,
spa_format_video_raw_parse(param, &cast->pwr_format); spa_format_video_raw_parse(param, &cast->pwr_format);
cast->framerate = (uint32_t)(cast->pwr_format.max_framerate.num / cast->pwr_format.max_framerate.denom); cast->framerate = (uint32_t)(cast->pwr_format.max_framerate.num / cast->pwr_format.max_framerate.denom);
if (spa_pod_find_prop(param, NULL, SPA_FORMAT_VIDEO_modifier) != NULL) {
abort();
} else {
cast->buffer_type = WL_SHM;
blocks = 1;
data_type = 1<<SPA_DATA_MemFd;
}
params[0] = build_buffer(&b, 1, cast->screencopy_frame_info.size, params[0] = build_buffer(&b, blocks, cast->screencopy_frame_info[cast->buffer_type].size,
cast->screencopy_frame_info.stride, 1<<SPA_DATA_MemFd); cast->screencopy_frame_info[cast->buffer_type].stride, data_type);
params[1] = spa_pod_builder_add_object(&b, params[1] = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
@ -152,6 +161,7 @@ static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) {
// Select buffer type from negotiation result // Select buffer type from negotiation result
if ((d[0].type & (1u << SPA_DATA_MemFd)) > 0) { if ((d[0].type & (1u << SPA_DATA_MemFd)) > 0) {
assert(cast->buffer_type == WL_SHM);
d[0].type = SPA_DATA_MemFd; d[0].type = SPA_DATA_MemFd;
} else { } else {
logprint(ERROR, "pipewire: unsupported buffer type"); logprint(ERROR, "pipewire: unsupported buffer type");
@ -161,7 +171,7 @@ 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);
struct xdpw_buffer *xdpw_buffer = xdpw_buffer_create(cast, &cast->screencopy_frame_info); struct xdpw_buffer *xdpw_buffer = xdpw_buffer_create(cast, cast->buffer_type, &cast->screencopy_frame_info[cast->buffer_type]);
if (xdpw_buffer == NULL) { if (xdpw_buffer == NULL) {
logprint(ERROR, "pipewire: failed to create xdpw buffer"); logprint(ERROR, "pipewire: failed to create xdpw buffer");
cast->err = 1; cast->err = 1;

View File

@ -455,7 +455,7 @@ static int method_screencast_start(sd_bus_message *msg, void *data,
"streams", "a(ua{sv})", 1, "streams", "a(ua{sv})", 1,
cast->node_id, 2, cast->node_id, 2,
"position", "(ii)", 0, 0, "position", "(ii)", 0, 0,
"size", "(ii)", cast->screencopy_frame_info.width, cast->screencopy_frame_info.height); "size", "(ii)", cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height);
if (ret < 0) { if (ret < 0) {
return ret; return ret;

View File

@ -57,35 +57,43 @@ static struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *c
} }
struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast, struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast,
struct xdpw_screencopy_frame_info *frame_info) { enum buffer_type buffer_type, struct xdpw_screencopy_frame_info *frame_info) {
struct xdpw_buffer *buffer = calloc(1, sizeof(struct xdpw_buffer)); struct xdpw_buffer *buffer = calloc(1, sizeof(struct xdpw_buffer));
buffer->width = frame_info->width; buffer->width = frame_info->width;
buffer->height = frame_info->height; buffer->height = frame_info->height;
buffer->format = frame_info->format; buffer->format = frame_info->format;
buffer->size = frame_info->size; buffer->buffer_type = buffer_type;
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) { switch (buffer_type) {
logprint(ERROR, "xdpw: unable to truncate filedescriptor"); case WL_SHM:
close(buffer->fd); buffer->size = frame_info->size;
free(buffer); buffer->stride = frame_info->stride;
return NULL; buffer->offset = 0;
} buffer->fd = anonymous_shm_open();
if (buffer->fd == -1) {
logprint(ERROR, "xdpw: unable to create anonymous filedescriptor");
free(buffer);
return NULL;
}
buffer->buffer = import_wl_shm_buffer(cast, buffer->fd, xdpw_format_wl_shm_from_drm_fourcc(frame_info->format), if (ftruncate(buffer->fd, buffer->size) < 0) {
frame_info->width, frame_info->height, frame_info->stride); logprint(ERROR, "xdpw: unable to truncate filedescriptor");
if (buffer->buffer == NULL) { close(buffer->fd);
logprint(ERROR, "xdpw: unable to create wl_buffer"); free(buffer);
close(buffer->fd); return NULL;
free(buffer); }
return NULL;
buffer->buffer = import_wl_shm_buffer(cast, buffer->fd, xdpw_format_wl_shm_from_drm_fourcc(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;
}
break;
case DMABUF:
abort();
} }
return buffer; return buffer;

View File

@ -87,11 +87,11 @@ static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame,
logprint(TRACE, "wlroots: buffer event handler"); logprint(TRACE, "wlroots: buffer event handler");
cast->wlr_frame = frame; cast->wlr_frame = frame;
cast->screencopy_frame_info.width = width; cast->screencopy_frame_info[WL_SHM].width = width;
cast->screencopy_frame_info.height = height; cast->screencopy_frame_info[WL_SHM].height = height;
cast->screencopy_frame_info.stride = stride; cast->screencopy_frame_info[WL_SHM].stride = stride;
cast->screencopy_frame_info.size = stride * height; cast->screencopy_frame_info[WL_SHM].size = stride * height;
cast->screencopy_frame_info.format = xdpw_format_drm_fourcc_from_wl_shm(format); cast->screencopy_frame_info[WL_SHM].format = xdpw_format_drm_fourcc_from_wl_shm(format);
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);
@ -116,10 +116,10 @@ static void wlr_frame_buffer_done(void *data,
} }
// Check if announced screencopy information is compatible with pipewire meta // Check if announced screencopy information is compatible with pipewire meta
if ((cast->pwr_format.format != xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info.format) && if ((cast->pwr_format.format != xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format) &&
cast->pwr_format.format != xdpw_format_pw_strip_alpha(xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info.format))) || cast->pwr_format.format != xdpw_format_pw_strip_alpha(xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format))) ||
cast->pwr_format.size.width != cast->screencopy_frame_info.width || cast->pwr_format.size.width != cast->screencopy_frame_info[cast->buffer_type].width ||
cast->pwr_format.size.height != cast->screencopy_frame_info.height) { cast->pwr_format.size.height != cast->screencopy_frame_info[cast->buffer_type].height) {
logprint(DEBUG, "wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream"); logprint(DEBUG, "wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream");
cast->frame_state = XDPW_FRAME_STATE_RENEG; cast->frame_state = XDPW_FRAME_STATE_RENEG;
xdpw_wlr_frame_finish(cast); xdpw_wlr_frame_finish(cast);
@ -140,8 +140,11 @@ static void wlr_frame_buffer_done(void *data,
assert(cast->current_frame.xdpw_buffer); 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.xdpw_buffer->size != cast->screencopy_frame_info.size || if (( cast->buffer_type == WL_SHM &&
cast->current_frame.xdpw_buffer->stride != cast->screencopy_frame_info.stride) { (cast->current_frame.xdpw_buffer->size != cast->screencopy_frame_info[cast->buffer_type].size ||
cast->current_frame.xdpw_buffer->stride != cast->screencopy_frame_info[cast->buffer_type].stride)) ||
cast->current_frame.xdpw_buffer->width != cast->screencopy_frame_info[cast->buffer_type].width ||
cast->current_frame.xdpw_buffer->height != cast->screencopy_frame_info[cast->buffer_type].height) {
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);