From 4b554493c5fe8bbfc2f14bdf1d1576e174dc48dc Mon Sep 17 00:00:00 2001 From: columbarius Date: Fri, 28 May 2021 01:27:08 +0200 Subject: [PATCH] screencast: pipewire restructure to import buffers directly from pipewire This needs to be split up into parts, but it's all somehow connected. lets find some bugs first --- include/screencast_common.h | 1 + include/wlr_screencast.h | 1 + src/screencast/pipewire_screencast.c | 43 ++----- src/screencast/wlr_screencast.c | 172 +++++++++++---------------- 4 files changed, 82 insertions(+), 135 deletions(-) diff --git a/include/screencast_common.h b/include/screencast_common.h index b94fe541..c0265ada 100644 --- a/include/screencast_common.h +++ b/include/screencast_common.h @@ -102,6 +102,7 @@ struct xdpw_screencast_instance { bool with_cursor; int err; bool quit; + bool changed; // fps limit struct fps_limit_state fps_limit; diff --git a/include/wlr_screencast.h b/include/wlr_screencast.h index a1585bcc..591d92c6 100644 --- a/include/wlr_screencast.h +++ b/include/wlr_screencast.h @@ -24,6 +24,7 @@ struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx struct wl_output *out, uint32_t id); struct xdpw_wlr_output *xdpw_wlr_output_chooser(struct xdpw_screencast_context *ctx); +void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast); void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast); void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast); diff --git a/src/screencast/pipewire_screencast.c b/src/screencast/pipewire_screencast.c index 4be80723..9cf0989e 100644 --- a/src/screencast/pipewire_screencast.c +++ b/src/screencast/pipewire_screencast.c @@ -12,22 +12,6 @@ #include "xdpw.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 inline struct spa_pod *build_format(struct spa_pod_builder *b, uint32_t format, uint32_t width, uint32_t height, uint32_t framerate) { @@ -78,6 +62,7 @@ static void pwr_handle_stream_state_changed(void *data, switch (state) { case PW_STREAM_STATE_STREAMING: cast->pwr_stream_state = true; + xdpw_wlr_frame_start(cast); break; default: cast->pwr_stream_state = false; @@ -157,12 +142,9 @@ static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) { return; } - // mmap buffer, so we can use the data_ptr int on_process - d[0].data = mmap(NULL, d[0].maxsize, PROT_READ | PROT_WRITE, MAP_SHARED, d[0].fd, d[0].mapoffset); - if (d[0].data == MAP_FAILED) { - logprint(ERROR, "pipewire: unable to mmap memory"); - return; - } + // create wl_buffer + d[0].data = import_shm_buffer(cast, d[0].fd, cast->simple_frame.format, + cast->simple_frame.width, cast->simple_frame.height, cast->simple_frame.stride); } } @@ -174,7 +156,7 @@ static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer d = buffer->buffer->datas; switch (d[0].type) { case SPA_DATA_MemFd: - munmap(d[0].data, d[0].maxsize); + wl_buffer_destroy(d[0].data); close(d[0].fd); break; default: @@ -203,6 +185,13 @@ void xdpw_pwr_import_buffer(struct xdpw_screencast_instance *cast) { } cast->current_pw_buffer = pw_buf; + struct spa_buffer *spa_buf; + struct spa_data *d; + + spa_buf = pw_buf->buffer; + d = spa_buf->datas; + + cast->simple_frame.buffer = d[0].data; } void xdpw_pwr_export_buffer(struct xdpw_screencast_instance *cast) { @@ -228,13 +217,6 @@ void xdpw_pwr_export_buffer(struct xdpw_screencast_instance *cast) { h->seq = cast->seq++; h->dts_offset = 0; } - if ((d[0].data) == NULL) { - logprint(TRACE, "pipewire: data pointer undefined"); - goto queue; - } - - writeFrameData(d[0].data, cast->simple_frame.data, cast->simple_frame.height, - cast->simple_frame.stride, cast->simple_frame.y_invert); logprint(TRACE, "********************"); logprint(TRACE, "pipewire: pointer %p", d[0].data); @@ -245,7 +227,6 @@ void xdpw_pwr_export_buffer(struct xdpw_screencast_instance *cast) { logprint(TRACE, "pipewire: y_invert %d", cast->simple_frame.y_invert); logprint(TRACE, "********************"); -queue: pw_stream_queue_buffer(cast->stream, pw_buf); cast->current_pw_buffer = NULL; diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c index 53f1eff4..64b0db5e 100644 --- a/src/screencast/wlr_screencast.c +++ b/src/screencast/wlr_screencast.c @@ -20,28 +20,10 @@ #include "logger.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->simple_frame.data != NULL) { - munmap(cast->simple_frame.data, cast->simple_frame.size); - cast->simple_frame.data = NULL; - } - - if (cast->simple_frame.buffer != NULL) { - wl_buffer_destroy(cast->simple_frame.buffer); - cast->simple_frame.buffer = NULL; - } -} - void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast) { zwlr_screencopy_frame_v1_destroy(cast->wlr_frame); cast->wlr_frame = NULL; - if (cast->quit || cast->err) { - wlr_frame_buffer_destroy(cast); - logprint(TRACE, "xdpw: simple_frame buffer destroyed"); - } + logprint(TRACE, "wlroots: frame destroyed"); if (cast->quit || cast->err) { @@ -52,6 +34,12 @@ void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast) { return ; } + if (cast->pwr_stream_state) { + xdpw_wlr_frame_start(cast); + } +} + +void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast) { uint64_t delay_ns = fps_limit_measure_end(&cast->fps_limit, cast->ctx->state->config->screencast_conf.max_fps); if (delay_ns > 0) { xdpw_add_timer(cast->ctx->state, delay_ns, @@ -61,44 +49,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_buffer_chparam(struct xdpw_screencast_instance *cast, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { logprint(DEBUG, "wlroots: reset buffer"); @@ -107,11 +57,33 @@ static void wlr_frame_buffer_chparam(struct xdpw_screencast_instance *cast, cast->simple_frame.stride = stride; cast->simple_frame.size = stride * height; cast->simple_frame.format = format; - wlr_frame_buffer_destroy(cast); +} +// declare function, because we have to call it manually for screencopy_manager < 3 +static void wlr_frame_buffer_done(void *data, + struct zwlr_screencopy_frame_v1 *frame); + +static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame, + uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { + struct xdpw_screencast_instance *cast = data; + + logprint(TRACE, "wlroots: buffer event handler"); + cast->wlr_frame = frame; if (cast->pwr_stream_state) { - logprint(DEBUG, "wlroots: request pipewire param change"); - pwr_update_stream_param(cast); + xdpw_pwr_import_buffer(cast); + } + if (cast->simple_frame.width != width || + cast->simple_frame.height != height || + cast->simple_frame.stride != stride || + cast->simple_frame.format != format) { + logprint(TRACE, "wlroots: buffer properties changed"); + wlr_frame_buffer_chparam(cast, format, width, height, stride); + cast->changed = true; + return; + } + + if (zwlr_screencopy_manager_v1_get_version(cast->ctx->screencopy_manager) < 3) { + wlr_frame_buffer_done(cast,frame); } } @@ -127,49 +99,41 @@ static void wlr_frame_buffer_done(void *data, logprint(TRACE, "wlroots: buffer_done event handler"); - if (!cast->quit && !cast->err && cast->pwr_stream_state) { - if(!xdpw_pwr_import_buffer(cast)) { - xdpw_wlr_frame_free(cast); - return; + if (cast->changed) { + if (cast->current_pw_buffer) { + xdpw_pwr_export_buffer(cast); + } + if (cast->pwr_stream_state) { + pwr_update_stream_param(cast); } + cast->changed = false; + xdpw_wlr_frame_free(cast); + return; } - zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->simple_frame.buffer); - logprint(TRACE, "wlroots: frame copied"); - - fps_limit_measure_start(&cast->fps_limit, cast->ctx->state->config->screencast_conf.max_fps); -} - -static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame, - uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { - struct xdpw_screencast_instance *cast = data; - - logprint(TRACE, "wlroots: buffer event handler"); - cast->wlr_frame = frame; - if (cast->simple_frame.width != width || - cast->simple_frame.height != height || - cast->simple_frame.stride != stride || - cast->simple_frame.format != format) { - logprint(TRACE, "wlroots: buffer properties changed"); - wlr_frame_buffer_chparam(cast, format, width, height, stride); + if (!cast->pwr_stream_state) { + if (cast->current_pw_buffer) { + xdpw_pwr_export_buffer(cast); + } + xdpw_wlr_frame_free(cast); + return; } if (cast->simple_frame.buffer == NULL) { - logprint(DEBUG, "wlroots: create shm buffer"); - cast->simple_frame.buffer = create_shm_buffer(cast, format, width, height, - stride, &cast->simple_frame.data); + logprint(DEBUG, "wlroots: failed to import buffer"); + if (cast->current_pw_buffer) { + xdpw_pwr_export_buffer(cast); + } + xdpw_wlr_frame_free(cast); + return; } else { logprint(TRACE,"wlroots: shm buffer exists"); } - if (cast->simple_frame.buffer == NULL) { - logprint(ERROR, "wlroots: failed to create buffer"); - abort(); - } + zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->simple_frame.buffer); + logprint(TRACE, "wlroots: frame copied"); - if (zwlr_screencopy_manager_v1_get_version(cast->ctx->screencopy_manager) < 3) { - wlr_frame_buffer_done(cast,frame); - } + fps_limit_measure_start(&cast->fps_limit, cast->ctx->state->config->screencast_conf.max_fps); } static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, @@ -180,6 +144,18 @@ static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, cast->simple_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; } +static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame, + uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + struct xdpw_screencast_instance *cast = data; + + logprint(TRACE, "wlroots: damage event handler"); + + cast->simple_frame.damage.x = x; + cast->simple_frame.damage.y = y; + cast->simple_frame.damage.width = width; + cast->simple_frame.damage.height = height; +} + static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { struct xdpw_screencast_instance *cast = data; @@ -206,18 +182,6 @@ static void wlr_frame_failed(void *data, xdpw_wlr_frame_free(cast); } -static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame, - uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - struct xdpw_screencast_instance *cast = data; - - logprint(TRACE, "wlroots: damage event handler"); - - cast->simple_frame.damage.x = x; - cast->simple_frame.damage.y = y; - cast->simple_frame.damage.width = width; - cast->simple_frame.damage.height = height; -} - static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = { .buffer = wlr_frame_buffer, .buffer_done = wlr_frame_buffer_done,