mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
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:
parent
4bc308835a
commit
cbe7b52a70
13 changed files with 145 additions and 61 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue