0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-20 04:42:18 +02:00

linux-pipewire: Read buffer transformation from PipeWire

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
This commit is contained in:
columbarius 2022-11-11 23:41:09 +01:00 committed by Georges Basile Stavracas Neto
parent 2a2d8fc1bb
commit 8abc3528cf

View File

@ -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)
@ -71,6 +90,8 @@ struct _obs_pipewire {
struct spa_video_info format;
enum spa_meta_videotransform_value transform;
struct {
bool valid;
int x, y;
@ -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;
@ -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 */
@ -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;
@ -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,
@ -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;
}
@ -911,10 +953,20 @@ 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)
@ -922,14 +974,30 @@ 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)
@ -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) {