Improve error reporting

Pass GError around for things that can fail and report the errors back
to the client.

Improve shutdown of pipeline when no clients are consuming.

Make GStreamer elements handle all kinds of data and not just video
because we can.
This commit is contained in:
Wim Taymans 2015-05-15 13:34:32 +02:00
parent 4bc308835a
commit cbe7b52a70
13 changed files with 145 additions and 61 deletions

View file

@ -61,7 +61,7 @@ static GstStaticPadTemplate gst_pulsevideo_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS (PVS_VIDEO_CAPS) GST_STATIC_CAPS_ANY
); );
#define gst_pulsevideo_sink_parent_class parent_class #define gst_pulsevideo_sink_parent_class parent_class

View file

@ -23,8 +23,6 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstbasesink.h> #include <gst/base/gstbasesink.h>
#include <gst/video/video.h>
#include <client/pv-context.h> #include <client/pv-context.h>
#include <client/pv-stream.h> #include <client/pv-stream.h>
#include <client/pv-introspect.h> #include <client/pv-introspect.h>

View file

@ -60,7 +60,7 @@ static GstStaticPadTemplate gst_pulsevideo_src_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC, GST_PAD_SRC,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS (PVS_VIDEO_CAPS) GST_STATIC_CAPS_ANY
); );
#define gst_pulsevideo_src_parent_class parent_class #define gst_pulsevideo_src_parent_class parent_class
@ -238,7 +238,7 @@ gst_pulsevideo_src_negotiate (GstBaseSrc * basesrc)
thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL); thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL);
GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps); GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
/* nothing or anything is allowed, we're done */ /* nothing or anything is allowed, we're done */
if (thiscaps == NULL || gst_caps_is_any (thiscaps)) if (thiscaps == NULL)
goto no_nego_needed; goto no_nego_needed;
if (G_UNLIKELY (gst_caps_is_empty (thiscaps))) if (G_UNLIKELY (gst_caps_is_empty (thiscaps)))

View file

@ -23,8 +23,6 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstpushsrc.h> #include <gst/base/gstpushsrc.h>
#include <gst/video/video.h>
#include <client/pv-context.h> #include <client/pv-context.h>
#include <client/pv-stream.h> #include <client/pv-stream.h>
#include <client/pv-introspect.h> #include <client/pv-introspect.h>

View file

