-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use pipewire buffers #141
use pipewire buffers #141
Changes from all commits
2323296
5216916
68d938f
5b8b189
e726cda
444aac9
d7cc408
c822461
5e69384
ff9965d
804920e
c4e98b8
a56312a
32b55d3
91c53a9
ca00e96
f5d62fc
ac2c3fe
b224a75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,27 +7,12 @@ | |
#include <spa/param/video/format-utils.h> | ||
#include <sys/mman.h> | ||
#include <unistd.h> | ||
#include <assert.h> | ||
|
||
#include "wlr_screencast.h" | ||
#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 struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_format format, | ||
uint32_t width, uint32_t height, uint32_t framerate) { | ||
struct spa_pod_frame f[1]; | ||
|
@@ -59,49 +44,12 @@ static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_fo | |
return spa_pod_builder_pop(b, &f[0]); | ||
} | ||
|
||
static void pwr_on_event(void *data, uint64_t expirations) { | ||
static void pwr_handle_stream_process(void *data) { | ||
struct xdpw_screencast_instance *cast = data; | ||
struct pw_buffer *pw_buf; | ||
struct spa_buffer *spa_buf; | ||
struct spa_meta_header *h; | ||
struct spa_data *d; | ||
|
||
logprint(TRACE, "********************"); | ||
logprint(TRACE, "pipewire: event fired"); | ||
|
||
if ((pw_buf = pw_stream_dequeue_buffer(cast->stream)) == NULL) { | ||
logprint(WARN, "pipewire: out of buffers"); | ||
goto out; | ||
} | ||
|
||
spa_buf = pw_buf->buffer; | ||
d = spa_buf->datas; | ||
if ((d[0].data) == NULL) { | ||
logprint(TRACE, "pipewire: data pointer undefined"); | ||
goto out; | ||
} | ||
if ((h = spa_buffer_find_meta_data(spa_buf, SPA_META_Header, sizeof(*h)))) { | ||
h->pts = -1; | ||
h->flags = 0; | ||
h->seq = cast->seq++; | ||
h->dts_offset = 0; | ||
} | ||
|
||
writeFrameData(d[0].data, cast->simple_frame.data, cast->simple_frame.height, | ||
cast->simple_frame.stride, cast->simple_frame.y_invert); | ||
|
||
logprint(TRACE, "pipewire: pointer %p", d[0].data); | ||
logprint(TRACE, "pipewire: size %d", d[0].maxsize); | ||
logprint(TRACE, "pipewire: stride %d", d[0].chunk->stride); | ||
logprint(TRACE, "pipewire: width %d", cast->simple_frame.width); | ||
logprint(TRACE, "pipewire: height %d", cast->simple_frame.height); | ||
logprint(TRACE, "pipewire: y_invert %d", cast->simple_frame.y_invert); | ||
logprint(TRACE, "********************"); | ||
|
||
pw_stream_queue_buffer(cast->stream, pw_buf); | ||
logprint(TRACE, "pipewire: stream process"); | ||
|
||
out: | ||
xdpw_wlr_frame_free(cast); | ||
xdpw_wlr_frame_start(cast); | ||
} | ||
|
||
static void pwr_handle_stream_state_changed(void *data, | ||
|
@@ -116,6 +64,9 @@ static void pwr_handle_stream_state_changed(void *data, | |
switch (state) { | ||
case PW_STREAM_STATE_STREAMING: | ||
cast->pwr_stream_state = true; | ||
if (!cast->wlr_frame) { | ||
xdpw_wlr_frame_start(cast); | ||
} | ||
break; | ||
default: | ||
cast->pwr_stream_state = false; | ||
|
@@ -144,8 +95,8 @@ static void pwr_handle_stream_param_changed(void *data, uint32_t id, | |
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, | ||
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(XDPW_PWR_BUFFERS, 1, 32), | ||
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), | ||
SPA_PARAM_BUFFERS_size, SPA_POD_Int(cast->simple_frame.size), | ||
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(cast->simple_frame.stride), | ||
SPA_PARAM_BUFFERS_size, SPA_POD_Int(cast->screencopy_frame.size), | ||
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(cast->screencopy_frame.stride), | ||
SPA_PARAM_BUFFERS_align, SPA_POD_Int(XDPW_PWR_ALIGN), | ||
SPA_PARAM_BUFFERS_dataType,SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_MemFd)); | ||
|
||
|
@@ -177,13 +128,14 @@ 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->simple_frame.size; | ||
d[0].maxsize = cast->screencopy_frame.size; | ||
d[0].mapoffset = 0; | ||
d[0].chunk->size = cast->simple_frame.size; | ||
d[0].chunk->stride = cast->simple_frame.stride; | ||
d[0].chunk->size = cast->screencopy_frame.size; | ||
d[0].chunk->stride = cast->screencopy_frame.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"); | ||
|
@@ -199,23 +151,26 @@ static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) { | |
return; | ||
} | ||
|
||
// mmap buffer, so we can use the data_ptr in 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"); | ||
cast->err = 1; | ||
return; | ||
} | ||
// create wl_buffer | ||
buffer->user_data = import_wl_shm_buffer(cast, d[0].fd, cast->screencopy_frame.format, | ||
cast->screencopy_frame.width, cast->screencopy_frame.height, cast->screencopy_frame.stride); | ||
} | ||
} | ||
|
||
static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer) { | ||
struct xdpw_screencast_instance *cast = data; | ||
|
||
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; | ||
} | ||
switch (d[0].type) { | ||
case SPA_DATA_MemFd: | ||
munmap(d[0].data, d[0].maxsize); | ||
wl_buffer_destroy(buffer->user_data); | ||
close(d[0].fd); | ||
break; | ||
default: | ||
|
@@ -229,8 +184,80 @@ static const struct pw_stream_events pwr_stream_events = { | |
.param_changed = pwr_handle_stream_param_changed, | ||
.add_buffer = pwr_handle_stream_add_buffer, | ||
.remove_buffer = pwr_handle_stream_remove_buffer, | ||
.process = pwr_handle_stream_process, | ||
}; | ||
|
||
void xdpw_pwr_trigger_process(struct xdpw_screencast_instance *cast) { | ||
pw_stream_trigger_process(cast->stream); | ||
} | ||
|
||
bool xdpw_pwr_is_driving(struct xdpw_screencast_instance *cast) { | ||
return pw_stream_is_driving(cast->stream); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need these trivial wrappers? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need a wrapper for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or we put the whole fps_limit logic into the |
||
|
||
columbarius marked this conversation as resolved.
Show resolved
Hide resolved
|
||
void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast) { | ||
logprint(TRACE, "pipewire: dequeueing buffer"); | ||
|
||
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; | ||
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; | ||
} | ||
|
||
void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) { | ||
logprint(TRACE, "pipewire: exporting buffer"); | ||
|
||
struct pw_buffer *pw_buf = cast->current_frame.current_pw_buffer; | ||
bool buffer_corrupt = cast->frame_state != XDPW_FRAME_STATE_SUCCESS; | ||
|
||
assert(pw_buf); | ||
|
||
struct spa_buffer *spa_buf = pw_buf->buffer; | ||
struct spa_data *d = spa_buf->datas; | ||
|
||
if (cast->current_frame.y_invert) { | ||
//TODO: Flip buffer or set stride negative | ||
buffer_corrupt = true; | ||
cast->err = 1; | ||
} | ||
|
||
struct spa_meta_header *h; | ||
if ((h = spa_buffer_find_meta_data(spa_buf, SPA_META_Header, sizeof(*h)))) { | ||
h->pts = -1; | ||
h->flags = buffer_corrupt ? SPA_META_HEADER_FLAG_CORRUPTED : 0; | ||
h->seq = cast->seq++; | ||
h->dts_offset = 0; | ||
} | ||
|
||
if (buffer_corrupt) { | ||
d[0].chunk->flags = SPA_CHUNK_FLAG_CORRUPTED; | ||
columbarius marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
d[0].chunk->flags = SPA_CHUNK_FLAG_NONE; | ||
} | ||
|
||
logprint(TRACE, "********************"); | ||
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.width); | ||
logprint(TRACE, "pipewire: height %d", cast->screencopy_frame.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; | ||
} | ||
|
||
void pwr_update_stream_param(struct xdpw_screencast_instance *cast) { | ||
logprint(TRACE, "pipewire: stream update parameters"); | ||
struct pw_stream *stream = cast->stream; | ||
|
@@ -239,10 +266,10 @@ void pwr_update_stream_param(struct xdpw_screencast_instance *cast) { | |
SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); | ||
const struct spa_pod *params[1]; | ||
|
||
enum spa_video_format format = xdpw_format_pw_from_wl_shm(cast->simple_frame.format); | ||
enum spa_video_format format = xdpw_format_pw_from_wl_shm(cast->screencopy_frame.format); | ||
|
||
params[0] = build_format(&b, format, | ||
cast->simple_frame.width, cast->simple_frame.height, cast->framerate); | ||
cast->screencopy_frame.width, cast->screencopy_frame.height, cast->framerate); | ||
|
||
pw_stream_update_params(stream, params, 1); | ||
} | ||
|
@@ -269,15 +296,10 @@ void xdpw_pwr_stream_create(struct xdpw_screencast_instance *cast) { | |
} | ||
cast->pwr_stream_state = false; | ||
|
||
/* make an event to signal frame ready */ | ||
cast->event = | ||
pw_loop_add_event(state->pw_loop, pwr_on_event, cast); | ||
logprint(DEBUG, "pipewire: registered event %p", cast->event); | ||
|
||
enum spa_video_format format = xdpw_format_pw_from_wl_shm(cast->simple_frame.format); | ||
enum spa_video_format format = xdpw_format_pw_from_wl_shm(cast->screencopy_frame.format); | ||
|
||
const struct spa_pod *param = build_format(&b, format, | ||
cast->simple_frame.width, cast->simple_frame.height, cast->framerate); | ||
cast->screencopy_frame.width, cast->screencopy_frame.height, cast->framerate); | ||
|
||
pw_stream_add_listener(cast->stream, &cast->stream_listener, | ||
&pwr_stream_events, cast); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked every commit again and found a race condition only in
screencast: only restart wlroots loop if stream is active
when PipeWire sends a
state_changed
changing the state toPW_STATE_STREAMING
in between thebuffer_done
and theready
/failed
events of the screencopy loop. This did trigger a new screencopy loop entrance while the old wasn't finished and triggered an assertion inxdpw_pwr_dequeue_buffer
.Checking if NO screencopy loop is currently running via
cast->wlr_frame == NULL
solved this issue.