Skip to content

Commit

Permalink
linux-pipewire: Read buffer transformation from PipeWire
Browse files Browse the repository at this point in the history
PipeWire allows since 0.3.62 [1] to attach metadata to a buffer
describing a transformation of the buffercontent. Clients should then
undo that transformation to import it correctly.

We can enable this feature using macro guards and runtime server version
checks on supported systems.

[1] https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/1423
  • Loading branch information
columbarius authored and GeorgesStavracas committed Feb 25, 2023
1 parent 2a2d8fc commit 8abc352
Showing 1 changed file with 130 additions and 17 deletions.
147 changes: 130 additions & 17 deletions plugins/linux-pipewire/pipewire.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,30 @@
#include <libdrm/drm_fourcc.h>
#include <pipewire/pipewire.h>
#include <spa/param/video/format-utils.h>
#include <spa/buffer/meta.h>
#include <spa/debug/format.h>
#include <spa/debug/types.h>
#include <spa/param/video/type-info.h>
#include <spa/utils/result.h>

#if !PW_CHECK_VERSION(0, 3, 62)
enum spa_meta_videotransform_value {
SPA_META_TRANSFORMATION_None = 0, /**< no transform */
SPA_META_TRANSFORMATION_90, /**< 90 degree counter-clockwise */
SPA_META_TRANSFORMATION_180, /**< 180 degree counter-clockwise */
SPA_META_TRANSFORMATION_270, /**< 270 degree counter-clockwise */
SPA_META_TRANSFORMATION_Flipped, /**< 180 degree flipped around the vertical axis. Equivalent
* to a reflexion through the vertical line splitting the
* bufffer in two equal sized parts */
SPA_META_TRANSFORMATION_Flipped90, /**< flip then rotate around 90 degree counter-clockwise */
SPA_META_TRANSFORMATION_Flipped180, /**< flip then rotate around 180 degree counter-clockwise */
SPA_META_TRANSFORMATION_Flipped270, /**< flip then rotate around 270 degree counter-clockwise */
};

#define SPA_META_VideoTransform 8

#endif

