Skip to content

Commit

Permalink
screencast: pipewire restructure to import buffers directly from pipe…
Browse files Browse the repository at this point in the history
…wire

This needs to be split up into parts, but it's all somehow connected. lets find some bugs first
  • Loading branch information
columbarius committed May 27, 2021
1 parent 4a6edc3 commit 8ffcef5
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 138 deletions.
2 changes: 1 addition & 1 deletion include/pipewire_screencast.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#define XDPW_PWR_BUFFERS 1
#define XDPW_PWR_ALIGN 16

int xdpw_pwr_import_buffer(struct xdpw_screencast_instance *cast);
void xdpw_pwr_import_buffer(struct xdpw_screencast_instance *cast);
void xdpw_pwr_export_buffer(struct xdpw_screencast_instance *cast);
void pwr_update_stream_param(struct xdpw_screencast_instance *cast);
void xdpw_pwr_stream_create(struct xdpw_screencast_instance *cast);
Expand Down
1 change: 1 addition & 0 deletions include/screencast_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions include/wlr_screencast.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
46 changes: 13 additions & 33 deletions src/screencast/pipewire_screencast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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:
Expand All @@ -190,18 +172,24 @@ static const struct pw_stream_events pwr_stream_events = {
.remove_buffer = pwr_handle_stream_remove_buffer,
};

int xdpw_pwr_import_buffer(struct xdpw_screencast_instance *cast) {
void xdpw_pwr_import_buffer(struct xdpw_screencast_instance *cast) {
struct pw_buffer *pw_buf;

logprint(TRACE, "pipewire: importing buffer");

if ((pw_buf = pw_stream_dequeue_buffer(cast->stream)) == NULL) {
logprint(WARN, "pipewire: out of buffers");
return;
}

cast->current_pw_buffer = pw_buf;
struct spa_buffer *spa_buf;
struct spa_data *d;

spa_buf = pw_buf->buffer;
d = spa_buf->datas;

return pw_buf != NULL;
cast->simple_frame.buffer = d[0].data;
}

void xdpw_pwr_export_buffer(struct xdpw_screencast_instance *cast) {
Expand All @@ -227,13 +215,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);
Expand All @@ -244,7 +225,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;
Expand Down
172 changes: 68 additions & 104 deletions src/screencast/wlr_screencast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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,
Expand All @@ -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");
Expand All @@ -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);
}
}

Expand All @@ -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,
Expand All @@ -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;
Expand All @@ -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,
Expand Down

0 comments on commit 8ffcef5

Please sign in to comment.