@ -34,8 +34,6 @@ struct _PvV4l2SourcePrivate
GstElement *sink; GstElement *sink;
GstCaps *possible_formats; GstCaps *possible_formats;
GSocket *socket;
}; };
G_DEFINE_TYPE (PvV4l2Source, pv_v4l2_source, PV_TYPE_SOURCE); G_DEFINE_TYPE (PvV4l2Source, pv_v4l2_source, PV_TYPE_SOURCE);
@ -91,6 +89,8 @@ setup_pipeline (PvV4l2Source *source)
bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline)); bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
gst_bus_add_watch (bus, bus_handler, source); gst_bus_add_watch (bus, bus_handler, source);
gst_object_unref (bus); gst_object_unref (bus);
gst_element_set_state (priv->pipeline, GST_STATE_READY);
} }
static GstCaps * static GstCaps *
@ -101,7 +101,7 @@ collect_caps (PvSource * source, GstCaps *filter)
GstQuery *query; GstQuery *query;
query = gst_query_new_caps (filter); query = gst_query_new_caps (filter);
gst_element_query (priv->src, query); gst_element_query (priv->filter, query);
gst_query_parse_caps_result (query, &res); gst_query_parse_caps_result (query, &res);
gst_caps_ref (res); gst_caps_ref (res);
gst_query_unref (query); gst_query_unref (query);
@ -154,36 +154,57 @@ on_socket_notify (GObject *gobject,
GSocket *socket; GSocket *socket;
guint num_handles; guint num_handles;
GstCaps *caps; GstCaps *caps;
GBytes *requested_format; GBytes *requested_format, *format;
g_object_get (gobject, "socket", &socket, NULL); g_object_get (gobject, "socket", &socket, NULL);
g_print ("source socket %p\n", socket); g_print ("source socket %p\n", socket);
if (socket == NULL) { if (socket == NULL) {
if (priv->socket) GSocket *prev_socket = g_object_get_data (gobject, "last-socket");
g_signal_emit_by_name (priv->sink, "remove", priv->socket); if (prev_socket) {
g_signal_emit_by_name (priv->sink, "remove", prev_socket);
}
} else { } else {
g_signal_emit_by_name (priv->sink, "add", socket); g_signal_emit_by_name (priv->sink, "add", socket);
} }
priv->socket = socket; g_object_set_data (gobject, "last-socket", socket);
g_object_get (gobject, "requested-format", &requested_format, NULL);
g_assert (requested_format != NULL);
g_object_set (gobject, "format", requested_format, NULL);
g_print ("final format %s\n", g_bytes_get_data (requested_format, NULL));
caps = gst_caps_from_string (g_bytes_get_data (requested_format, NULL));
g_assert (caps != NULL);
g_object_set (priv->filter, "caps", caps, NULL);
gst_caps_unref (caps);
g_bytes_unref (requested_format);
g_object_get (priv->sink, "num-handles", &num_handles, NULL); g_object_get (priv->sink, "num-handles", &num_handles, NULL);
g_print ("num handles %d\n", num_handles);
if (num_handles == 0) { if (num_handles == 0) {
gst_element_set_state (priv->pipeline, GST_STATE_READY); gst_element_set_state (priv->pipeline, GST_STATE_READY);
} else { g_object_set (priv->filter, "caps", NULL, NULL);
} else if (socket) {
/* what client requested */
g_object_get (gobject, "requested-format", &requested_format, NULL);
g_assert (requested_format != NULL);
if (num_handles == 1) {
/* first client, we set the requested format as the format */
format = requested_format;
/* set on the filter */
caps = gst_caps_from_string (g_bytes_get_data (format, NULL));
g_assert (caps != NULL);
g_object_set (priv->filter, "caps", caps, NULL);
gst_caps_unref (caps);
} else {
gchar *str;
/* we already have a client, format is whatever is configured already */
g_bytes_unref (requested_format);
g_object_get (priv->filter, "caps", &caps, NULL);
str = gst_caps_to_string (caps);
format = g_bytes_new (str, strlen (str) + 1);
gst_caps_unref (caps);
}
/* this is what we use as the final format for the output */
g_print ("final format %s\n", (gchar *) g_bytes_get_data (format, NULL));
g_object_set (gobject, "format", format, NULL);
g_bytes_unref (format);
gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
} }
} }
@ -192,9 +213,9 @@ static PvSourceOutput *
v4l2_create_source_output (PvSource *source, v4l2_create_source_output (PvSource *source,
const gchar *client_path, const gchar *client_path,
GBytes *format_filter, GBytes *format_filter,
const gchar *prefix) const gchar *prefix,
GError **error)
{ {
PvV4l2SourcePrivate *priv = PV_V4L2_SOURCE (source)->priv;
PvSourceOutput *output; PvSourceOutput *output;
GstCaps *caps, *filtered; GstCaps *caps, *filtered;
gchar *str; gchar *str;
@ -205,8 +226,6 @@ v4l2_create_source_output (PvSource *source,
if (caps == NULL) if (caps == NULL)
goto invalid_caps; goto invalid_caps;
gst_element_set_state (priv->pipeline, GST_STATE_READY);
filtered = collect_caps (source, caps); filtered = collect_caps (source, caps);
if (filtered == NULL || gst_caps_is_empty (filtered)) if (filtered == NULL || gst_caps_is_empty (filtered))
goto no_format; goto no_format;
@ -215,7 +234,14 @@ v4l2_create_source_output (PvSource *source,
g_print ("output filter %s\n", str); g_print ("output filter %s\n", str);
format_filter = g_bytes_new_take (str, strlen (str) + 1); format_filter = g_bytes_new_take (str, strlen (str) + 1);
output = PV_SOURCE_CLASS (pv_v4l2_source_parent_class)->create_source_output (source, client_path, format_filter, prefix); output = PV_SOURCE_CLASS (pv_v4l2_source_parent_class)
->create_source_output (source,
client_path,
format_filter,
prefix,
error);
if (error == 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);
@ -224,10 +250,18 @@ v4l2_create_source_output (PvSource *source,
/* ERRORS */ /* ERRORS */
invalid_caps: invalid_caps:
{ {
if (error)
*error = g_error_new (G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"Input filter data invalid");
return NULL; return NULL;
} }
no_format: no_format:
{ {
if (error)
*error = g_error_new (G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"No format available that matches input filter");
return NULL; return NULL;
} }
} }

View file

@ -184,7 +184,8 @@ static PvSourceOutput *
client_create_source_output (PvSource *source, client_create_source_output (PvSource *source,
const gchar *client_path, const gchar *client_path,
GBytes *format_filter, GBytes *format_filter,
const gchar *prefix) const gchar *prefix,
GError **error)
{ {
PvClientSourcePrivate *priv = PV_CLIENT_SOURCE (source)->priv; PvClientSourcePrivate *priv = PV_CLIENT_SOURCE (source)->priv;
PvSourceOutput *output; PvSourceOutput *output;
@ -192,7 +193,15 @@ client_create_source_output (PvSource *source,
/* propose format of input */ /* propose format of input */
g_object_get (priv->input, "format", &format_filter, NULL); g_object_get (priv->input, "format", &format_filter, NULL);
output = PV_SOURCE_CLASS (pv_client_source_parent_class)->create_source_output (source, client_path, format_filter, prefix); output = PV_SOURCE_CLASS (pv_client_source_parent_class)
->create_source_output (source,
client_path,
format_filter,
prefix,
error);
if (output == NULL)
return NULL;
gst_element_set_state (priv->pipeline, GST_STATE_READY); gst_element_set_state (priv->pipeline, GST_STATE_READY);
@ -246,7 +255,8 @@ PvSourceOutput *
pv_client_source_get_source_input (PvClientSource *source, pv_client_source_get_source_input (PvClientSource *source,
const gchar *client_path, const gchar *client_path,
GBytes *format_filter, GBytes *format_filter,
const gchar *prefix) const gchar *prefix,
GError **error)
{ {
PvClientSourcePrivate *priv; PvClientSourcePrivate *priv;
@ -254,7 +264,15 @@ pv_client_source_get_source_input (PvClientSource *source,
priv = source->priv; priv = source->priv;
if (priv->input == NULL) { if (priv->input == NULL) {
priv->input = PV_SOURCE_CLASS (pv_client_source_parent_class)->create_source_output (PV_SOURCE (source), client_path, format_filter, prefix); priv->input = PV_SOURCE_CLASS (pv_client_source_parent_class)
->create_source_output (PV_SOURCE (source),
client_path,
format_filter,
prefix,
error);
if (priv->input == NULL)
return NULL;
g_signal_connect (priv->input, "notify::socket", (GCallback) on_input_socket_notify, source); g_signal_connect (priv->input, "notify::socket", (GCallback) on_input_socket_notify, source);
} }
return priv->input; return priv->input;

View file

@ -68,7 +68,8 @@ PvSource * pv_client_source_new (PvDaemon *daemon);
PvSourceOutput * pv_client_source_get_source_input (PvClientSource *source, PvSourceOutput * pv_client_source_get_source_input (PvClientSource *source,
const gchar *client_path, const gchar *client_path,
GBytes *format_filter, GBytes *format_filter,
const gchar *prefix); const gchar *prefix,
GError **error);
G_END_DECLS G_END_DECLS

View file

@ -130,14 +130,23 @@ handle_create_source_output (PvClient1 *interface,
PvSourceOutput *output; PvSourceOutput *output;
const gchar *object_path, *sender; const gchar *object_path, *sender;
GBytes *formats; GBytes *formats;
GError *error = NULL;
formats = g_bytes_new (arg_accepted_formats, strlen (arg_accepted_formats) + 1); formats = g_bytes_new (arg_accepted_formats, strlen (arg_accepted_formats) + 1);
source = pv_daemon_find_source (priv->daemon, arg_source, priv->properties, formats); source = pv_daemon_find_source (priv->daemon,
arg_source,
priv->properties,
formats,
&error);
if (source == NULL) if (source == NULL)
goto no_source; goto no_source;
output = pv_source_create_source_output (source, priv->object_path, formats, priv->object_path); output = pv_source_create_source_output (source,
priv->object_path,
formats,
priv->object_path,
&error);
if (output == NULL) if (output == NULL)
goto no_output; goto no_output;
@ -154,14 +163,16 @@ handle_create_source_output (PvClient1 *interface,
/* ERRORS */ /* ERRORS */
no_source: no_source:
{ {
g_dbus_method_invocation_return_dbus_error (invocation, g_dbus_method_invocation_return_gerror (invocation, error);
"org.pulsevideo.Error", "Can't create source"); g_clear_error (&error);
g_bytes_unref (formats);
return TRUE; return TRUE;
} }
no_output: no_output:
{ {
g_dbus_method_invocation_return_dbus_error (invocation, g_dbus_method_invocation_return_gerror (invocation, error);
"org.pulsevideo.Error", "Can't create output"); g_clear_error (&error);
g_bytes_unref (formats);
return TRUE; return TRUE;
} }
} }
@ -178,6 +189,7 @@ handle_create_source_input (PvClient1 *interface,
PvSourceOutput *input; PvSourceOutput *input;
const gchar *source_input_path, *sender; const gchar *source_input_path, *sender;
GBytes *formats; GBytes *formats;
GError *error = NULL;
source = pv_client_source_new (priv->daemon); source = pv_client_source_new (priv->daemon);
if (source == NULL) if (source == NULL)
@ -192,7 +204,8 @@ handle_create_source_input (PvClient1 *interface,
input = pv_client_source_get_source_input (PV_CLIENT_SOURCE (source), input = pv_client_source_get_source_input (PV_CLIENT_SOURCE (source),
priv->object_path, priv->object_path,
formats, formats,
priv->object_path); priv->object_path,
&error);
if (input == NULL) if (input == NULL)
goto no_input; goto no_input;
@ -214,8 +227,9 @@ no_source:
} }
no_input: no_input:
{ {
g_dbus_method_invocation_return_dbus_error (invocation, g_dbus_method_invocation_return_gerror (invocation, error);
"org.pulsevideo.Error", "Can't create input"); g_clear_error (&error);
g_bytes_unref (formats);
return TRUE; return TRUE;
} }
} }

View file

@ -78,8 +78,6 @@ client_name_appeared_handler (GDBusConnection *connection,
SenderData *data = user_data; SenderData *data = user_data;
PvDaemonPrivate *priv = data->daemon->priv; PvDaemonPrivate *priv = data->daemon->priv;
g_print ("client name appeared def: %p\n", g_main_context_get_thread_default ());
g_hash_table_insert (priv->senders, data->sender, data); g_hash_table_insert (priv->senders, data->sender, data);
if (!g_strcmp0 (name, g_dbus_connection_get_unique_name (connection))) if (!g_strcmp0 (name, g_dbus_connection_get_unique_name (connection)))
@ -121,7 +119,6 @@ sender_data_new (PvDaemon *daemon, const gchar *sender)
data->daemon = daemon; data->daemon = daemon;
data->sender = g_strdup (sender); data->sender = g_strdup (sender);
g_print ("watch name def: %p\n", g_main_context_get_thread_default ());
g_print ("watch name %s %p\n", sender, data); g_print ("watch name %s %p\n", sender, data);
data->id = g_bus_watch_name_on_connection (priv->connection, data->id = g_bus_watch_name_on_connection (priv->connection,
@ -371,15 +368,21 @@ PvSource *
pv_daemon_find_source (PvDaemon *daemon, pv_daemon_find_source (PvDaemon *daemon,
const gchar *name, const gchar *name,
GVariant *props, GVariant *props,
GBytes *format_filter) GBytes *format_filter,
GError **error)
{ {
PvDaemonPrivate *priv; PvDaemonPrivate *priv;
g_return_val_if_fail (PV_IS_DAEMON (daemon), NULL); g_return_val_if_fail (PV_IS_DAEMON (daemon), NULL);
priv = daemon->priv; priv = daemon->priv;
if (priv->sources == NULL) if (priv->sources == NULL) {
if (error)
*error = g_error_new (G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"No sources registered");
return NULL; return NULL;
}
return priv->sources->data; return priv->sources->data;
} }

View file

@ -78,7 +78,8 @@ void pv_daemon_remove_source (PvDaemon *daemon, PvSource *source
PvSource * pv_daemon_find_source (PvDaemon *daemon, PvSource * pv_daemon_find_source (PvDaemon *daemon,
const gchar *name, const gchar *name,
GVariant *props, GVariant *props,
GBytes *format_filter); GBytes *format_filter,
GError **error);
G_END_DECLS G_END_DECLS

View file

@ -271,14 +271,22 @@ output_unregister_object (PvSourceOutput *output)
pv_daemon_unexport (priv->daemon, priv->object_path); pv_daemon_unexport (priv->daemon, priv->object_path);
} }
static void
pv_source_output_dispose (GObject * object)
{
PvSourceOutput *output = PV_SOURCE_OUTPUT (object);
output_unregister_object (output);
G_OBJECT_CLASS (pv_source_output_parent_class)->dispose (object);
}
static void static void
pv_source_output_finalize (GObject * object) pv_source_output_finalize (GObject * object)
{ {
PvSourceOutput *output = PV_SOURCE_OUTPUT (object); PvSourceOutput *output = PV_SOURCE_OUTPUT (object);
PvSourceOutputPrivate *priv = output->priv; PvSourceOutputPrivate *priv = output->priv;
output_unregister_object (output);
g_object_unref (priv->daemon); g_object_unref (priv->daemon);
g_object_unref (priv->iface); g_object_unref (priv->iface);
g_free (priv->client_path); g_free (priv->client_path);
@ -306,6 +314,7 @@ pv_source_output_class_init (PvSourceOutputClass * klass)
g_type_class_add_private (klass, sizeof (PvSourceOutputPrivate)); g_type_class_add_private (klass, sizeof (PvSourceOutputPrivate));
gobject_class->dispose = pv_source_output_dispose;
gobject_class->finalize = pv_source_output_finalize; gobject_class->finalize = pv_source_output_finalize;
gobject_class->set_property = pv_source_output_set_property; gobject_class->set_property = pv_source_output_set_property;
gobject_class->get_property = pv_source_output_get_property; gobject_class->get_property = pv_source_output_get_property;

View file

@ -210,7 +210,6 @@ default_release_source_output (PvSource *source, PvSourceOutput *output)
return TRUE; return TRUE;
} }
static void static void
pv_source_class_init (PvSourceClass * klass) pv_source_class_init (PvSourceClass * klass)
{ {
@ -354,7 +353,8 @@ PvSourceOutput *
pv_source_create_source_output (PvSource *source, pv_source_create_source_output (PvSource *source,
const gchar *client_path, const gchar *client_path,
GBytes *format_filter, GBytes *format_filter,
const gchar *prefix) const gchar *prefix,
GError **error)
{ {
PvSourceClass *klass; PvSourceClass *klass;
PvSourceOutput *res; PvSourceOutput *res;
@ -363,10 +363,16 @@ pv_source_create_source_output (PvSource *source,
klass = PV_SOURCE_GET_CLASS (source); klass = PV_SOURCE_GET_CLASS (source);
if (klass->create_source_output) if (klass->create_source_output) {
res = klass->create_source_output (source, client_path, format_filter, prefix); res = klass->create_source_output (source, client_path, format_filter, prefix, error);
else } else {
if (error) {
*error = g_error_new (G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"Create SourceOutput not implemented");
}
res = NULL; res = NULL;
}
return res; return res;
} }

View file

@ -71,7 +71,8 @@ struct _PvSourceClass {
PvSourceOutput * (*create_source_output) (PvSource *source, PvSourceOutput * (*create_source_output) (PvSource *source,
const gchar *client_path, const gchar *client_path,
GBytes *format_filter, GBytes *format_filter,
const gchar *prefix); const gchar *prefix,
GError **error);
gboolean (*release_source_output) (PvSource *source, gboolean (*release_source_output) (PvSource *source,
PvSourceOutput *output); PvSourceOutput *output);
}; };
@ -90,7 +91,8 @@ void pv_source_report_error (PvSource *source, GError *err
PvSourceOutput * pv_source_create_source_output (PvSource *source, PvSourceOutput * pv_source_create_source_output (PvSource *source,
const gchar *client_path, const gchar *client_path,
GBytes *format_filter, GBytes *format_filter,
const gchar *prefix); const gchar *prefix,
GError **error);
gboolean pv_source_release_source_output (PvSource *source, PvSourceOutput *output); gboolean pv_source_release_source_output (PvSource *source, PvSourceOutput *output);
G_END_DECLS G_END_DECLS