#define CURSOR_META_SIZE(width, height) \
(sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + \
width * height * 4)
Expand Down Expand Up @@ -71,6 +90,8 @@ struct _obs_pipewire {

struct spa_video_info format;

enum spa_meta_videotransform_value transform;

struct {
bool valid;
int x, y;
Expand Down Expand Up @@ -454,6 +475,7 @@ static void on_process_cb(void *user_data)
uint32_t drm_format;
struct spa_meta_header *header;
struct spa_meta_region *region;
struct spa_meta_videotransform *video_transform;
struct spa_buffer *buffer;
struct pw_buffer *b;
bool swap_red_blue = false;
Expand Down Expand Up @@ -605,6 +627,14 @@ static void on_process_cb(void *user_data)
obs_pw->crop.valid = false;
}

/* Video Transform */
video_transform = spa_buffer_find_meta_data(
buffer, SPA_META_VideoTransform, sizeof(*video_transform));
if (video_transform)
obs_pw->transform = video_transform->transform;
else
obs_pw->transform = SPA_META_TRANSFORMATION_None;

read_metadata:

/* Cursor */
Expand Down Expand Up @@ -656,7 +686,8 @@ static void on_param_changed_cb(void *user_data, uint32_t id,
{
obs_pipewire *obs_pw = user_data;
struct spa_pod_builder pod_builder;
const struct spa_pod *params[4];
const struct spa_pod *params[5];
uint32_t n_params = 0;
uint32_t buffer_types;
uint8_t params_buffer[1024];
int result;
Expand Down Expand Up @@ -705,14 +736,14 @@ static void on_param_changed_cb(void *user_data, uint32_t id,
/* Video crop */
pod_builder =
SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
params[0] = spa_pod_builder_add_object(
params[n_params++] = spa_pod_builder_add_object(
&pod_builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoCrop),
SPA_PARAM_META_size,
SPA_POD_Int(sizeof(struct spa_meta_region)));

/* Cursor */
params[1] = spa_pod_builder_add_object(
params[n_params++] = spa_pod_builder_add_object(
&pod_builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Cursor),
SPA_PARAM_META_size,
Expand All @@ -721,18 +752,29 @@ static void on_param_changed_cb(void *user_data, uint32_t id,
CURSOR_META_SIZE(1024, 1024)));

/* Buffer options */
params[2] = spa_pod_builder_add_object(
params[n_params++] = spa_pod_builder_add_object(
&pod_builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
SPA_PARAM_BUFFERS_dataType, SPA_POD_Int(buffer_types));

/* Meta header */
params[3] = spa_pod_builder_add_object(
params[n_params++] = spa_pod_builder_add_object(
&pod_builder, 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)));

pw_stream_update_params(obs_pw->stream, params, 4);
#if PW_CHECK_VERSION(0, 3, 62)
if (check_pw_version(&obs_pw->server_version, 0, 3, 62)) {
/* Video transformation */
params[n_params++] = spa_pod_builder_add_object(
&pod_builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
SPA_PARAM_META_type,
SPA_POD_Id(SPA_META_VideoTransform),
SPA_PARAM_META_size,
SPA_POD_Int(sizeof(struct spa_meta_videotransform)));
}
#endif
pw_stream_update_params(obs_pw->stream, params, n_params);

obs_pw->negotiated = true;
}
Expand Down Expand Up @@ -911,25 +953,51 @@ uint32_t obs_pipewire_get_width(obs_pipewire *obs_pw)
if (!obs_pw->negotiated)
return 0;

if (obs_pw->crop.valid)
return obs_pw->crop.width;
else
return obs_pw->format.info.raw.size.width;
switch (obs_pw->transform) {
case SPA_META_TRANSFORMATION_Flipped:
case SPA_META_TRANSFORMATION_None:
case SPA_META_TRANSFORMATION_Flipped180:
case SPA_META_TRANSFORMATION_180:
return obs_pw->crop.valid ? obs_pw->crop.width
: obs_pw->format.info.raw.size.width;
case SPA_META_TRANSFORMATION_Flipped90:
case SPA_META_TRANSFORMATION_90:
case SPA_META_TRANSFORMATION_Flipped270:
case SPA_META_TRANSFORMATION_270:
return obs_pw->crop.valid ? obs_pw->crop.height
: obs_pw->format.info.raw.size.height;
}
}

uint32_t obs_pipewire_get_height(obs_pipewire *obs_pw)
{
if (!obs_pw->negotiated)
return 0;

if (obs_pw->crop.valid)
return obs_pw->crop.height;
else
return obs_pw->format.info.raw.size.height;
switch (obs_pw->transform) {
case SPA_META_TRANSFORMATION_Flipped:
case SPA_META_TRANSFORMATION_None:
case SPA_META_TRANSFORMATION_Flipped180:
case SPA_META_TRANSFORMATION_180:
return obs_pw->crop.valid ? obs_pw->crop.height
: obs_pw->format.info.raw.size.height;
case SPA_META_TRANSFORMATION_Flipped90:
case SPA_META_TRANSFORMATION_90:
case SPA_META_TRANSFORMATION_Flipped270:
case SPA_META_TRANSFORMATION_270:
return obs_pw->crop.valid ? obs_pw->crop.width
: obs_pw->format.info.raw.size.width;
}
}

void obs_pipewire_video_render(obs_pipewire *obs_pw, gs_effect_t *effect)
{
double rot = 0;
int flip = 0;
double offset_x = 0;
double offset_y = 0;
bool has_crop;

gs_eparam_t *image;

if (!obs_pw->texture)
Expand All @@ -938,13 +1006,58 @@ void obs_pipewire_video_render(obs_pipewire *obs_pw, gs_effect_t *effect)
image = gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, obs_pw->texture);

if (has_effective_crop(obs_pw)) {
gs_draw_sprite_subregion(obs_pw->texture, 0, obs_pw->crop.x,
has_crop = has_effective_crop(obs_pw);

switch (obs_pw->transform) {
case SPA_META_TRANSFORMATION_Flipped:
flip = GS_FLIP_U;
/* fallthrough */
case SPA_META_TRANSFORMATION_None:
rot = 0;
break;
case SPA_META_TRANSFORMATION_Flipped90:
flip = GS_FLIP_V;
/* fallthrough */
case SPA_META_TRANSFORMATION_90:
rot = 90;
offset_x = 0;
offset_y = has_crop ? obs_pw->crop.height
: obs_pw->format.info.raw.size.height;
break;
case SPA_META_TRANSFORMATION_Flipped180:
flip = GS_FLIP_U;
/* fallthrough */
case SPA_META_TRANSFORMATION_180:
rot = 180;
offset_x = has_crop ? obs_pw->crop.width
: obs_pw->format.info.raw.size.width;
offset_y = has_crop ? obs_pw->crop.height
: obs_pw->format.info.raw.size.height;
break;
case SPA_META_TRANSFORMATION_Flipped270:
flip = GS_FLIP_V;
/* fallthrough */
case SPA_META_TRANSFORMATION_270:
rot = 270;
offset_x = has_crop ? obs_pw->crop.width
: obs_pw->format.info.raw.size.width;
offset_y = 0;
break;
}
if (rot != 0) {
gs_matrix_push();
gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD(rot));
gs_matrix_translate3f(-offset_x, -offset_y, 0.0f);
}
if (has_crop) {
gs_draw_sprite_subregion(obs_pw->texture, flip, obs_pw->crop.x,
obs_pw->crop.y, obs_pw->crop.width,
obs_pw->crop.height);
} else {
gs_draw_sprite(obs_pw->texture, 0, 0, 0);
gs_draw_sprite(obs_pw->texture, flip, 0, 0);
}
if (rot != 0)
gs_matrix_pop();

if (obs_pw->cursor.visible && obs_pw->cursor.valid &&
obs_pw->cursor.texture) {
Expand Down

0 comments on commit 8abc352

Please sign in to comment.