diff --git a/src/client/properties.c b/src/client/properties.c index 2f2276d0f..f02c4413b 100644 --- a/src/client/properties.c +++ b/src/client/properties.c @@ -76,7 +76,7 @@ pinos_properties_copy (PinosProperties *properties) g_return_val_if_fail (properties != NULL, NULL); - copy = pinos_properties_new (NULL); + copy = pinos_properties_new (NULL, NULL); g_hash_table_foreach (properties->hashtable, (GHFunc) copy_func, copy->hashtable); return copy; @@ -97,6 +97,15 @@ pinos_properties_free (PinosProperties *properties) g_free (properties); } +/** + * pinos_properties_set: + * @properties: a #PinosProperties + * @key: a key + * @value: a value + * + * Set the property in @properties with @key to @value. Any previous value + * of @key will be overwritten. + */ void pinos_properties_set (PinosProperties *properties, const gchar *key, @@ -109,6 +118,44 @@ pinos_properties_set (PinosProperties *properties, g_hash_table_replace (properties->hashtable, g_strdup (key), g_strdup (value)); } +/** + * pinos_properties_setf: + * @properties: a #PinosProperties + * @key: a key + * @format: a value + * @...: extra arguments + * + * Set the property in @properties with @key to the value in printf style @format + * Any previous value of @key will be overwritten. + */ +void +pinos_properties_setf (PinosProperties *properties, + const gchar *key, + const gchar *format, + ...) +{ + va_list varargs; + + g_return_if_fail (properties != NULL); + g_return_if_fail (key != NULL); + g_return_if_fail (format != NULL); + + va_start (varargs, format); + g_hash_table_replace (properties->hashtable, + g_strdup (key), + g_strdup_vprintf (format, varargs)); + va_end (varargs); +} + +/** + * pinos_properties_get: + * @properties: a #PinosProperties + * @key: a key + * + * Get the property in @properties with @key. + * + * Returns: the property for @key or %NULL when the key was not found + */ const gchar * pinos_properties_get (PinosProperties *properties, const gchar *key) @@ -118,6 +165,14 @@ pinos_properties_get (PinosProperties *properties, return g_hash_table_lookup (properties->hashtable, key); } + +/** + * pinos_properties_remove: + * @properties: a #PinosProperties + * @key: a key + * + * Remove the property in @properties with @key. + */ void pinos_properties_remove (PinosProperties *properties, const gchar *key) @@ -187,7 +242,7 @@ pinos_properties_from_variant (GVariant *variant) g_return_val_if_fail (variant != NULL, NULL); - props = pinos_properties_new (NULL); + props = pinos_properties_new (NULL, NULL); g_variant_iter_init (&iter, variant); while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) diff --git a/src/client/properties.h b/src/client/properties.h index ea52d1d0e..42080fd79 100644 --- a/src/client/properties.h +++ b/src/client/properties.h @@ -29,13 +29,17 @@ typedef struct _PinosProperties PinosProperties; #define PINOS_TYPE_PROPERTIES (pinos_properties_get_type()) GType pinos_properties_get_type (void); -PinosProperties * pinos_properties_new (const gchar *key, ...); +PinosProperties * pinos_properties_new (const gchar *key, ...) G_GNUC_NULL_TERMINATED; PinosProperties * pinos_properties_copy (PinosProperties *properties); void pinos_properties_free (PinosProperties *properties); void pinos_properties_set (PinosProperties *properties, const gchar *key, const gchar *value); +void pinos_properties_setf (PinosProperties *properties, + const gchar *key, + const gchar *format, + ...) G_GNUC_PRINTF (3, 4); const gchar * pinos_properties_get (PinosProperties *properties, const gchar *key); void pinos_properties_remove (PinosProperties *properties, diff --git a/src/client/stream.c b/src/client/stream.c index 331bdc4f1..c632dbf32 100644 --- a/src/client/stream.c +++ b/src/client/stream.c @@ -474,6 +474,15 @@ on_source_output_proxy (GObject *source_object, g_object_notify (G_OBJECT (stream), "possible-formats"); } + v = g_dbus_proxy_get_cached_property (priv->source_output, "Properties"); + if (v) { + if (priv->properties) + pinos_properties_free (priv->properties); + priv->properties = pinos_properties_from_variant (v); + g_variant_unref (v); + + g_object_notify (G_OBJECT (stream), "properties"); + } stream_set_state (stream, PINOS_STREAM_STATE_READY); g_object_unref (stream); diff --git a/src/modules/gst/gst-manager.c b/src/modules/gst/gst-manager.c index c5dacff62..82e512e21 100644 --- a/src/modules/gst/gst-manager.c +++ b/src/modules/gst/gst-manager.c @@ -67,23 +67,33 @@ device_added (PinosGstManager *manager, PinosSource *source; GstStructure *p; PinosProperties *properties; + GstCaps *caps; name = gst_device_get_display_name (device); if (strcmp (name, "gst") == 0) return; + caps = gst_device_get_caps (device); + g_print("Device added: %s\n", name); - properties = pinos_properties_new (NULL); + properties = pinos_properties_new (NULL, NULL); if ((p = gst_device_get_properties (device))) gst_structure_foreach (p, copy_properties, properties); + pinos_properties_set (properties, "gstreamer.device.class", gst_device_get_device_class (device)); element = gst_device_create_element (device, NULL); - source = pinos_gst_source_new (priv->daemon, name, properties, element); + source = pinos_gst_source_new (priv->daemon, + name, + properties, + element, + caps); g_object_set_data (G_OBJECT (device), "PinosSource", source); + + gst_caps_unref (caps); g_free (name); } diff --git a/src/modules/gst/gst-source.c b/src/modules/gst/gst-source.c index 8523e233b..e9623e48f 100644 --- a/src/modules/gst/gst-source.c +++ b/src/modules/gst/gst-source.c @@ -21,6 +21,8 @@ #include #include +#include + #include "gst-source.h" #define PINOS_GST_SOURCE_GET_PRIVATE(obj) \ @@ -34,11 +36,18 @@ struct _PinosGstSourcePrivate GstElement *sink; GstCaps *possible_formats; + + GstNetTimeProvider *provider; + + PinosProperties *props; + + gint n_outputs; }; enum { PROP_0, PROP_ELEMENT, + PROP_POSSIBLE_FORMATS }; G_DEFINE_TYPE (PinosGstSource, pinos_gst_source, PINOS_TYPE_SOURCE); @@ -65,20 +74,49 @@ bus_handler (GstBus *bus, gst_element_set_state (priv->pipeline, GST_STATE_NULL); break; } + case GST_MESSAGE_NEW_CLOCK: + { + GstClock *clock; + PinosProperties *props; + + gst_message_parse_new_clock (message, &clock); + GST_INFO ("got new clock %s", GST_OBJECT_NAME (clock)); + + g_object_get (source, "properties", &props, NULL); + pinos_properties_set (props, "gst.pipeline.clock", GST_OBJECT_NAME (clock)); + g_object_set (source, "properties", props, NULL); + pinos_properties_free (props); + break; + } + case GST_MESSAGE_CLOCK_LOST: + { + GstClock *clock; + PinosProperties *props; + + gst_message_parse_new_clock (message, &clock); + GST_INFO ("clock lost %s", GST_OBJECT_NAME (clock)); + + g_object_get (source, "properties", &props, NULL); + pinos_properties_remove (props, "gst.pipeline.clock"); + g_object_set (source, "properties", props, NULL); + pinos_properties_free (props); + + gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); + gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); + break; + } default: break; } return TRUE; } -static void -setup_pipeline (PinosGstSource *source) +static gboolean +setup_pipeline (PinosGstSource *source, GError **error) { PinosGstSourcePrivate *priv = source->priv; GstBus *bus; GstElement *elem; - GstCaps *res; - GstQuery *query; priv->pipeline = gst_pipeline_new (NULL); @@ -107,13 +145,7 @@ setup_pipeline (PinosGstSource *source) gst_bus_add_watch (bus, bus_handler, source); gst_object_unref (bus); - gst_element_set_state (priv->pipeline, GST_STATE_READY); - query = gst_query_new_caps (NULL); - gst_element_query (priv->element, query); - gst_query_parse_caps_result (query, &res); - priv->possible_formats = gst_caps_ref (res); - gst_query_unref (query); - gst_element_set_state (priv->pipeline, GST_STATE_NULL); + return TRUE; } static void @@ -128,6 +160,44 @@ destroy_pipeline (PinosGstSource *source) } } +static gboolean +start_pipeline (PinosGstSource *source, GError **error) +{ + PinosGstSourcePrivate *priv = source->priv; + GstCaps *res; + GstQuery *query; + GstClock *clock; + gchar *address; + gint port; + + gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); + gst_element_get_state (priv->pipeline, NULL, NULL, -1); + + query = gst_query_new_caps (NULL); + gst_element_query (priv->element, query); + gst_query_parse_caps_result (query, &res); + priv->possible_formats = gst_caps_ref (res); + gst_query_unref (query); + + clock = gst_pipeline_get_clock (GST_PIPELINE (priv->pipeline)); + + if (priv->provider) + g_object_unref (priv->provider); + priv->provider = gst_net_time_provider_new (clock, NULL, 0); + + g_object_get (priv->provider, "address", &address, "port", &port, NULL); + + pinos_properties_set (priv->props, "pinos.clock.type", "gst.net.time.provider"); + pinos_properties_set (priv->props, "pinos.clock.source", GST_OBJECT_NAME (clock)); + pinos_properties_set (priv->props, "pinos.clock.address", address); + pinos_properties_setf (priv->props, "pinos.clock.port", "%d", port); + + g_free (address); + gst_object_unref (clock); + + return TRUE; +} + static gboolean set_state (PinosSource *source, PinosSourceState state) @@ -290,11 +360,21 @@ create_source_output (PinosSource *source, const gchar *prefix, GError **error) { + PinosGstSource *s = PINOS_GST_SOURCE (source); + PinosGstSourcePrivate *priv = s->priv; PinosSourceOutput *output; + gpointer state = NULL; + const char *key, *val; - format_filter = get_formats (source, format_filter, error); - if (format_filter == NULL) - return NULL; + if (priv->n_outputs == 0) { + if (!start_pipeline (s, error)) + return NULL; + } + + while ((key = pinos_properties_iterate (priv->props, &state))) { + val = pinos_properties_get (priv->props, key); + pinos_properties_set (props, key, val); + } output = PINOS_SOURCE_CLASS (pinos_gst_source_parent_class) ->create_source_output (source, @@ -303,12 +383,15 @@ create_source_output (PinosSource *source, props, prefix, error); - g_bytes_unref (format_filter); - if (output == NULL) return NULL; - g_signal_connect (output, "notify::socket", (GCallback) on_socket_notify, source); + g_signal_connect (output, + "notify::socket", + (GCallback) on_socket_notify, + source); + + priv->n_outputs++; return output; } @@ -317,7 +400,8 @@ static gboolean release_source_output (PinosSource *source, PinosSourceOutput *output) { - return PINOS_SOURCE_CLASS (pinos_gst_source_parent_class)->release_source_output (source, output); + return PINOS_SOURCE_CLASS (pinos_gst_source_parent_class) + ->release_source_output (source, output); } static void @@ -334,6 +418,10 @@ get_property (GObject *object, g_value_set_object (value, priv->element); break; + case PROP_POSSIBLE_FORMATS: + g_value_set_boxed (value, priv->possible_formats); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -354,6 +442,10 @@ set_property (GObject *object, priv->element = g_value_dup_object (value); break; + case PROP_POSSIBLE_FORMATS: + priv->possible_formats = g_value_dup_boxed (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -365,7 +457,7 @@ source_constructed (GObject * object) { PinosGstSource *source = PINOS_GST_SOURCE (object); - setup_pipeline (source); + setup_pipeline (source, NULL); G_OBJECT_CLASS (pinos_gst_source_parent_class)->constructed (object); } @@ -374,8 +466,10 @@ static void source_finalize (GObject * object) { PinosGstSource *source = PINOS_GST_SOURCE (object); + PinosGstSourcePrivate *priv = source->priv; destroy_pipeline (source); + pinos_properties_free (priv->props); G_OBJECT_CLASS (pinos_gst_source_parent_class)->finalize (object); } @@ -402,7 +496,15 @@ pinos_gst_source_class_init (PinosGstSourceClass * klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - + g_object_class_install_property (gobject_class, + PROP_POSSIBLE_FORMATS, + g_param_spec_boxed ("possible-formats", + "Possible Formats", + "The possible formats", + GST_TYPE_CAPS, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); source_class->get_formats = get_formats; source_class->set_state = set_state; @@ -413,19 +515,28 @@ pinos_gst_source_class_init (PinosGstSourceClass * klass) static void pinos_gst_source_init (PinosGstSource * source) { - source->priv = PINOS_GST_SOURCE_GET_PRIVATE (source); + PinosGstSourcePrivate *priv; + + priv = source->priv = PINOS_GST_SOURCE_GET_PRIVATE (source); + priv->props = pinos_properties_new (NULL, NULL); } PinosSource * pinos_gst_source_new (PinosDaemon *daemon, const gchar *name, PinosProperties *properties, - GstElement *element) + GstElement *element, + GstCaps *caps) { - return g_object_new (PINOS_TYPE_GST_SOURCE, - "daemon", daemon, - "name", name, - "properties", properties, - "element", element, - NULL); + PinosSource *source; + + source = g_object_new (PINOS_TYPE_GST_SOURCE, + "daemon", daemon, + "name", name, + "properties", properties, + "element", element, + "possible-formats", caps, + NULL); + + return source; } diff --git a/src/modules/gst/gst-source.h b/src/modules/gst/gst-source.h index e09a5b65f..200d3fcbd 100644 --- a/src/modules/gst/gst-source.h +++ b/src/modules/gst/gst-source.h @@ -53,10 +53,11 @@ struct _PinosGstSourceClass { GType pinos_gst_source_get_type (void); -PinosSource * pinos_gst_source_new (PinosDaemon *daemon, - const gchar *name, +PinosSource * pinos_gst_source_new (PinosDaemon *daemon, + const gchar *name, PinosProperties *properties, - GstElement *element); + GstElement *element, + GstCaps *caps); G_END_DECLS diff --git a/src/server/client.c b/src/server/client.c index 2891c9dfd..9bf91f54a 100644 --- a/src/server/client.c +++ b/src/server/client.c @@ -179,8 +179,6 @@ handle_create_source_output (PinosClient1 *interface, if (output == NULL) goto no_output; - object_path = pinos_source_output_get_object_path (output); - priv->outputs = g_list_prepend (priv->outputs, output); g_signal_connect (output, @@ -188,6 +186,7 @@ handle_create_source_output (PinosClient1 *interface, (GCallback) handle_remove_source_output, client); + object_path = pinos_source_output_get_object_path (output); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", object_path));