/* GStreamer */ /* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ /* SPDX-FileCopyrightText: Copyright © 2024 Collabora Ltd. */ /* SPDX-License-Identifier: MIT */ #include "gstpipewirestream.h" #include "gstpipewirepool.h" #include "gstpipewireclock.h" GST_DEBUG_CATEGORY_STATIC (pipewire_stream_debug); #define GST_CAT_DEFAULT pipewire_stream_debug G_DEFINE_TYPE (GstPipeWireStream, gst_pipewire_stream, GST_TYPE_OBJECT) static void gst_pipewire_stream_init (GstPipeWireStream * self) { self->fd = -1; self->client_name = g_strdup (pw_get_client_name()); self->pool = gst_pipewire_pool_new (self); spa_dll_init(&self->dll); } static void gst_pipewire_stream_finalize (GObject * object) { GstPipeWireStream * self = GST_PIPEWIRE_STREAM (object); g_clear_object (&self->pool); g_free (self->path); g_free (self->target_object); g_free (self->client_name); gst_clear_structure (&self->client_properties); gst_clear_structure (&self->stream_properties); G_OBJECT_CLASS(gst_pipewire_stream_parent_class)->finalize (object); } void gst_pipewire_stream_class_init (GstPipeWireStreamClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = gst_pipewire_stream_finalize; GST_DEBUG_CATEGORY_INIT (pipewire_stream_debug, "pipewirestream", 0, "PipeWire Stream"); } GstPipeWireStream * gst_pipewire_stream_new (GstElement * element) { GstPipeWireStream *stream; stream = g_object_new (GST_TYPE_PIPEWIRE_STREAM, NULL); stream->element = element; return stream; } static gboolean copy_properties (GQuark field_id, const GValue *value, gpointer user_data) { struct pw_properties *properties = user_data; GValue dst = { 0 }; if (g_value_type_transformable (G_VALUE_TYPE(value), G_TYPE_STRING)) { g_value_init (&dst, G_TYPE_STRING); if (g_value_transform (value, &dst)) { pw_properties_set (properties, g_quark_to_string (field_id), g_value_get_string (&dst)); } g_value_unset (&dst); } return TRUE; } gboolean gst_pipewire_stream_open (GstPipeWireStream * self, const struct pw_stream_events * pwstream_events) { struct pw_properties *props; g_return_val_if_fail (self->core == NULL, FALSE); GST_DEBUG_OBJECT (self, "open"); /* acquire the core */ self->core = gst_pipewire_core_get (self->fd); if (self->core == NULL) goto connect_error; pw_thread_loop_lock (self->core->loop); /* update the client properties */ if (self->client_properties) { props = pw_properties_new (NULL, NULL); gst_structure_foreach (self->client_properties, copy_properties, props); pw_core_update_properties (self->core->core, &props->dict); pw_properties_free (props); } /* create stream */ props = pw_properties_new (NULL, NULL); if (self->client_name) { pw_properties_set (props, PW_KEY_NODE_NAME, self->client_name); pw_properties_set (props, PW_KEY_NODE_DESCRIPTION, self->client_name); } if (self->stream_properties) { gst_structure_foreach (self->stream_properties, copy_properties, props); } if ((self->pwstream = pw_stream_new (self->core->core, self->client_name, props)) == NULL) goto no_stream; pw_stream_add_listener(self->pwstream, &self->pwstream_listener, pwstream_events, self->element); /* create clock */ self->clock = gst_pipewire_clock_new (self, 0); pw_thread_loop_unlock (self->core->loop); return TRUE; /* ERRORS */ connect_error: { GST_ELEMENT_ERROR (self->element, RESOURCE, FAILED, ("Failed to connect"), (NULL)); return FALSE; } no_stream: { GST_ELEMENT_ERROR (self->element, RESOURCE, FAILED, ("can't create stream"), (NULL)); pw_thread_loop_unlock (self->core->loop); return FALSE; } } void gst_pipewire_stream_close (GstPipeWireStream * self) { GST_DEBUG_OBJECT (self, "close"); /* destroy the clock */ gst_element_post_message (GST_ELEMENT (self->element), gst_message_new_clock_lost (GST_OBJECT_CAST (self->element), self->clock)); g_weak_ref_set (&GST_PIPEWIRE_CLOCK (self->clock)->stream, NULL); g_clear_object (&self->clock); /* destroy the pw stream */ pw_thread_loop_lock (self->core->loop); if (self->pwstream) { /* Do not use g_clear_pointer() here as pw_stream_destroy() may chain up to * code requiring the pointer to still be around */ pw_stream_destroy (self->pwstream); self->pwstream = NULL; } pw_thread_loop_unlock (self->core->loop); /* release the core */ g_clear_pointer (&self->core, gst_pipewire_core_release); }