2020-01-24 23:31:01 +01:00
|
|
|
#include "pipewire_screencast.h"
|
|
|
|
|
|
|
|
static inline void init_type(struct pwr_type *type, struct pw_type *map) {
|
|
|
|
pw_type_get(map, SPA_TYPE__MediaType, &type->media_type);
|
|
|
|
pw_type_get(map, SPA_TYPE__MediaSubtype, &type->media_subtype);
|
|
|
|
pw_type_get(map, SPA_TYPE_FORMAT__Video, &type->format_video);
|
|
|
|
pw_type_get(map, SPA_TYPE__VideoFormat, &type->video_format);
|
|
|
|
pw_type_get(map, SPA_TYPE_META__Cursor, &type->meta_cursor);
|
|
|
|
};
|
|
|
|
|
|
|
|
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 void pwr_on_event(void *data, uint64_t expirations) {
|
2020-03-10 20:46:37 +01:00
|
|
|
struct xdpw_state *state = data;
|
|
|
|
struct screencast_context *ctx = &state->screencast;
|
2020-01-24 23:31:01 +01:00
|
|
|
struct pw_buffer *pw_buf;
|
|
|
|
struct spa_buffer *spa_buf;
|
|
|
|
struct spa_meta_header *h;
|
|
|
|
struct spa_data *d;
|
|
|
|
|
2020-02-20 20:59:46 +01:00
|
|
|
logprint(TRACE, "********************");
|
|
|
|
logprint(TRACE, "pipewire: event fired");
|
2020-01-24 23:31:01 +01:00
|
|
|
|
|
|
|
if ((pw_buf = pw_stream_dequeue_buffer(ctx->stream)) == NULL) {
|
2020-02-20 20:59:46 +01:00
|
|
|
logprint(WARN, "pipewire: out of buffers");
|
2020-01-24 23:31:01 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spa_buf = pw_buf->buffer;
|
|
|
|
d = spa_buf->datas;
|
2020-03-10 20:46:37 +01:00
|
|
|
if ((d[0].data) == NULL) {
|
|
|
|
logprint(TRACE, "pipewire: data pointer undefined");
|
2020-01-24 23:31:01 +01:00
|
|
|
return;
|
2020-03-10 20:46:37 +01:00
|
|
|
}
|
2020-01-24 23:31:01 +01:00
|
|
|
if ((h = spa_buffer_find_meta(spa_buf, ctx->t->meta.Header))) {
|
|
|
|
h->pts = -1;
|
|
|
|
h->flags = 0;
|
|
|
|
h->seq = ctx->seq++;
|
|
|
|
h->dts_offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
d[0].type = ctx->t->data.MemPtr;
|
|
|
|
d[0].maxsize = ctx->simple_frame.size;
|
|
|
|
d[0].mapoffset = 0;
|
|
|
|
d[0].chunk->size = ctx->simple_frame.size;
|
|
|
|
d[0].chunk->stride = ctx->simple_frame.stride;
|
|
|
|
d[0].chunk->offset = 0;
|
|
|
|
d[0].flags = 0;
|
|
|
|
d[0].fd = -1;
|
|
|
|
|
|
|
|
writeFrameData(d[0].data, ctx->simple_frame.data, ctx->simple_frame.height,
|
|
|
|
ctx->simple_frame.stride, ctx->simple_frame.y_invert);
|
|
|
|
|
2020-02-20 20:59:46 +01:00
|
|
|
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", ctx->simple_frame.width);
|
|
|
|
logprint(TRACE, "pipewire: height %d", ctx->simple_frame.height);
|
|
|
|
logprint(TRACE, "pipewire: y_invert %d", ctx->simple_frame.y_invert);
|
|
|
|
logprint(TRACE, "********************");
|
2020-01-24 23:31:01 +01:00
|
|
|
|
|
|
|
pw_stream_queue_buffer(ctx->stream, pw_buf);
|
|
|
|
|
2020-03-10 20:46:37 +01:00
|
|
|
wlr_frame_free(state);
|
|
|
|
|
2020-01-24 23:31:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pwr_handle_stream_state_changed(void *data,
|
|
|
|
enum pw_stream_state old,
|
|
|
|
enum pw_stream_state state,
|
|
|
|
const char *error) {
|
|
|
|
struct screencast_context *ctx = data;
|
|
|
|
ctx->node_id = pw_stream_get_node_id(ctx->stream);
|
|
|
|
|
2020-02-20 20:59:46 +01:00
|
|
|
logprint(INFO, "pipewire: stream state changed to \"%s\"", pw_stream_state_as_string(state));
|
|
|
|
logprint(INFO, "pipewire: node id is %d", ctx->node_id);
|
2020-01-24 23:31:01 +01:00
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case PW_STREAM_STATE_STREAMING:
|
|
|
|
ctx->stream_state = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ctx->stream_state = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pwr_handle_stream_format_changed(void *data,
|
|
|
|
const struct spa_pod *format) {
|
|
|
|
struct screencast_context *ctx = data;
|
|
|
|
struct pw_stream *stream = ctx->stream;
|
|
|
|
struct pw_type *t = ctx->t;
|
|
|
|
uint8_t params_buffer[1024];
|
|
|
|
struct spa_pod_builder b =
|
|
|
|
SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
|
|
|
const struct spa_pod *params[2];
|
|
|
|
|
|
|
|
if (format == NULL) {
|
|
|
|
pw_stream_finish_format(stream, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
spa_format_video_raw_parse(format, &ctx->pwr_format, &ctx->type.format_video);
|
|
|
|
|
|
|
|
params[0] = spa_pod_builder_object(
|
|
|
|
&b, t->param.idBuffers, t->param_buffers.Buffers, ":",
|
|
|
|
t->param_buffers.size, "i", ctx->simple_frame.size, ":",
|
|
|
|
t->param_buffers.stride, "i", ctx->simple_frame.stride, ":",
|
|
|
|
t->param_buffers.buffers, "iru", BUFFERS, SPA_POD_PROP_MIN_MAX(1, 32),
|
|
|
|
":", t->param_buffers.align, "i", ALIGN);
|
|
|
|
|
|
|
|
params[1] = spa_pod_builder_object(&b, t->param.idMeta, t->param_meta.Meta,
|
|
|
|
":", t->param_meta.type, "I",
|
|
|
|
t->meta.Header, ":", t->param_meta.size,
|
|
|
|
"i", sizeof(struct spa_meta_header));
|
|
|
|
|
|
|
|
pw_stream_finish_format(stream, 0, params, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct pw_stream_events pwr_stream_events = {
|
|
|
|
PW_VERSION_STREAM_EVENTS,
|
|
|
|
.state_changed = pwr_handle_stream_state_changed,
|
|
|
|
.format_changed = pwr_handle_stream_format_changed,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void pwr_handle_state_changed(void *data, enum pw_remote_state old,
|
2020-03-10 20:46:37 +01:00
|
|
|
enum pw_remote_state pwr_remote_state,
|
2020-01-24 23:31:01 +01:00
|
|
|
const char *error) {
|
2020-03-10 20:46:37 +01:00
|
|
|
struct xdpw_state *state = data;
|
|
|
|
struct screencast_context *ctx = &state->screencast;
|
2020-01-24 23:31:01 +01:00
|
|
|
struct pw_remote *remote = ctx->remote;
|
|
|
|
|
2020-03-10 20:46:37 +01:00
|
|
|
switch (pwr_remote_state) {
|
2020-01-24 23:31:01 +01:00
|
|
|
case PW_REMOTE_STATE_ERROR:
|
2020-02-20 20:59:46 +01:00
|
|
|
logprint(INFO, "pipewire: remote state changed to \"%s\"",
|
2020-03-10 20:46:37 +01:00
|
|
|
pw_remote_state_as_string(pwr_remote_state));
|
2020-02-20 20:59:46 +01:00
|
|
|
logprint(ERROR, "pipewire: remote error: %s", error);
|
2020-03-10 20:46:37 +01:00
|
|
|
pw_loop_leave(state->pw_loop);
|
|
|
|
pw_loop_destroy(state->pw_loop);
|
|
|
|
ctx->err = true;
|
2020-01-24 23:31:01 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PW_REMOTE_STATE_CONNECTED: {
|
|
|
|
const struct spa_pod *params[1];
|
|
|
|
uint8_t buffer[1024];
|
|
|
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
|
|
|
|
2020-02-20 20:59:46 +01:00
|
|
|
logprint(INFO, "pipewire: remote state changed to \"%s\"",
|
2020-03-10 20:46:37 +01:00
|
|
|
pw_remote_state_as_string(pwr_remote_state));
|
2020-01-24 23:31:01 +01:00
|
|
|
|
|
|
|
ctx->stream = pw_stream_new(
|
|
|
|
remote, "wlr_screeencopy",
|
|
|
|
pw_properties_new("media.class", "Video/Source", PW_NODE_PROP_MEDIA,
|
|
|
|
"Video", PW_NODE_PROP_CATEGORY, "Source",
|
|
|
|
PW_NODE_PROP_ROLE, "Screen", NULL));
|
|
|
|
|
|
|
|
params[0] = spa_pod_builder_object(
|
|
|
|
&b, ctx->t->param.idEnumFormat, ctx->t->spa_format, "I",
|
|
|
|
ctx->type.media_type.video, "I", ctx->type.media_subtype.raw, ":",
|
|
|
|
ctx->type.format_video.format, "I", pipewire_from_wl_shm(ctx), ":",
|
|
|
|
ctx->type.format_video.size, "Rru",
|
|
|
|
&SPA_RECTANGLE(ctx->simple_frame.width, ctx->simple_frame.height),
|
|
|
|
SPA_POD_PROP_MIN_MAX(&SPA_RECTANGLE(1, 1), &SPA_RECTANGLE(4096, 4096)),
|
|
|
|
":", ctx->type.format_video.framerate, "F",
|
2020-02-19 17:48:36 +01:00
|
|
|
// specify variable framerate
|
|
|
|
&SPA_FRACTION(0, 1),
|
|
|
|
":", ctx->type.format_video.max_framerate, "F",
|
|
|
|
// with a maximum at the wlroots specified hardware framerate
|
2020-01-24 23:31:01 +01:00
|
|
|
&SPA_FRACTION(ctx->framerate, 1));
|
|
|
|
|
|
|
|
pw_stream_add_listener(ctx->stream, &ctx->stream_listener,
|
|
|
|
&pwr_stream_events, ctx);
|
|
|
|
|
|
|
|
pw_stream_connect(ctx->stream, PW_DIRECTION_OUTPUT, NULL,
|
|
|
|
PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS,
|
|
|
|
params, 1);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2020-02-20 20:59:46 +01:00
|
|
|
logprint(INFO, "pipewire: remote state changed to \"%s\"",
|
2020-03-10 20:46:37 +01:00
|
|
|
pw_remote_state_as_string(pwr_remote_state));
|
2020-01-24 23:31:01 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct pw_remote_events pwr_remote_events = {
|
|
|
|
PW_VERSION_REMOTE_EVENTS,
|
|
|
|
.state_changed = pwr_handle_state_changed,
|
|
|
|
};
|
|
|
|
|
2020-03-10 20:46:37 +01:00
|
|
|
void *pwr_start(struct xdpw_state *state) {
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2020-03-10 20:46:37 +01:00
|
|
|
struct screencast_context *ctx = &state->screencast;
|
|
|
|
pw_loop_enter(state->pw_loop);
|
2020-03-06 20:41:53 +01:00
|
|
|
|
|
|
|
/* create a core, a remote, and initialize types */
|
2020-03-10 20:46:37 +01:00
|
|
|
ctx->core = pw_core_new(state->pw_loop, NULL);
|
2020-01-24 23:31:01 +01:00
|
|
|
ctx->t = pw_core_get_type(ctx->core);
|
|
|
|
ctx->remote = pw_remote_new(ctx->core, NULL, 0);
|
|
|
|
|
|
|
|
init_type(&ctx->type, ctx->t);
|
|
|
|
|
|
|
|
/* make an event to signal frame ready */
|
|
|
|
ctx->event =
|
2020-03-10 20:46:37 +01:00
|
|
|
pw_loop_add_event(state->pw_loop, pwr_on_event, state);
|
2020-01-24 23:31:01 +01:00
|
|
|
|
|
|
|
pw_remote_add_listener(ctx->remote, &ctx->remote_listener, &pwr_remote_events,
|
2020-03-10 20:46:37 +01:00
|
|
|
state);
|
2020-01-24 23:31:01 +01:00
|
|
|
pw_remote_connect(ctx->remote);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|