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_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (PVS_VIDEO_CAPS)
GST_STATIC_CAPS_ANY
);
#define gst_pulsevideo_sink_parent_class parent_class

View file

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

View file

@ -60,7 +60,7 @@ static GstStaticPadTemplate gst_pulsevideo_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (PVS_VIDEO_CAPS)
GST_STATIC_CAPS_ANY
);
#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);
GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
/* nothing or anything is allowed, we're done */
if (thiscaps == NULL || gst_caps_is_any (thiscaps))
if (thiscaps == NULL)
goto no_nego_needed;
if (G_UNLIKELY (gst_caps_is_empty (thiscaps)))

View file

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

View file

@ -34,8 +34,6 @@ struct _PvV4l2SourcePrivate
GstElement *sink;
GstCaps *possible_formats;
GSocket *socket;
};
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));
gst_bus_add_watch (bus, bus_handler, source);
gst_object_unref (bus);
gst_element_set_state (priv->pipeline, GST_STATE_READY);
}
static GstCaps *
@ -101,7 +101,7 @@ collect_caps (PvSource * source, GstCaps *filter)
GstQuery *query;
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_caps_ref (res);
gst_query_unref (query);
@ -154,36 +154,57 @@ on_socket_notify (GObject *gobject,
GSocket *socket;
guint num_handles;
GstCaps *caps;
GBytes *requested_format;
GBytes *requested_format, *format;
g_object_get (gobject, "socket", &socket, NULL);
g_print ("source socket %p\n", socket);
if (socket == NULL) {
if (priv->socket)
g_signal_emit_by_name (priv->sink, "remove", priv->socket);
GSocket *prev_socket = g_object_get_data (gobject, "last-socket");
if (prev_socket) {
g_signal_emit_by_name (priv->sink, "remove", prev_socket);
}
} else {
g_signal_emit_by_name (priv->sink, "add", socket);
}
priv->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_set_data (gobject, "last-socket", socket);
g_object_get (priv->sink, "num-handles", &num_handles, NULL);
g_print ("num handles %d\n", num_handles);
if (num_handles == 0) {
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);
}
}
@ -192,9 +213,9 @@ static PvSourceOutput *
v4l2_create_source_output (PvSource *source,
const gchar *client_path,
GBytes *format_filter,
const gchar *prefix)
const gchar *prefix,
GError **error)
{
PvV4l2SourcePrivate *priv = PV_V4L2_SOURCE (source)->priv;
PvSourceOutput *output;
GstCaps *caps, *filtered;
gchar *str;
@ -205,8 +226,6 @@ v4l2_create_source_output (PvSource *source,
if (caps == NULL)
goto invalid_caps;
gst_element_set_state (priv->pipeline, GST_STATE_READY);
filtered = collect_caps (source, caps);
if (filtered == NULL || gst_caps_is_empty (filtered))
goto no_format;
@ -215,7 +234,14 @@ v4l2_create_source_output (PvSource *source,
g_print ("output filter %s\n", str);
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);
@ -224,10 +250,18 @@ v4l2_create_source_output (PvSource *source,
/* ERRORS */
invalid_caps:
{
if (error)
*error = g_error_new (G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"Input filter data invalid");
return NULL;
}
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;
}
}

View file

@ -184,7 +184,8 @@ static PvSourceOutput *
client_create_source_output (PvSource *source,
const gchar *client_path,
GBytes *format_filter,
const gchar *prefix)
const gchar *prefix,
GError **error)
{
PvClientSourcePrivate *priv = PV_CLIENT_SOURCE (source)->priv;
PvSourceOutput *output;
@ -192,7 +193,15 @@ client_create_source_output (PvSource *source,
/* propose format of input */
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);
@ -246,7 +255,8 @@ PvSourceOutput *
pv_client_source_get_source_input (PvClientSource *source,
const gchar *client_path,
GBytes *format_filter,
const gchar *prefix)
const gchar *prefix,
GError **error)
{
PvClientSourcePrivate *priv;
@ -254,7 +264,15 @@ pv_client_source_get_source_input (PvClientSource *source,
priv = source->priv;
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);
}
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,
const gchar *client_path,
GBytes *format_filter,
const gchar *prefix);
const gchar *prefix,
GError **error);
G_END_DECLS

View file

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

View file

@ -78,8 +78,6 @@ client_name_appeared_handler (GDBusConnection *connection,
SenderData *data = user_data;
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);
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->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);
data->id = g_bus_watch_name_on_connection (priv->connection,
@ -371,15 +368,21 @@ PvSource *
pv_daemon_find_source (PvDaemon *daemon,
const gchar *name,
GVariant *props,
GBytes *format_filter)
GBytes *format_filter,
GError **error)
{
PvDaemonPrivate *priv;
g_return_val_if_fail (PV_IS_DAEMON (daemon), NULL);
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 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,
const gchar *name,
GVariant *props,
GBytes *format_filter);
GBytes *format_filter,
GError **error);
G_END_DECLS

View file

@ -271,14 +271,22 @@ output_unregister_object (PvSourceOutput *output)
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
pv_source_output_finalize (GObject * object)
{
PvSourceOutput *output = PV_SOURCE_OUTPUT (object);
PvSourceOutputPrivate *priv = output->priv;
output_unregister_object (output);
g_object_unref (priv->daemon);
g_object_unref (priv->iface);
g_free (priv->client_path);
@ -306,6 +314,7 @@ pv_source_output_class_init (PvSourceOutputClass * klass)
g_type_class_add_private (klass, sizeof (PvSourceOutputPrivate));
gobject_class->dispose = pv_source_output_dispose;
gobject_class->finalize = pv_source_output_finalize;
gobject_class->set_property = pv_source_output_set_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;
}
static void
pv_source_class_init (PvSourceClass * klass)
{
@ -354,7 +353,8 @@ PvSourceOutput *
pv_source_create_source_output (PvSource *source,
const gchar *client_path,
GBytes *format_filter,
const gchar *prefix)
const gchar *prefix,
GError **error)
{
PvSourceClass *klass;
PvSourceOutput *res;
@ -363,10 +363,16 @@ pv_source_create_source_output (PvSource *source,
klass = PV_SOURCE_GET_CLASS (source);
if (klass->create_source_output)
res = klass->create_source_output (source, client_path, format_filter, prefix);
else
if (klass->create_source_output) {
res = klass->create_source_output (source, client_path, format_filter, prefix, error);
} else {
if (error) {
*error = g_error_new (G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"Create SourceOutput not implemented");
}
res = NULL;
}
return res;
}

View file

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