2020-01-24 23:31:01 +01:00
|
|
|
#include "pipewire_screencast.h"
|
|
|
|
|
2020-04-16 10:21:55 +02:00
|
|
|
#include <pipewire/pipewire.h>
|
|
|
|
#include <spa/utils/result.h>
|
|
|
|
#include <spa/param/props.h>
|
|
|
|
#include <spa/param/format-utils.h>
|
|
|
|
#include <spa/param/video/format-utils.h>
|
2020-10-26 16:22:08 +01:00
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <unistd.h>
|
2021-05-27 18:27:03 +02:00
|
|
|
#include <assert.h>
|
2021-06-17 23:27:07 +02:00
|
|
|
#include <libdrm/drm_fourcc.h>
|
2020-04-16 10:21:55 +02:00
|
|
|
|
|
|
|
#include "wlr_screencast.h"
|
|
|
|
#include "xdpw.h"
|
|
|
|
#include "logger.h"
|
|
|
|
|
2021-08-08 03:31:49 +02:00
|
|
|
static struct spa_pod *build_buffer(struct spa_pod_builder *b, uint32_t blocks, uint32_t size,
|
|
|
|
uint32_t stride, uint32_t datatype) {
|
|
|
|
assert(blocks > 0);
|
|
|
|
assert(datatype > 0);
|
|
|
|
struct spa_pod_frame f[1];
|
|
|
|
|
|
|
|
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers);
|
|
|
|
spa_pod_builder_add(b, SPA_PARAM_BUFFERS_buffers,
|
|
|
|
SPA_POD_CHOICE_RANGE_Int(XDPW_PWR_BUFFERS, XDPW_PWR_BUFFERS_MIN, 32), 0);
|
|
|
|
spa_pod_builder_add(b, SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(blocks), 0);
|
|
|
|
if (size > 0) {
|
|
|
|
spa_pod_builder_add(b, SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), 0);
|
|
|
|
}
|
|
|
|
if (stride > 0) {
|
|
|
|
spa_pod_builder_add(b, SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), 0);
|
|
|
|
}
|
|
|
|
spa_pod_builder_add(b, SPA_PARAM_BUFFERS_align, SPA_POD_Int(XDPW_PWR_ALIGN), 0);
|
|
|
|
spa_pod_builder_add(b, SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(datatype), 0);
|
|
|
|
return spa_pod_builder_pop(b, &f[0]);
|
|
|
|
}
|
|
|
|
|
2021-08-08 05:09:04 +02:00
|
|
|
static struct spa_pod *fixate_format(struct spa_pod_builder *b, enum spa_video_format format,
|
|
|
|
uint32_t width, uint32_t height, uint32_t framerate, uint64_t *modifier)
|
|
|
|
{
|
|
|
|
struct spa_pod_frame f[1];
|
|
|
|
|
|
|
|
enum spa_video_format format_without_alpha = xdpw_format_pw_strip_alpha(format);
|
|
|
|
|
|
|
|
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
|
|
|
|
/* format */
|
|
|
|
if (modifier || format_without_alpha == SPA_VIDEO_FORMAT_UNKNOWN) {
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
|
|
|
} else {
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format,
|
|
|
|
SPA_POD_CHOICE_ENUM_Id(3, format, format, format_without_alpha), 0);
|
|
|
|
}
|
|
|
|
/* modifiers */
|
|
|
|
if (modifier) {
|
|
|
|
// implicit modifier
|
|
|
|
spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY);
|
|
|
|
spa_pod_builder_long(b, *modifier);
|
|
|
|
}
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_size,
|
|
|
|
SPA_POD_Rectangle(&SPA_RECTANGLE(width, height)),
|
|
|
|
0);
|
|
|
|
// variable framerate
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_framerate,
|
|
|
|
SPA_POD_Fraction(&SPA_FRACTION(0, 1)), 0);
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_maxFramerate,
|
|
|
|
SPA_POD_CHOICE_RANGE_Fraction(
|
|
|
|
&SPA_FRACTION(framerate, 1),
|
|
|
|
&SPA_FRACTION(1, 1),
|
|
|
|
&SPA_FRACTION(framerate, 1)),
|
|
|
|
0);
|
|
|
|
return spa_pod_builder_pop(b, &f[0]);
|
|
|
|
}
|
|
|
|
|
2021-05-26 00:54:30 +02:00
|
|
|
static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_format format,
|
2021-06-17 23:27:07 +02:00
|
|
|
uint32_t width, uint32_t height, uint32_t framerate,
|
|
|
|
uint64_t *modifiers, int modifier_count) {
|
|
|
|
struct spa_pod_frame f[2];
|
|
|
|
int i, c;
|
2021-05-26 00:54:30 +02:00
|
|
|
|
|
|
|
enum spa_video_format format_without_alpha = xdpw_format_pw_strip_alpha(format);
|
|
|
|
|
|
|
|
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
2021-05-25 23:58:51 +02:00
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
|
2021-05-26 00:54:30 +02:00
|
|
|
/* format */
|
2021-06-17 23:27:07 +02:00
|
|
|
if (modifier_count > 0 || format_without_alpha == SPA_VIDEO_FORMAT_UNKNOWN) {
|
|
|
|
// modifiers are defined only in combinations with their format
|
|
|
|
// we should not announce the format without alpha
|
2021-05-26 00:54:30 +02:00
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
2021-05-25 23:58:51 +02:00
|
|
|
} else {
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format,
|
2021-05-26 00:54:30 +02:00
|
|
|
SPA_POD_CHOICE_ENUM_Id(3, format, format, format_without_alpha), 0);
|
2021-05-25 23:58:51 +02:00
|
|
|
}
|
2021-06-17 23:27:07 +02:00
|
|
|
/* modifiers */
|
|
|
|
if (modifier_count == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) {
|
|
|
|
// we only support implicit modifiers, use shortpath to skip fixation phase
|
|
|
|
spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY);
|
|
|
|
spa_pod_builder_long(b, modifiers[0]);
|
|
|
|
} else if (modifier_count > 0) {
|
|
|
|
// build an enumeration of modifiers
|
|
|
|
spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
|
|
|
|
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Enum, 0);
|
|
|
|
// modifiers from the array
|
|
|
|
for (i = 0, c = 0; i < modifier_count; i++) {
|
|
|
|
spa_pod_builder_long(b, modifiers[i]);
|
|
|
|
if (c++ == 0)
|
|
|
|
spa_pod_builder_long(b, modifiers[i]);
|
|
|
|
}
|
|
|
|
spa_pod_builder_pop(b, &f[1]);
|
|
|
|
}
|
2021-05-25 23:58:51 +02:00
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_size,
|
2021-05-26 01:10:04 +02:00
|
|
|
SPA_POD_Rectangle(&SPA_RECTANGLE(width, height)),
|
2021-05-25 23:58:51 +02:00
|
|
|
0);
|
|
|
|
// variable framerate
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_framerate,
|
|
|
|
SPA_POD_Fraction(&SPA_FRACTION(0, 1)), 0);
|
|
|
|
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_maxFramerate,
|
|
|
|
SPA_POD_CHOICE_RANGE_Fraction(
|
|
|
|
&SPA_FRACTION(framerate, 1),
|
|
|
|
&SPA_FRACTION(1, 1),
|
|
|
|
&SPA_FRACTION(framerate, 1)),
|
|
|
|
0);
|
2021-05-26 00:54:30 +02:00
|
|
|
return spa_pod_builder_pop(b, &f[0]);
|
2021-05-25 23:58:51 +02:00
|
|
|
}
|
|
|
|
|
2021-11-03 22:21:53 +01:00
|
|
|
static uint32_t build_formats(struct spa_pod_builder *b, struct xdpw_screencast_instance *cast,
|
2022-03-05 09:54:46 +01:00
|
|
|
const struct spa_pod *params[static 2]) {
|
|
|
|
uint32_t param_count;
|
|
|
|
uint32_t modifier_count = 1;
|
|
|
|
uint64_t modifier = DRM_FORMAT_MOD_INVALID;
|
|
|
|
|
|
|
|
if (cast->ctx->gbm) {
|
|
|
|
param_count = 2;
|
|
|
|
params[0] = build_format(b, xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[DMABUF].format),
|
|
|
|
cast->screencopy_frame_info[DMABUF].width, cast->screencopy_frame_info[DMABUF].height, cast->framerate,
|
|
|
|
&modifier, modifier_count);
|
|
|
|
params[1] = build_format(b, xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[WL_SHM].format),
|
|
|
|
cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height, cast->framerate,
|
|
|
|
NULL, 0);
|
|
|
|
} else {
|
|
|
|
param_count = 1;
|
|
|
|
params[0] = build_format(b, xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[WL_SHM].format),
|
|
|
|
cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height, cast->framerate,
|
|
|
|
NULL, 0);
|
|
|
|
}
|
2021-11-03 22:21:53 +01:00
|
|
|
|
|
|
|
return param_count;
|
|
|
|
}
|
|
|
|
|
2020-01-24 23:31:01 +01:00
|
|
|
static void pwr_handle_stream_state_changed(void *data,
|
2020-03-13 17:43:24 +01:00
|
|
|
enum pw_stream_state old, enum pw_stream_state state, const char *error) {
|
2020-04-16 10:21:55 +02:00
|
|
|
struct xdpw_screencast_instance *cast = data;
|
|
|
|
cast->node_id = pw_stream_get_node_id(cast->stream);
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2020-03-13 17:43:24 +01:00
|
|
|
logprint(INFO, "pipewire: stream state changed to \"%s\"",
|
|
|
|
pw_stream_state_as_string(state));
|
2021-08-10 11:56:39 +02:00
|
|
|
logprint(INFO, "pipewire: node id is %d", (int)cast->node_id);
|
2020-01-24 23:31:01 +01:00
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case PW_STREAM_STATE_STREAMING:
|
2020-04-16 10:21:55 +02:00
|
|
|
cast->pwr_stream_state = true;
|
2022-03-03 00:25:42 +01:00
|
|
|
if (cast->frame_state == XDPW_FRAME_STATE_NONE) {
|
2021-08-18 22:33:27 +02:00
|
|
|
xdpw_wlr_frame_start(cast);
|
2021-06-16 18:35:37 +02:00
|
|
|
}
|
2020-01-24 23:31:01 +01:00
|
|
|
break;
|
2022-03-02 17:57:07 +01:00
|
|
|
case PW_STREAM_STATE_PAUSED:
|
|
|
|
if (old == PW_STREAM_STATE_STREAMING) {
|
|
|
|
xdpw_pwr_enqueue_buffer(cast);
|
|
|
|
}
|
|
|
|
// fall through
|
2020-01-24 23:31:01 +01:00
|
|
|
default:
|
2020-04-16 10:21:55 +02:00
|
|
|
cast->pwr_stream_state = false;
|
2020-01-24 23:31:01 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-13 23:19:19 +01:00
|
|
|
static void pwr_handle_stream_param_changed(void *data, uint32_t id,
|
|
|
|
const struct spa_pod *param) {
|
2020-08-14 02:01:04 +02:00
|
|
|
logprint(TRACE, "pipewire: stream parameters changed");
|
2020-04-16 10:21:55 +02:00
|
|
|
struct xdpw_screencast_instance *cast = data;
|
|
|
|
struct pw_stream *stream = cast->stream;
|
2020-01-24 23:31:01 +01:00
|
|
|
uint8_t params_buffer[1024];
|
|
|
|
struct spa_pod_builder b =
|
2020-03-13 17:43:24 +01:00
|
|
|
SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
2021-08-08 05:09:04 +02:00
|
|
|
const struct spa_pod *params[3];
|
2021-06-17 21:51:16 +02:00
|
|
|
uint32_t blocks;
|
|
|
|
uint32_t data_type;
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2020-03-13 23:19:19 +01:00
|
|
|
if (!param || id != SPA_PARAM_Format) {
|
2020-01-24 23:31:01 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-03-13 17:43:24 +01:00
|
|
|
|
2020-04-16 10:21:55 +02:00
|
|
|
spa_format_video_raw_parse(param, &cast->pwr_format);
|
2021-06-13 20:33:34 +02:00
|
|
|
cast->framerate = (uint32_t)(cast->pwr_format.max_framerate.num / cast->pwr_format.max_framerate.denom);
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2021-08-08 04:55:43 +02:00
|
|
|
const struct spa_pod_prop *prop_modifier;
|
|
|
|
if ((prop_modifier = spa_pod_find_prop(param, NULL, SPA_FORMAT_VIDEO_modifier)) != NULL) {
|
2022-03-05 09:54:46 +01:00
|
|
|
cast->buffer_type = DMABUF;
|
|
|
|
data_type = 1<<SPA_DATA_DmaBuf;
|
2021-08-08 04:55:43 +02:00
|
|
|
assert(cast->pwr_format.format == xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[DMABUF].format));
|
|
|
|
if ((prop_modifier->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) > 0) {
|
2021-08-08 05:09:04 +02:00
|
|
|
const struct spa_pod *pod_modifier = &prop_modifier->value;
|
|
|
|
|
|
|
|
uint32_t n_modifiers = SPA_POD_CHOICE_N_VALUES(pod_modifier) - 1;
|
|
|
|
uint64_t *modifiers = SPA_POD_CHOICE_VALUES(pod_modifier);
|
|
|
|
modifiers++;
|
|
|
|
uint32_t flags = GBM_BO_USE_RENDERING;
|
|
|
|
uint64_t modifier;
|
|
|
|
|
|
|
|
struct gbm_bo *bo = gbm_bo_create_with_modifiers2(cast->ctx->gbm,
|
|
|
|
cast->screencopy_frame_info[cast->buffer_type].width, cast->screencopy_frame_info[cast->buffer_type].height,
|
|
|
|
cast->screencopy_frame_info[cast->buffer_type].format, modifiers, n_modifiers, flags);
|
|
|
|
if (bo) {
|
|
|
|
modifier = gbm_bo_get_modifier(bo);
|
|
|
|
gbm_bo_destroy(bo);
|
|
|
|
goto fixate_format;
|
|
|
|
}
|
|
|
|
|
|
|
|
logprint(INFO, "pipewire: unable to allocate a dmabuf with modifiers. Falling back to the old api");
|
|
|
|
for (uint32_t i = 0; i < n_modifiers; i++) {
|
|
|
|
switch (modifiers[i]) {
|
|
|
|
case DRM_FORMAT_MOD_INVALID:
|
|
|
|
modifiers = NULL;
|
|
|
|
n_modifiers = 0;
|
|
|
|
flags = GBM_BO_USE_RENDERING;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
bo = gbm_bo_create_with_modifiers2(cast->ctx->gbm,
|
|
|
|
cast->screencopy_frame_info[cast->buffer_type].width, cast->screencopy_frame_info[cast->buffer_type].height,
|
|
|
|
cast->screencopy_frame_info[cast->buffer_type].format, modifiers, n_modifiers, flags);
|
|
|
|
if (bo) {
|
|
|
|
modifier = gbm_bo_get_modifier(bo);
|
|
|
|
gbm_bo_destroy(bo);
|
|
|
|
goto fixate_format;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logprint(ERROR, "pipewire: unable to allocate a dmabuf");
|
2021-08-08 04:55:43 +02:00
|
|
|
abort();
|
2021-08-08 05:09:04 +02:00
|
|
|
|
|
|
|
fixate_format:
|
|
|
|
params[0] = fixate_format(&b, xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format),
|
|
|
|
cast->screencopy_frame_info[cast->buffer_type].width, cast->screencopy_frame_info[cast->buffer_type].height, cast->framerate, &modifier);
|
|
|
|
|
|
|
|
uint32_t n_params = build_formats(&b, cast, ¶ms[1]);
|
|
|
|
n_params++;
|
|
|
|
|
|
|
|
pw_stream_update_params(stream, params, n_params);
|
|
|
|
return;
|
2021-08-08 04:55:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cast->pwr_format.modifier == DRM_FORMAT_MOD_INVALID) {
|
|
|
|
blocks = 1;
|
|
|
|
} else {
|
|
|
|
blocks = gbm_device_get_format_modifier_plane_count(cast->ctx->gbm,
|
|
|
|
cast->screencopy_frame_info[DMABUF].format, cast->pwr_format.modifier);
|
|
|
|
}
|
2021-06-17 21:51:16 +02:00
|
|
|
} else {
|
|
|
|
cast->buffer_type = WL_SHM;
|
|
|
|
blocks = 1;
|
|
|
|
data_type = 1<<SPA_DATA_MemFd;
|
|
|
|
}
|
2022-03-02 17:57:07 +01:00
|
|
|
|
2022-03-05 12:35:46 +01:00
|
|
|
logprint(DEBUG, "pipewire: Format negotiated:");
|
|
|
|
logprint(DEBUG, "pipewire: buffer_type: %u (%u)", cast->buffer_type, data_type);
|
|
|
|
logprint(DEBUG, "pipewire: format: %u", cast->pwr_format.format);
|
|
|
|
logprint(DEBUG, "pipewire: modifier: %lu", cast->pwr_format.modifier);
|
|
|
|
logprint(DEBUG, "pipewire: size: (%u, %u)", cast->pwr_format.size.width, cast->pwr_format.size.height);
|
|
|
|
logprint(DEBUG, "pipewire: max_framerate: (%u / %u)", cast->pwr_format.max_framerate.num, cast->pwr_format.max_framerate.denom);
|
|
|
|
|
2021-06-17 21:51:16 +02:00
|
|
|
params[0] = build_buffer(&b, blocks, cast->screencopy_frame_info[cast->buffer_type].size,
|
|
|
|
cast->screencopy_frame_info[cast->buffer_type].stride, data_type);
|
2020-03-13 17:43:24 +01:00
|
|
|
|
2020-03-13 23:19:19 +01:00
|
|
|
params[1] = spa_pod_builder_add_object(&b,
|
|
|
|
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
|
|
|
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
|
|
|
|
SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2020-03-13 23:19:19 +01:00
|
|
|
pw_stream_update_params(stream, params, 2);
|
2020-01-24 23:31:01 +01:00
|
|
|
}
|
|
|
|
|
2020-10-21 19:41:26 +02:00
|
|
|
static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) {
|
|
|
|
struct xdpw_screencast_instance *cast = data;
|
|
|
|
struct spa_data *d;
|
|
|
|
|
2022-03-05 12:35:46 +01:00
|
|
|
logprint(DEBUG, "pipewire: add buffer event handle");
|
2020-10-21 19:41:26 +02:00
|
|
|
|
|
|
|
d = buffer->buffer->datas;
|
|
|
|
|
|
|
|
// Select buffer type from negotiation result
|
2020-10-26 16:22:08 +01:00
|
|
|
if ((d[0].type & (1u << SPA_DATA_MemFd)) > 0) {
|
2021-06-17 21:51:16 +02:00
|
|
|
assert(cast->buffer_type == WL_SHM);
|
2020-10-26 16:22:08 +01:00
|
|
|
d[0].type = SPA_DATA_MemFd;
|
2021-06-22 03:49:13 +02:00
|
|
|
} else if ((d[0].type & (1u << SPA_DATA_DmaBuf)) > 0) {
|
|
|
|
assert(cast->buffer_type == DMABUF);
|
|
|
|
d[0].type = SPA_DATA_DmaBuf;
|
2020-10-21 19:41:26 +02:00
|
|
|
} else {
|
|
|
|
logprint(ERROR, "pipewire: unsupported buffer type");
|
|
|
|
cast->err = 1;
|
2020-10-26 03:22:06 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-26 16:22:08 +01:00
|
|
|
logprint(TRACE, "pipewire: selected buffertype %u", d[0].type);
|
|
|
|
|
2021-06-17 21:51:16 +02:00
|
|
|
struct xdpw_buffer *xdpw_buffer = xdpw_buffer_create(cast, cast->buffer_type, &cast->screencopy_frame_info[cast->buffer_type]);
|
2022-03-02 01:57:11 +01:00
|
|
|
if (xdpw_buffer == NULL) {
|
|
|
|
logprint(ERROR, "pipewire: failed to create xdpw buffer");
|
|
|
|
cast->err = 1;
|
|
|
|
return;
|
2020-10-21 19:41:26 +02:00
|
|
|
}
|
2022-03-02 01:57:11 +01:00
|
|
|
wl_list_insert(&cast->buffer_list, &xdpw_buffer->link);
|
|
|
|
buffer->user_data = xdpw_buffer;
|
|
|
|
|
2022-04-16 02:23:13 +02:00
|
|
|
assert(xdpw_buffer->plane_count >= 0 && buffer->buffer->n_datas == (uint32_t)xdpw_buffer->plane_count);
|
|
|
|
for (uint32_t plane = 0; plane < buffer->buffer->n_datas; plane++) {
|
|
|
|
d[plane].maxsize = xdpw_buffer->size[plane];
|
|
|
|
d[plane].mapoffset = 0;
|
|
|
|
d[plane].chunk->size = xdpw_buffer->size[plane];
|
|
|
|
d[plane].chunk->stride = xdpw_buffer->stride[plane];
|
|
|
|
d[plane].chunk->offset = xdpw_buffer->offset[plane];
|
|
|
|
d[plane].flags = 0;
|
|
|
|
d[plane].fd = xdpw_buffer->fd[plane];
|
|
|
|
d[plane].data = NULL;
|
|
|
|
// clients have implemented to check chunk->size if the buffer is valid instead
|
|
|
|
// of using the flags. Until they are patched we should use some arbitrary value.
|
|
|
|
if (xdpw_buffer->buffer_type == DMABUF && d[plane].chunk->size == 0) {
|
|
|
|
d[plane].chunk->size = 9; // This was choosen by a fair d20.
|
|
|
|
}
|
2022-03-05 12:27:33 +01:00
|
|
|
}
|
2020-10-21 19:41:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer) {
|
2021-11-03 22:37:56 +01:00
|
|
|
struct xdpw_screencast_instance *cast = data;
|
|
|
|
|
2022-03-05 12:35:46 +01:00
|
|
|
logprint(DEBUG, "pipewire: remove buffer event handle");
|
2020-10-26 16:22:08 +01:00
|
|
|
|
2022-03-02 01:57:11 +01:00
|
|
|
struct xdpw_buffer *xdpw_buffer = buffer->user_data;
|
|
|
|
if (xdpw_buffer) {
|
|
|
|
xdpw_buffer_destroy(xdpw_buffer);
|
2022-03-02 17:57:07 +01:00
|
|
|
}
|
|
|
|
if (cast->current_frame.pw_buffer == buffer) {
|
|
|
|
cast->current_frame.pw_buffer = NULL;
|
2020-10-26 16:22:08 +01:00
|
|
|
}
|
2022-04-16 02:23:13 +02:00
|
|
|
for (uint32_t plane = 0; plane < buffer->buffer->n_datas; plane++) {
|
|
|
|
buffer->buffer->datas[plane].fd = -1;
|
|
|
|
}
|
2022-03-02 01:57:11 +01:00
|
|
|
buffer->user_data = NULL;
|
2020-10-21 19:41:26 +02:00
|
|
|
}
|
|
|
|
|
2020-01-24 23:31:01 +01:00
|
|
|
static const struct pw_stream_events pwr_stream_events = {
|
2020-03-13 17:43:24 +01:00
|
|
|
PW_VERSION_STREAM_EVENTS,
|
|
|
|
.state_changed = pwr_handle_stream_state_changed,
|
2020-03-13 23:19:19 +01:00
|
|
|
.param_changed = pwr_handle_stream_param_changed,
|
2020-10-21 19:41:26 +02:00
|
|
|
.add_buffer = pwr_handle_stream_add_buffer,
|
|
|
|
.remove_buffer = pwr_handle_stream_remove_buffer,
|
2020-01-24 23:31:01 +01:00
|
|
|
};
|
|
|
|
|
2021-05-27 18:27:03 +02:00
|
|
|
void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast) {
|
|
|
|
logprint(TRACE, "pipewire: dequeueing buffer");
|
|
|
|
|
2022-03-02 17:57:07 +01:00
|
|
|
assert(!cast->current_frame.pw_buffer);
|
|
|
|
if ((cast->current_frame.pw_buffer = pw_stream_dequeue_buffer(cast->stream)) == NULL) {
|
2021-05-27 18:27:03 +02:00
|
|
|
logprint(WARN, "pipewire: out of buffers");
|
|
|
|
return;
|
|
|
|
}
|
2021-06-16 02:38:44 +02:00
|
|
|
|
2022-03-02 17:57:07 +01:00
|
|
|
cast->current_frame.xdpw_buffer = cast->current_frame.pw_buffer->user_data;
|
2021-05-27 18:27:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) {
|
2022-03-02 17:57:07 +01:00
|
|
|
logprint(TRACE, "pipewire: enqueueing buffer");
|
2021-05-27 18:27:03 +02:00
|
|
|
|
2022-03-02 17:57:07 +01:00
|
|
|
if (!cast->current_frame.pw_buffer) {
|
|
|
|
logprint(WARN, "pipewire: no buffer to queue");
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
struct pw_buffer *pw_buf = cast->current_frame.pw_buffer;
|
2021-05-27 18:27:03 +02:00
|
|
|
struct spa_buffer *spa_buf = pw_buf->buffer;
|
|
|
|
struct spa_data *d = spa_buf->datas;
|
2021-08-18 23:06:04 +02:00
|
|
|
|
2022-03-02 17:57:07 +01:00
|
|
|
bool buffer_corrupt = cast->frame_state != XDPW_FRAME_STATE_SUCCESS;
|
|
|
|
|
2021-08-18 23:06:04 +02:00
|
|
|
if (cast->current_frame.y_invert) {
|
|
|
|
//TODO: Flip buffer or set stride negative
|
|
|
|
buffer_corrupt = true;
|
|
|
|
cast->err = 1;
|
|
|
|
}
|
|
|
|
|
2021-05-27 18:27:03 +02:00
|
|
|
struct spa_meta_header *h;
|
|
|
|
if ((h = spa_buffer_find_meta_data(spa_buf, SPA_META_Header, sizeof(*h)))) {
|
|
|
|
h->pts = -1;
|
2021-08-18 23:06:04 +02:00
|
|
|
h->flags = buffer_corrupt ? SPA_META_HEADER_FLAG_CORRUPTED : 0;
|
2021-05-27 18:27:03 +02:00
|
|
|
h->seq = cast->seq++;
|
|
|
|
h->dts_offset = 0;
|
|
|
|
}
|
|
|
|
|
2021-08-18 23:06:04 +02:00
|
|
|
if (buffer_corrupt) {
|
2022-04-16 02:23:13 +02:00
|
|
|
for (uint32_t plane = 0; plane < spa_buf->n_datas; plane++) {
|
|
|
|
d[plane].chunk->flags = SPA_CHUNK_FLAG_CORRUPTED;
|
|
|
|
}
|
2021-08-18 23:06:04 +02:00
|
|
|
} else {
|
2022-04-16 02:23:13 +02:00
|
|
|
for (uint32_t plane = 0; plane < spa_buf->n_datas; plane++) {
|
|
|
|
d[plane].chunk->flags = SPA_CHUNK_FLAG_NONE;
|
|
|
|
}
|
2021-06-16 19:17:07 +02:00
|
|
|
}
|
2021-05-27 18:27:03 +02:00
|
|
|
|
|
|
|
logprint(TRACE, "********************");
|
2022-04-16 02:23:13 +02:00
|
|
|
for (uint32_t plane = 0; plane < spa_buf->n_datas; plane++) {
|
|
|
|
logprint(TRACE, "pipewire: plane %d", plane);
|
|
|
|
logprint(TRACE, "pipewire: fd %u", d[plane].fd);
|
|
|
|
logprint(TRACE, "pipewire: maxsize %d", d[plane].maxsize);
|
|
|
|
logprint(TRACE, "pipewire: size %d", d[plane].chunk->size);
|
|
|
|
logprint(TRACE, "pipewire: stride %d", d[plane].chunk->stride);
|
|
|
|
logprint(TRACE, "pipewire: offset %d", d[plane].chunk->offset);
|
|
|
|
logprint(TRACE, "pipewire: chunk flags %d", d[plane].chunk->flags);
|
|
|
|
}
|
2022-03-02 01:57:11 +01:00
|
|
|
logprint(TRACE, "pipewire: width %d", cast->current_frame.xdpw_buffer->width);
|
|
|
|
logprint(TRACE, "pipewire: height %d", cast->current_frame.xdpw_buffer->height);
|
2021-06-17 01:37:30 +02:00
|
|
|
logprint(TRACE, "pipewire: y_invert %d", cast->current_frame.y_invert);
|
2021-05-27 18:27:03 +02:00
|
|
|
logprint(TRACE, "********************");
|
|
|
|
|
|
|
|
pw_stream_queue_buffer(cast->stream, pw_buf);
|
|
|
|
|
2022-03-02 17:57:07 +01:00
|
|
|
done:
|
2022-03-02 01:57:11 +01:00
|
|
|
cast->current_frame.xdpw_buffer = NULL;
|
2022-03-02 17:57:07 +01:00
|
|
|
cast->current_frame.pw_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-26 02:10:45 +02:00
|
|
|
void pwr_update_stream_param(struct xdpw_screencast_instance *cast) {
|
|
|
|
logprint(TRACE, "pipewire: stream update parameters");
|
|
|
|
struct pw_stream *stream = cast->stream;
|
|
|
|
uint8_t params_buffer[1024];
|
|
|
|
struct spa_pod_builder b =
|
|
|
|
SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
2022-03-05 09:54:46 +01:00
|
|
|
const struct spa_pod *params[2];
|
2021-05-26 02:10:45 +02:00
|
|
|
|
2021-11-03 22:21:53 +01:00
|
|
|
uint32_t n_params = build_formats(&b, cast, params);
|
2021-05-26 02:10:45 +02:00
|
|
|
|
2021-11-03 22:21:53 +01:00
|
|
|
pw_stream_update_params(stream, params, n_params);
|
2021-05-26 02:10:45 +02:00
|
|
|
}
|
|
|
|
|
2021-05-09 13:42:24 +02:00
|
|
|
void xdpw_pwr_stream_create(struct xdpw_screencast_instance *cast) {
|
2020-04-16 10:21:55 +02:00
|
|
|
struct xdpw_screencast_context *ctx = cast->ctx;
|
|
|
|
struct xdpw_state *state = ctx->state;
|
|
|
|
|
2020-03-13 23:19:19 +01:00
|
|
|
pw_loop_enter(state->pw_loop);
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2020-03-13 23:19:19 +01:00
|
|
|
uint8_t buffer[1024];
|
|
|
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
2022-03-05 09:54:46 +01:00
|
|
|
const struct spa_pod *params[2];
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2020-04-16 10:21:55 +02:00
|
|
|
char name[] = "xdpw-stream-XXXXXX";
|
|
|
|
randname(name + strlen(name) - 6);
|
|
|
|
cast->stream = pw_stream_new(ctx->core, name,
|
2020-03-13 23:19:19 +01:00
|
|
|
pw_properties_new(
|
|
|
|
PW_KEY_MEDIA_CLASS, "Video/Source",
|
|
|
|
NULL));
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2020-04-16 10:21:55 +02:00
|
|
|
if (!cast->stream) {
|
2020-03-13 23:19:19 +01:00
|
|
|
logprint(ERROR, "pipewire: failed to create stream");
|
|
|
|
abort();
|
|
|
|
}
|
2020-04-16 10:21:55 +02:00
|
|
|
cast->pwr_stream_state = false;
|
2020-01-24 23:31:01 +01:00
|
|
|
|
2021-11-03 22:21:53 +01:00
|
|
|
uint32_t param_count = build_formats(&b, cast, params);
|
2020-03-13 23:19:19 +01:00
|
|
|
|
2020-04-16 10:21:55 +02:00
|
|
|
pw_stream_add_listener(cast->stream, &cast->stream_listener,
|
|
|
|
&pwr_stream_events, cast);
|
2020-03-13 23:19:19 +01:00
|
|
|
|
2020-04-16 10:21:55 +02:00
|
|
|
pw_stream_connect(cast->stream,
|
2020-03-13 23:19:19 +01:00
|
|
|
PW_DIRECTION_OUTPUT,
|
|
|
|
PW_ID_ANY,
|
2020-10-26 16:22:08 +01:00
|
|
|
(PW_STREAM_FLAG_DRIVER |
|
|
|
|
PW_STREAM_FLAG_ALLOC_BUFFERS),
|
2021-11-03 22:21:53 +01:00
|
|
|
params, param_count);
|
2020-04-16 10:21:55 +02:00
|
|
|
}
|
|
|
|
|
2021-05-22 19:31:52 +02:00
|
|
|
void xdpw_pwr_stream_destroy(struct xdpw_screencast_instance *cast) {
|
|
|
|
if (!cast->stream) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
logprint(DEBUG, "pipewire: destroying stream");
|
|
|
|
pw_stream_flush(cast->stream, false);
|
|
|
|
pw_stream_disconnect(cast->stream);
|
|
|
|
pw_stream_destroy(cast->stream);
|
|
|
|
cast->stream = NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-09 13:42:24 +02:00
|
|
|
int xdpw_pwr_context_create(struct xdpw_state *state) {
|
2020-04-16 10:21:55 +02:00
|
|
|
struct xdpw_screencast_context *ctx = &state->screencast;
|
|
|
|
|
2020-04-20 17:05:08 +02:00
|
|
|
logprint(DEBUG, "pipewire: establishing connection to core");
|
2020-04-16 10:21:55 +02:00
|
|
|
|
|
|
|
if (!ctx->pwr_context) {
|
|
|
|
ctx->pwr_context = pw_context_new(state->pw_loop, NULL, 0);
|
|
|
|
if (!ctx->pwr_context) {
|
|
|
|
logprint(ERROR, "pipewire: failed to create context");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctx->core) {
|
|
|
|
ctx->core = pw_context_connect(ctx->pwr_context, NULL, 0);
|
|
|
|
if (!ctx->core) {
|
|
|
|
logprint(ERROR, "pipewire: couldn't connect to context");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-09 13:44:10 +02:00
|
|
|
void xdpw_pwr_context_destroy(struct xdpw_state *state) {
|
|
|
|
struct xdpw_screencast_context *ctx = &state->screencast;
|
|
|
|
|
|
|
|
logprint(DEBUG, "pipewire: disconnecting fom core");
|
|
|
|
|
|
|
|
if (ctx->core) {
|
|
|
|
pw_core_disconnect(ctx->core);
|
|
|
|
ctx->core = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->pwr_context) {
|
|
|
|
pw_context_destroy(ctx->pwr_context);
|
|
|
|
ctx->pwr_context = NULL;
|
|
|
|
}
|
|
|
|
}
|