From c5d2fc856a28b3a4f26ffffa420cbd9ff92875bb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 14 Jan 2020 09:44:45 +0100 Subject: [PATCH] screen-cast: Update to PipeWire 0.3 API Update to 0.3 API [jadahl: update Dockerfile to include new enough pipewire] Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1051 https://gitlab.gnome.org/GNOME/mutter/merge_requests/1062 --- .gitlab-ci/Dockerfile | 3 + meson.build | 4 +- src/backends/meta-screen-cast-stream-src.c | 261 ++++++++------------- 3 files changed, 107 insertions(+), 161 deletions(-) diff --git a/.gitlab-ci/Dockerfile b/.gitlab-ci/Dockerfile index 9accc9756..393c76394 100644 --- a/.gitlab-ci/Dockerfile +++ b/.gitlab-ci/Dockerfile @@ -11,6 +11,7 @@ RUN dnf -y update && dnf -y upgrade && \ dnf install -y 'dnf-command(builddep)' && \ dnf install -y 'dnf-command(copr)' && \ dnf copr enable -y fmuellner/gnome-shell-ci && \ + dnf copr enable -y jadahl/mutter-ci && \ dnf -y update && dnf -y upgrade && \ dnf builddep -y mutter && \ @@ -26,4 +27,6 @@ RUN dnf -y update && dnf -y upgrade && \ dnf remove -y gnome-bluetooth-libs-devel dbus-glib-devel upower-devel python3-devel && \ dnf remove -y --noautoremove mutter mutter-devel && \ + dnf upgrade -y 'pkgconfig(libpipewire-0.3)' && \ + dnf clean all diff --git a/meson.build b/meson.build index daa6cc6fb..e071a5e40 100644 --- a/meson.build +++ b/meson.build @@ -50,7 +50,7 @@ libinput_req = '>= 1.7' gbm_req = '>= 10.3' # screen cast version requirements -libpipewire_req = '>= 0.2.5' +libpipewire_req = '>= 0.3.0' gnome = import('gnome') pkg = import('pkgconfig') @@ -241,7 +241,7 @@ endif have_remote_desktop = get_option('remote_desktop') if have_remote_desktop - libpipewire_dep = dependency('libpipewire-0.2', version: libpipewire_req) + libpipewire_dep = dependency('libpipewire-0.3', version: libpipewire_req) endif have_introspection = get_option('introspection') diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 82c5cba43..ba1ce94a7 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -62,15 +63,6 @@ enum static guint signals[N_SIGNALS]; -typedef struct _MetaSpaType -{ - struct spa_type_media_type media_type; - struct spa_type_media_subtype media_subtype; - struct spa_type_format_video format_video; - struct spa_type_video_format video_format; - uint32_t meta_cursor; -} MetaSpaType; - typedef struct _MetaPipeWireSource { GSource base; @@ -82,19 +74,19 @@ typedef struct _MetaScreenCastStreamSrcPrivate { MetaScreenCastStream *stream; + struct pw_context *pipewire_context; struct pw_core *pipewire_core; - struct pw_remote *pipewire_remote; - struct pw_type *pipewire_type; MetaPipeWireSource *pipewire_source; - struct spa_hook pipewire_remote_listener; + struct spa_hook pipewire_core_listener; gboolean is_enabled; struct pw_stream *pipewire_stream; struct spa_hook pipewire_stream_listener; + uint32_t node_id; - MetaSpaType spa_type; struct spa_video_info_raw video_format; + int video_stride; uint64_t last_frame_timestamp_us; @@ -112,8 +104,6 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStreamSrc, meta_screen_cast_stream_src_init_initable_iface) G_ADD_PRIVATE (MetaScreenCastStreamSrc)) -#define PROP_RANGE(min, max) 2, (min), (max) - static void meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src, int *width, @@ -286,9 +276,6 @@ meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStre int x, int y) { - MetaScreenCastStreamSrcPrivate *priv = - meta_screen_cast_stream_src_get_instance_private (src); - MetaSpaType *spa_type = &priv->spa_type; struct spa_meta_bitmap *spa_meta_bitmap; spa_meta_cursor->id = 1; @@ -300,7 +287,7 @@ meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStre spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, spa_meta_cursor->bitmap_offset, struct spa_meta_bitmap); - spa_meta_bitmap->format = spa_type->video_format.RGBA; + spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA; spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap); spa_meta_cursor->hotspot.x = 0; @@ -317,9 +304,6 @@ meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc int y, float scale) { - MetaScreenCastStreamSrcPrivate *priv = - meta_screen_cast_stream_src_get_instance_private (src); - MetaSpaType *spa_type = &priv->spa_type; CoglTexture *cursor_texture; struct spa_meta_bitmap *spa_meta_bitmap; int hotspot_x, hotspot_y; @@ -346,7 +330,7 @@ meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, spa_meta_cursor->bitmap_offset, struct spa_meta_bitmap); - spa_meta_bitmap->format = spa_type->video_format.RGBA; + spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA; spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap); meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); @@ -382,12 +366,10 @@ static void add_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_buffer *spa_buffer) { - MetaScreenCastStreamSrcPrivate *priv = - meta_screen_cast_stream_src_get_instance_private (src); - MetaSpaType *spa_type = &priv->spa_type; struct spa_meta_cursor *spa_meta_cursor; - spa_meta_cursor = spa_buffer_find_meta (spa_buffer, spa_type->meta_cursor); + spa_meta_cursor = spa_buffer_find_meta_data (spa_buffer, SPA_META_Cursor, + sizeof (*spa_meta_cursor)); if (spa_meta_cursor) meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor); } @@ -447,14 +429,14 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) { data = spa_buffer->datas[0].data; } - else if (spa_buffer->datas[0].type == priv->pipewire_type->data.MemFd) + else if (spa_buffer->datas[0].type == SPA_DATA_MemFd) { map = mmap (NULL, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, PROT_READ | PROT_WRITE, MAP_SHARED, spa_buffer->datas[0].fd, 0); if (map == MAP_FAILED) { - g_warning ("Failed to mmap pipewire stream buffer: %s\n", + g_warning ("Failed to mmap pipewire stream buffer: %s", strerror (errno)); return; } @@ -469,28 +451,30 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) if (meta_screen_cast_stream_src_record_frame (src, data)) { - struct spa_meta_video_crop *spa_meta_video_crop; + struct spa_meta_region *spa_meta_video_crop; spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; + spa_buffer->datas[0].chunk->stride = priv->video_stride; /* Update VideoCrop if needed */ spa_meta_video_crop = - spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop); + spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop, + sizeof (*spa_meta_video_crop)); if (spa_meta_video_crop) { if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect)) { - spa_meta_video_crop->x = crop_rect.x; - spa_meta_video_crop->y = crop_rect.y; - spa_meta_video_crop->width = crop_rect.width; - spa_meta_video_crop->height = crop_rect.height; + spa_meta_video_crop->region.position.x = crop_rect.x; + spa_meta_video_crop->region.position.y = crop_rect.y; + spa_meta_video_crop->region.size.width = crop_rect.width; + spa_meta_video_crop->region.size.height = crop_rect.height; } else { - spa_meta_video_crop->x = 0; - spa_meta_video_crop->y = 0; - spa_meta_video_crop->width = priv->stream_width; - spa_meta_video_crop->height = priv->stream_height; + spa_meta_video_crop->region.position.x = 0; + spa_meta_video_crop->region.position.y = 0; + spa_meta_video_crop->region.size.width = priv->stream_width; + spa_meta_video_crop->region.size.height = priv->stream_height; } } } @@ -555,7 +539,6 @@ on_stream_state_changed (void *data, MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); - uint32_t node_id; switch (state) { @@ -563,14 +546,12 @@ on_stream_state_changed (void *data, g_warning ("pipewire stream error: %s", error_message); meta_screen_cast_stream_src_notify_closed (src); break; - case PW_STREAM_STATE_CONFIGURE: - node_id = pw_stream_get_node_id (priv->pipewire_stream); - g_signal_emit (src, signals[READY], 0, (unsigned int) node_id); - break; - case PW_STREAM_STATE_UNCONNECTED: - case PW_STREAM_STATE_CONNECTING: - case PW_STREAM_STATE_READY: case PW_STREAM_STATE_PAUSED: + if (priv->node_id == SPA_ID_INVALID && priv->pipewire_stream) + { + priv->node_id = pw_stream_get_node_id (priv->pipewire_stream); + g_signal_emit (src, signals[READY], 0, (unsigned int) priv->node_id); + } if (meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_disable (src); break; @@ -578,68 +559,69 @@ on_stream_state_changed (void *data, if (!meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_enable (src); break; + case PW_STREAM_STATE_UNCONNECTED: + case PW_STREAM_STATE_CONNECTING: + break; } } static void -on_stream_format_changed (void *data, - const struct spa_pod *format) +on_stream_param_changed (void *data, + uint32_t id, + const struct spa_pod *format) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); - struct pw_type *pipewire_type = priv->pipewire_type; uint8_t params_buffer[1024]; int32_t width, height, stride, size; struct spa_pod_builder pod_builder; const struct spa_pod *params[3]; const int bpp = 4; - if (!format) - { - pw_stream_finish_format (priv->pipewire_stream, 0, NULL, 0); - return; - } + if (!format || id != SPA_PARAM_Format) + return; spa_format_video_raw_parse (format, - &priv->video_format, - &priv->spa_type.format_video); + &priv->video_format); width = priv->video_format.size.width; height = priv->video_format.size.height; stride = SPA_ROUND_UP_N (width * bpp, 4); size = height * stride; + priv->video_stride = stride; + pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer)); - params[0] = spa_pod_builder_object ( + params[0] = spa_pod_builder_add_object ( &pod_builder, - pipewire_type->param.idBuffers, pipewire_type->param_buffers.Buffers, - ":", pipewire_type->param_buffers.size, "i", size, - ":", pipewire_type->param_buffers.stride, "i", stride, - ":", pipewire_type->param_buffers.buffers, "iru", 16, PROP_RANGE (2, 16), - ":", pipewire_type->param_buffers.align, "i", 16); + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (16, 2, 16), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int (1), + SPA_PARAM_BUFFERS_size, SPA_POD_Int (size), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int (stride), + SPA_PARAM_BUFFERS_align, SPA_POD_Int (16)); - params[1] = spa_pod_builder_object ( + params[1] = spa_pod_builder_add_object ( &pod_builder, - pipewire_type->param.idMeta, pipewire_type->param_meta.Meta, - ":", pipewire_type->param_meta.type, "I", pipewire_type->meta.VideoCrop, - ":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop)); + 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))); - params[2] = spa_pod_builder_object ( + params[2] = spa_pod_builder_add_object ( &pod_builder, - pipewire_type->param.idMeta, pipewire_type->param_meta.Meta, - ":", pipewire_type->param_meta.type, "I", priv->spa_type.meta_cursor, - ":", pipewire_type->param_meta.size, "i", CURSOR_META_SIZE (64, 64)); + SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, + SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor), + SPA_PARAM_META_size, SPA_POD_Int (CURSOR_META_SIZE (64, 64))); - pw_stream_finish_format (priv->pipewire_stream, 0, - params, G_N_ELEMENTS (params)); + pw_stream_update_params (priv->pipewire_stream, params, G_N_ELEMENTS (params)); } static const struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, .state_changed = on_stream_state_changed, - .format_changed = on_stream_format_changed, + .param_changed = on_stream_param_changed, }; static struct pw_stream * @@ -652,8 +634,6 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, uint8_t buffer[1024]; struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT (buffer, sizeof (buffer)); - MetaSpaType *spa_type = &priv->spa_type; - struct pw_type *pipewire_type = priv->pipewire_type; float frame_rate; MetaFraction frame_rate_fraction; struct spa_fraction max_framerate; @@ -661,7 +641,9 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, const struct spa_pod *params[1]; int result; - pipewire_stream = pw_stream_new (priv->pipewire_remote, + priv->node_id = SPA_ID_INVALID; + + pipewire_stream = pw_stream_new (priv->pipewire_core, "meta-screen-cast-src", NULL); if (!pipewire_stream) @@ -682,17 +664,17 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, max_framerate = SPA_FRACTION (frame_rate_fraction.num, frame_rate_fraction.denom); - params[0] = spa_pod_builder_object ( + params[0] = spa_pod_builder_add_object ( &pod_builder, - pipewire_type->param.idEnumFormat, pipewire_type->spa_format, - "I", spa_type->media_type.video, - "I", spa_type->media_subtype.raw, - ":", spa_type->format_video.format, "I", spa_type->video_format.BGRx, - ":", spa_type->format_video.size, "R", &SPA_RECTANGLE (priv->stream_width, - priv->stream_height), - ":", spa_type->format_video.framerate, "F", &SPA_FRACTION (0, 1), - ":", spa_type->format_video.max_framerate, "Fru", &max_framerate, - PROP_RANGE (&min_framerate, + 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_Id (SPA_VIDEO_FORMAT_BGRx), + SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle (&SPA_RECTANGLE (priv->stream_width, + priv->stream_height)), + SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)), + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction (&max_framerate, + &min_framerate, &max_framerate)); pw_stream_add_listener (pipewire_stream, @@ -702,7 +684,7 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, result = pw_stream_connect (pipewire_stream, PW_DIRECTION_OUTPUT, - NULL, + SPA_ID_INVALID, (PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS), params, G_N_ELEMENTS (params)); @@ -717,40 +699,18 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, } static void -on_state_changed (void *data, - enum pw_remote_state old, - enum pw_remote_state state, - const char *error_message) +on_core_error (void *data, + uint32_t id, + int seq, + int res, + const char *message) { MetaScreenCastStreamSrc *src = data; - MetaScreenCastStreamSrcPrivate *priv = - meta_screen_cast_stream_src_get_instance_private (src); - struct pw_stream *pipewire_stream; - GError *error = NULL; - switch (state) - { - case PW_REMOTE_STATE_ERROR: - g_warning ("pipewire remote error: %s\n", error_message); - meta_screen_cast_stream_src_notify_closed (src); - break; - case PW_REMOTE_STATE_CONNECTED: - pipewire_stream = create_pipewire_stream (src, &error); - if (!pipewire_stream) - { - g_warning ("Could not create pipewire stream: %s", error->message); - g_error_free (error); - meta_screen_cast_stream_src_notify_closed (src); - } - else - { - priv->pipewire_stream = pipewire_stream; - } - break; - case PW_REMOTE_STATE_UNCONNECTED: - case PW_REMOTE_STATE_CONNECTING: - break; - } + g_warning ("pipewire remote error: id:%u %s", id, message); + + if (id == PW_ID_CORE && res == -EPIPE) + meta_screen_cast_stream_src_notify_closed (src); } static gboolean @@ -793,17 +753,6 @@ static GSourceFuncs pipewire_source_funcs = pipewire_loop_source_finalize }; -static void -init_spa_type (MetaSpaType *type, - struct spa_type_map *map) -{ - spa_type_media_type_map (map, &type->media_type); - spa_type_media_subtype_map (map, &type->media_subtype); - spa_type_format_video_map (map, &type->format_video); - spa_type_video_format_map (map, &type->video_format); - type->meta_cursor = spa_type_map_get_id(map, SPA_TYPE_META__Cursor); -} - static MetaPipeWireSource * create_pipewire_source (void) { @@ -829,9 +778,9 @@ create_pipewire_source (void) return pipewire_source; } -static const struct pw_remote_events remote_events = { - PW_VERSION_REMOTE_EVENTS, - .state_changed = on_state_changed, +static const struct pw_core_events core_events = { + PW_VERSION_CORE_EVENTS, + .error = on_core_error, }; static gboolean @@ -851,37 +800,31 @@ meta_screen_cast_stream_src_initable_init (GInitable *initable, return FALSE; } - priv->pipewire_core = pw_core_new (priv->pipewire_source->pipewire_loop, - NULL); + priv->pipewire_context = pw_context_new (priv->pipewire_source->pipewire_loop, + NULL, 0); + if (!priv->pipewire_context) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to create pipewire context"); + return FALSE; + } + + priv->pipewire_core = pw_context_connect (priv->pipewire_context, NULL, 0); if (!priv->pipewire_core) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Failed to create pipewire core"); + "Couldn't connect pipewire context"); return FALSE; } - priv->pipewire_remote = pw_remote_new (priv->pipewire_core, NULL, 0); - if (!priv->pipewire_remote) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Couldn't creat pipewire remote"); - return FALSE; - } + pw_core_add_listener (priv->pipewire_core, + &priv->pipewire_core_listener, + &core_events, + src); - pw_remote_add_listener (priv->pipewire_remote, - &priv->pipewire_remote_listener, - &remote_events, - src); - - priv->pipewire_type = pw_core_get_type (priv->pipewire_core); - init_spa_type (&priv->spa_type, priv->pipewire_type->map); - - if (pw_remote_connect (priv->pipewire_remote) != 0) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Couldn't connect pipewire remote"); - return FALSE; - } + priv->pipewire_stream = create_pipewire_stream (src, error); + if (!priv->pipewire_stream) + return FALSE; return TRUE; } @@ -912,8 +855,8 @@ meta_screen_cast_stream_src_finalize (GObject *object) meta_screen_cast_stream_src_disable (src); g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy); - g_clear_pointer (&priv->pipewire_remote, pw_remote_destroy); - g_clear_pointer (&priv->pipewire_core, pw_core_destroy); + g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); + g_clear_pointer (&priv->pipewire_context, pw_context_destroy); g_source_destroy (&priv->pipewire_source->base); G_OBJECT_CLASS (meta_screen_cast_stream_src_parent_class)->finalize (object);