linux-capture: Announce supported modifiers via PipeWire
Sharing DMA-BUFs via PipeWire requires the client to announce all formats together with their supported modifiers. [1] [1] https://docs.pipewire.org/page_dma_buf.html
This commit is contained in:
parent
b57f7f0aed
commit
c31aba9600
@ -22,6 +22,7 @@
|
||||
|
||||
#include "portal.h"
|
||||
|
||||
#include <util/darray.h>
|
||||
#include <util/dstr.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
@ -50,6 +51,12 @@ struct obs_pw_version {
|
||||
int micro;
|
||||
};
|
||||
|
||||
struct format_info {
|
||||
uint32_t spa_format;
|
||||
uint32_t drm_format;
|
||||
DARRAY(uint64_t) modifiers;
|
||||
};
|
||||
|
||||
struct _obs_pipewire_data {
|
||||
GCancellable *cancellable;
|
||||
|
||||
@ -98,6 +105,8 @@ struct _obs_pipewire_data {
|
||||
enum obs_pw_capture_type capture_type;
|
||||
struct obs_video_info video_info;
|
||||
bool negotiated;
|
||||
|
||||
DARRAY(struct format_info) format_info;
|
||||
};
|
||||
|
||||
struct dbus_call_data {
|
||||
@ -366,6 +375,176 @@ static void swap_texture_red_blue(gs_texture_t *texture)
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static inline struct spa_pod *build_format(struct spa_pod_builder *b,
|
||||
struct obs_video_info *ovi,
|
||||
uint32_t format, uint64_t *modifiers,
|
||||
size_t modifier_count)
|
||||
{
|
||||
struct spa_pod_frame f[2];
|
||||
|
||||
/* Make an object of type SPA_TYPE_OBJECT_Format and id SPA_PARAM_EnumFormat.
|
||||
* The object type is important because it defines the properties that are
|
||||
* acceptable. The id gives more context about what the object is meant to
|
||||
* contain. In this case we enumerate supported formats. */
|
||||
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format,
|
||||
SPA_PARAM_EnumFormat);
|
||||
/* add media type and media subtype properties */
|
||||
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);
|
||||
|
||||
/* formats */
|
||||
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
||||
|
||||
/* modifier */
|
||||
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 an array */
|
||||
for (uint32_t i = 0; i < modifier_count; i++) {
|
||||
uint64_t modifier = modifiers[i];
|
||||
spa_pod_builder_long(b, modifier);
|
||||
if (i == 0)
|
||||
spa_pod_builder_long(b, modifier);
|
||||
}
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
}
|
||||
/* add size and framerate ranges */
|
||||
spa_pod_builder_add(b, SPA_FORMAT_VIDEO_size,
|
||||
SPA_POD_CHOICE_RANGE_Rectangle(
|
||||
&SPA_RECTANGLE(320, 240), // Arbitrary
|
||||
&SPA_RECTANGLE(1, 1),
|
||||
&SPA_RECTANGLE(8192, 4320)),
|
||||
SPA_FORMAT_VIDEO_framerate,
|
||||
SPA_POD_CHOICE_RANGE_Fraction(
|
||||
&SPA_FRACTION(ovi->fps_num, ovi->fps_den),
|
||||
&SPA_FRACTION(0, 1), &SPA_FRACTION(360, 1)),
|
||||
0);
|
||||
return spa_pod_builder_pop(b, &f[0]);
|
||||
}
|
||||
|
||||
static bool build_format_params(obs_pipewire_data *obs_pw,
|
||||
struct spa_pod_builder *pod_builder,
|
||||
const struct spa_pod ***param_list,
|
||||
uint32_t *n_params)
|
||||
{
|
||||
uint32_t params_count = 0;
|
||||
|
||||
const struct spa_pod **params;
|
||||
params =
|
||||
bzalloc(2 * obs_pw->format_info.num * sizeof(struct spa_pod *));
|
||||
|
||||
if (!params) {
|
||||
blog(LOG_ERROR,
|
||||
"[pipewire] Failed to allocate memory for param pointers");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < obs_pw->format_info.num; i++) {
|
||||
if (obs_pw->format_info.array[i].modifiers.num == 0 ||
|
||||
!check_pw_version(&obs_pw->server_version, 0, 3, 33)) {
|
||||
continue;
|
||||
}
|
||||
params[params_count++] = build_format(
|
||||
pod_builder, &obs_pw->video_info,
|
||||
obs_pw->format_info.array[i].spa_format,
|
||||
obs_pw->format_info.array[i].modifiers.array,
|
||||
obs_pw->format_info.array[i].modifiers.num);
|
||||
}
|
||||
for (size_t i = 0; i < obs_pw->format_info.num; i++) {
|
||||
params[params_count++] = build_format(
|
||||
pod_builder, &obs_pw->video_info,
|
||||
obs_pw->format_info.array[i].spa_format, NULL, 0);
|
||||
}
|
||||
*param_list = params;
|
||||
*n_params = params_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool drm_format_available(uint32_t drm_format, uint32_t *drm_formats,
|
||||
size_t n_drm_formats)
|
||||
{
|
||||
for (size_t j = 0; j < n_drm_formats; j++) {
|
||||
if (drm_format == drm_formats[j]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void init_format_info(obs_pipewire_data *obs_pw)
|
||||
{
|
||||
da_init(obs_pw->format_info);
|
||||
|
||||
uint32_t formats[] = {
|
||||
SPA_VIDEO_FORMAT_BGRA,
|
||||
SPA_VIDEO_FORMAT_RGBA,
|
||||
SPA_VIDEO_FORMAT_BGRx,
|
||||
SPA_VIDEO_FORMAT_RGBx,
|
||||
};
|
||||
|
||||
size_t n_formats = sizeof(formats) / sizeof(formats[0]);
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
enum gs_dmabuf_flags dmabuf_flags;
|
||||
uint32_t *drm_formats = NULL;
|
||||
size_t n_drm_formats;
|
||||
|
||||
bool capabilities_queried = gs_query_dmabuf_capabilities(
|
||||
&dmabuf_flags, &drm_formats, &n_drm_formats);
|
||||
|
||||
for (size_t i = 0; i < n_formats; i++) {
|
||||
struct format_info *info;
|
||||
uint32_t drm_format;
|
||||
|
||||
if (!spa_pixel_format_to_drm_format(formats[i], &drm_format))
|
||||
continue;
|
||||
|
||||
if (!drm_format_available(drm_format, drm_formats,
|
||||
n_drm_formats))
|
||||
continue;
|
||||
|
||||
info = da_push_back_new(obs_pw->format_info);
|
||||
da_init(info->modifiers);
|
||||
info->spa_format = formats[i];
|
||||
info->drm_format = drm_format;
|
||||
|
||||
if (!capabilities_queried)
|
||||
continue;
|
||||
|
||||
size_t n_modifiers;
|
||||
uint64_t *modifiers = NULL;
|
||||
if (gs_query_dmabuf_modifiers_for_format(drm_format, &modifiers,
|
||||
&n_modifiers)) {
|
||||
da_push_back_array(info->modifiers, modifiers,
|
||||
n_modifiers);
|
||||
}
|
||||
bfree(modifiers);
|
||||
|
||||
if (dmabuf_flags &
|
||||
GS_DMABUF_FLAG_IMPLICIT_MODIFIERS_SUPPORTED) {
|
||||
uint64_t modifier_implicit = DRM_FORMAT_MOD_INVALID;
|
||||
da_push_back(info->modifiers, &modifier_implicit);
|
||||
}
|
||||
}
|
||||
obs_leave_graphics();
|
||||
|
||||
bfree(drm_formats);
|
||||
}
|
||||
|
||||
static void clear_format_info(obs_pipewire_data *obs_pw)
|
||||
{
|
||||
for (size_t i = 0; i < obs_pw->format_info.num; i++) {
|
||||
da_free(obs_pw->format_info.array[i].modifiers);
|
||||
}
|
||||
da_free(obs_pw->format_info);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------- */
|
||||
|
||||
static void on_process_cb(void *user_data)
|
||||
@ -555,7 +734,10 @@ static void on_param_changed_cb(void *user_data, uint32_t id,
|
||||
spa_format_video_raw_parse(param, &obs_pw->format.info.raw);
|
||||
|
||||
buffer_types = 1 << SPA_DATA_MemPtr;
|
||||
if (check_pw_version(&obs_pw->server_version, 0, 3, 24))
|
||||
bool has_modifier =
|
||||
spa_pod_find_prop(param, NULL, SPA_FORMAT_VIDEO_modifier) !=
|
||||
NULL;
|
||||
if (has_modifier || check_pw_version(&obs_pw->server_version, 0, 3, 24))
|
||||
buffer_types |= 1 << SPA_DATA_DmaBuf;
|
||||
|
||||
blog(LOG_DEBUG, "[pipewire] Negotiated format:");
|
||||
@ -659,9 +841,9 @@ static const struct pw_core_events core_events = {
|
||||
static void play_pipewire_stream(obs_pipewire_data *obs_pw)
|
||||
{
|
||||
struct spa_pod_builder pod_builder;
|
||||
const struct spa_pod *params[1];
|
||||
uint8_t params_buffer[1024];
|
||||
struct obs_video_info ovi;
|
||||
const struct spa_pod **params = NULL;
|
||||
uint32_t n_params;
|
||||
uint8_t params_buffer[2048];
|
||||
|
||||
obs_pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL);
|
||||
obs_pw->context = pw_context_new(
|
||||
@ -706,33 +888,23 @@ static void play_pipewire_stream(obs_pipewire_data *obs_pw)
|
||||
pod_builder =
|
||||
SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
||||
|
||||
obs_get_video_info(&ovi);
|
||||
params[0] = spa_pod_builder_add_object(
|
||||
&pod_builder, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_VIDEO_format,
|
||||
SPA_POD_CHOICE_ENUM_Id(
|
||||
4, SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA,
|
||||
SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx),
|
||||
SPA_FORMAT_VIDEO_size,
|
||||
SPA_POD_CHOICE_RANGE_Rectangle(
|
||||
&SPA_RECTANGLE(320, 240), // Arbitrary
|
||||
&SPA_RECTANGLE(1, 1), &SPA_RECTANGLE(8192, 4320)),
|
||||
SPA_FORMAT_VIDEO_framerate,
|
||||
SPA_POD_CHOICE_RANGE_Fraction(
|
||||
&SPA_FRACTION(ovi.fps_num, ovi.fps_den),
|
||||
&SPA_FRACTION(0, 1), &SPA_FRACTION(360, 1)));
|
||||
obs_pw->video_info = ovi;
|
||||
obs_get_video_info(&obs_pw->video_info);
|
||||
|
||||
if (!build_format_params(obs_pw, &pod_builder, ¶ms, &n_params)) {
|
||||
pw_thread_loop_unlock(obs_pw->thread_loop);
|
||||
teardown_pipewire(obs_pw);
|
||||
return;
|
||||
}
|
||||
|
||||
pw_stream_connect(
|
||||
obs_pw->stream, PW_DIRECTION_INPUT, obs_pw->pipewire_node,
|
||||
PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS, params,
|
||||
1);
|
||||
n_params);
|
||||
|
||||
blog(LOG_INFO, "[pipewire] playing stream…");
|
||||
|
||||
pw_thread_loop_unlock(obs_pw->thread_loop);
|
||||
bfree(params);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------- */
|
||||
@ -1193,6 +1365,8 @@ void *obs_pipewire_create(enum obs_pw_capture_type capture_type,
|
||||
if (!init_obs_pipewire(obs_pw))
|
||||
g_clear_pointer(&obs_pw, bfree);
|
||||
|
||||
init_format_info(obs_pw);
|
||||
|
||||
return obs_pw;
|
||||
}
|
||||
|
||||
@ -1205,6 +1379,7 @@ void obs_pipewire_destroy(obs_pipewire_data *obs_pw)
|
||||
destroy_session(obs_pw);
|
||||
|
||||
g_clear_pointer(&obs_pw->restore_token, bfree);
|
||||
clear_format_info(obs_pw);
|
||||
|
||||
bfree(obs_pw);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user