mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-17 08:56:49 -05:00
Implement negotiation
Use generic byte blobs for formats. We currently use them to store gstreamer caps but we could also use them to exchange serialized GVariants if we want. Make properties a variant dictionary
This commit is contained in:
parent
ca7e4602f6
commit
4bc308835a
21 changed files with 620 additions and 582 deletions
|
|
@ -138,8 +138,10 @@ pv_context_finalize (GObject * object)
|
||||||
PvContext *context = PV_CONTEXT (object);
|
PvContext *context = PV_CONTEXT (object);
|
||||||
PvContextPrivate *priv = context->priv;
|
PvContextPrivate *priv = context->priv;
|
||||||
|
|
||||||
g_object_unref (priv->server_manager);
|
g_free (priv->name);
|
||||||
g_clear_error (&priv->error);
|
g_clear_error (&priv->error);
|
||||||
|
if (priv->properties)
|
||||||
|
g_variant_unref (priv->properties);
|
||||||
|
|
||||||
G_OBJECT_CLASS (pv_context_parent_class)->finalize (object);
|
G_OBJECT_CLASS (pv_context_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +194,7 @@ pv_context_class_init (PvContextClass * klass)
|
||||||
g_param_spec_variant ("properties",
|
g_param_spec_variant ("properties",
|
||||||
"Properties",
|
"Properties",
|
||||||
"Extra properties",
|
"Extra properties",
|
||||||
G_VARIANT_TYPE_VARIANT,
|
G_VARIANT_TYPE_DICTIONARY,
|
||||||
NULL,
|
NULL,
|
||||||
G_PARAM_READWRITE |
|
G_PARAM_READWRITE |
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
@ -267,7 +269,6 @@ pv_context_init (PvContext * context)
|
||||||
PvContextPrivate *priv = context->priv = PV_CONTEXT_GET_PRIVATE (context);
|
PvContextPrivate *priv = context->priv = PV_CONTEXT_GET_PRIVATE (context);
|
||||||
|
|
||||||
priv->state = PV_CONTEXT_STATE_UNCONNECTED;
|
priv->state = PV_CONTEXT_STATE_UNCONNECTED;
|
||||||
priv->server_manager = g_dbus_object_manager_server_new (PV_DBUS_OBJECT_PREFIX);
|
|
||||||
priv->subscribe = pv_subscribe_new ();
|
priv->subscribe = pv_subscribe_new ();
|
||||||
g_object_set (priv->subscribe, "subscription-mask", PV_SUBSCRIPTION_FLAGS_ALL, NULL);
|
g_object_set (priv->subscribe, "subscription-mask", PV_SUBSCRIPTION_FLAGS_ALL, NULL);
|
||||||
g_signal_connect (priv->subscribe, "subscription-event", (GCallback) subscription_cb, context);
|
g_signal_connect (priv->subscribe, "subscription-event", (GCallback) subscription_cb, context);
|
||||||
|
|
@ -286,6 +287,15 @@ pv_context_init (PvContext * context)
|
||||||
PvContext *
|
PvContext *
|
||||||
pv_context_new (GMainContext *context, const gchar *name, GVariant *properties)
|
pv_context_new (GMainContext *context, const gchar *name, GVariant *properties)
|
||||||
{
|
{
|
||||||
|
g_return_val_if_fail (name != NULL, NULL);
|
||||||
|
|
||||||
|
if (properties == NULL) {
|
||||||
|
GVariantBuilder builder;
|
||||||
|
|
||||||
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
||||||
|
g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string (name));
|
||||||
|
properties = g_variant_builder_end (&builder);
|
||||||
|
}
|
||||||
return g_object_new (PV_TYPE_CONTEXT, "main-context", context, "name", name, "properties", properties, NULL);
|
return g_object_new (PV_TYPE_CONTEXT, "main-context", context, "name", name, "properties", properties, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -330,18 +340,14 @@ on_daemon_connected (GObject *source_object,
|
||||||
{
|
{
|
||||||
PvContext *context = user_data;
|
PvContext *context = user_data;
|
||||||
PvContextPrivate *priv = context->priv;
|
PvContextPrivate *priv = context->priv;
|
||||||
GVariantBuilder builder;
|
|
||||||
|
|
||||||
g_assert (g_main_context_get_thread_default () == priv->context);
|
g_assert (g_main_context_get_thread_default () == priv->context);
|
||||||
|
|
||||||
context_set_state (context, PV_CONTEXT_STATE_REGISTERING);
|
context_set_state (context, PV_CONTEXT_STATE_REGISTERING);
|
||||||
|
|
||||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string ("hello"));
|
|
||||||
|
|
||||||
g_dbus_proxy_call (priv->daemon,
|
g_dbus_proxy_call (priv->daemon,
|
||||||
"ConnectClient",
|
"ConnectClient",
|
||||||
g_variant_new ("(@a{sv})", g_variant_builder_end (&builder)),
|
g_variant_new ("(@a{sv})", priv->properties),
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1,
|
-1,
|
||||||
NULL,
|
NULL,
|
||||||
|
|
@ -436,7 +442,6 @@ on_name_appeared (GDBusConnection *connection,
|
||||||
g_print ("context: on name appeared\n");
|
g_print ("context: on name appeared\n");
|
||||||
|
|
||||||
priv->connection = connection;
|
priv->connection = connection;
|
||||||
g_dbus_object_manager_server_set_connection (priv->server_manager, connection);
|
|
||||||
|
|
||||||
g_object_set (priv->subscribe, "connection", priv->connection,
|
g_object_set (priv->subscribe, "connection", priv->connection,
|
||||||
"service", name, NULL);
|
"service", name, NULL);
|
||||||
|
|
@ -455,7 +460,6 @@ on_name_vanished (GDBusConnection *connection,
|
||||||
g_print ("context: on name vanished\n");
|
g_print ("context: on name vanished\n");
|
||||||
|
|
||||||
priv->connection = connection;
|
priv->connection = connection;
|
||||||
g_dbus_object_manager_server_set_connection (priv->server_manager, connection);
|
|
||||||
|
|
||||||
g_object_set (priv->subscribe, "connection", connection, NULL);
|
g_object_set (priv->subscribe, "connection", connection, NULL);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,4 @@ struct _PvContextPrivate
|
||||||
PvSubscribe *subscribe;
|
PvSubscribe *subscribe;
|
||||||
|
|
||||||
GList *sources;
|
GList *sources;
|
||||||
|
|
||||||
GDBusObjectManagerServer *server_manager;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <gio/gunixfdlist.h>
|
#include <gio/gunixfdlist.h>
|
||||||
|
|
||||||
#include "server/pv-daemon.h"
|
#include "server/pv-daemon.h"
|
||||||
|
|
@ -35,14 +36,17 @@ struct _PvStreamPrivate
|
||||||
gchar *target;
|
gchar *target;
|
||||||
PvStreamState state;
|
PvStreamState state;
|
||||||
GError *error;
|
GError *error;
|
||||||
|
gboolean provide;
|
||||||
|
|
||||||
|
GBytes *accepted_formats;
|
||||||
|
GBytes *possible_formats;
|
||||||
|
GBytes *format;
|
||||||
gchar *source_output_path;
|
gchar *source_output_path;
|
||||||
GVariant *spec;
|
|
||||||
GDBusProxy *source_output;
|
GDBusProxy *source_output;
|
||||||
|
|
||||||
GSocket *socket;
|
|
||||||
PvStreamMode mode;
|
PvStreamMode mode;
|
||||||
guint socket_id;
|
GSocket *socket;
|
||||||
|
GSource *socket_source;
|
||||||
|
|
||||||
PvBufferInfo info;
|
PvBufferInfo info;
|
||||||
};
|
};
|
||||||
|
|
@ -59,7 +63,9 @@ enum
|
||||||
PROP_NAME,
|
PROP_NAME,
|
||||||
PROP_PROPERTIES,
|
PROP_PROPERTIES,
|
||||||
PROP_STATE,
|
PROP_STATE,
|
||||||
PROP_SOCKET
|
PROP_POSSIBLE_FORMATS,
|
||||||
|
PROP_FORMAT,
|
||||||
|
PROP_SOCKET,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
|
@ -96,6 +102,14 @@ pv_stream_get_property (GObject *_object,
|
||||||
g_value_set_enum (value, priv->state);
|
g_value_set_enum (value, priv->state);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_POSSIBLE_FORMATS:
|
||||||
|
g_value_set_boxed (value, priv->possible_formats);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_FORMAT:
|
||||||
|
g_value_set_boxed (value, priv->format);
|
||||||
|
break;
|
||||||
|
|
||||||
case PROP_SOCKET:
|
case PROP_SOCKET:
|
||||||
g_value_set_object (value, priv->socket);
|
g_value_set_object (value, priv->socket);
|
||||||
break;
|
break;
|
||||||
|
|
@ -216,7 +230,33 @@ pv_stream_class_init (PvStreamClass * klass)
|
||||||
PV_STREAM_STATE_UNCONNECTED,
|
PV_STREAM_STATE_UNCONNECTED,
|
||||||
G_PARAM_READABLE |
|
G_PARAM_READABLE |
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
/**
|
||||||
|
* PvStream:possible-formats
|
||||||
|
*
|
||||||
|
* The possible formats for the stream. this can only be used after connecting
|
||||||
|
* the stream for capture or provide.
|
||||||
|
*/
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_POSSIBLE_FORMATS,
|
||||||
|
g_param_spec_boxed ("possible-formats",
|
||||||
|
"Possible Formats",
|
||||||
|
"The possbile formats of the stream",
|
||||||
|
G_TYPE_BYTES,
|
||||||
|
G_PARAM_READABLE |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
/**
|
||||||
|
* PvStream:formats
|
||||||
|
*
|
||||||
|
* The format of the stream. This will be set after starting the stream.
|
||||||
|
*/
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_FORMAT,
|
||||||
|
g_param_spec_boxed ("format",
|
||||||
|
"Format",
|
||||||
|
"The format of the stream",
|
||||||
|
G_TYPE_BYTES,
|
||||||
|
G_PARAM_READABLE |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
/**
|
/**
|
||||||
* PvStream:socket
|
* PvStream:socket
|
||||||
*
|
*
|
||||||
|
|
@ -329,7 +369,7 @@ on_source_output_signal (GDBusProxy *proxy,
|
||||||
GVariant *parameters,
|
GVariant *parameters,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
g_print ("on source output signal\n");
|
g_print ("on source output signal %s %s\n", sender_name, signal_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -340,6 +380,8 @@ on_source_output_proxy (GObject *source_object,
|
||||||
PvStream *stream = user_data;
|
PvStream *stream = user_data;
|
||||||
PvStreamPrivate *priv = stream->priv;
|
PvStreamPrivate *priv = stream->priv;
|
||||||
PvContext *context = priv->context;
|
PvContext *context = priv->context;
|
||||||
|
GVariant *v;
|
||||||
|
gchar *str;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
priv->source_output = pv_subscribe_get_proxy_finish (context->priv->subscribe,
|
priv->source_output = pv_subscribe_get_proxy_finish (context->priv->subscribe,
|
||||||
|
|
@ -348,6 +390,22 @@ on_source_output_proxy (GObject *source_object,
|
||||||
if (priv->source_output == NULL)
|
if (priv->source_output == NULL)
|
||||||
goto source_output_failed;
|
goto source_output_failed;
|
||||||
|
|
||||||
|
g_print ("got source-output %s\n", priv->source_output_path);
|
||||||
|
|
||||||
|
v = g_dbus_proxy_get_cached_property (priv->source_output, "PossibleFormats");
|
||||||
|
if (v) {
|
||||||
|
str = g_variant_dup_string (v, NULL);
|
||||||
|
g_variant_unref (v);
|
||||||
|
|
||||||
|
g_print ("got possible formats %s\n", str);
|
||||||
|
|
||||||
|
if (priv->possible_formats)
|
||||||
|
g_bytes_unref (priv->possible_formats);
|
||||||
|
priv->possible_formats = g_bytes_new_take (str, strlen (str) + 1);
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (stream), "possible-formats");
|
||||||
|
}
|
||||||
|
|
||||||
g_signal_connect (priv->source_output,
|
g_signal_connect (priv->source_output,
|
||||||
"g-signal",
|
"g-signal",
|
||||||
(GCallback) on_source_output_signal,
|
(GCallback) on_source_output_signal,
|
||||||
|
|
@ -384,7 +442,6 @@ on_source_output_created (GObject *source_object,
|
||||||
goto create_failed;
|
goto create_failed;
|
||||||
|
|
||||||
g_variant_get (ret, "(o)", &priv->source_output_path);
|
g_variant_get (ret, "(o)", &priv->source_output_path);
|
||||||
g_print ("got source-output %s\n", priv->source_output_path);
|
|
||||||
g_variant_unref (ret);
|
g_variant_unref (ret);
|
||||||
|
|
||||||
pv_subscribe_get_proxy (context->priv->subscribe,
|
pv_subscribe_get_proxy (context->priv->subscribe,
|
||||||
|
|
@ -417,9 +474,9 @@ do_connect_capture (PvStream *stream)
|
||||||
|
|
||||||
g_dbus_proxy_call (context->priv->client,
|
g_dbus_proxy_call (context->priv->client,
|
||||||
"CreateSourceOutput",
|
"CreateSourceOutput",
|
||||||
g_variant_new ("(o@a{sv})",
|
g_variant_new ("(os)",
|
||||||
(priv->target ? priv->target : "/"),
|
(priv->target ? priv->target : "/"),
|
||||||
priv->spec),
|
g_bytes_get_data (priv->accepted_formats, NULL)),
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1,
|
-1,
|
||||||
NULL, /* GCancellable *cancellable */
|
NULL, /* GCancellable *cancellable */
|
||||||
|
|
@ -444,20 +501,21 @@ gboolean
|
||||||
pv_stream_connect_capture (PvStream *stream,
|
pv_stream_connect_capture (PvStream *stream,
|
||||||
const gchar *source,
|
const gchar *source,
|
||||||
PvStreamFlags flags,
|
PvStreamFlags flags,
|
||||||
GVariant *spec)
|
GBytes *accepted_formats)
|
||||||
{
|
{
|
||||||
PvStreamPrivate *priv;
|
PvStreamPrivate *priv;
|
||||||
PvContext *context;
|
PvContext *context;
|
||||||
|
|
||||||
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
||||||
g_return_val_if_fail (spec != NULL, FALSE);
|
g_return_val_if_fail (accepted_formats != NULL, FALSE);
|
||||||
|
|
||||||
priv = stream->priv;
|
priv = stream->priv;
|
||||||
context = priv->context;
|
context = priv->context;
|
||||||
g_return_val_if_fail (pv_context_get_state (context) == PV_CONTEXT_STATE_READY, FALSE);
|
g_return_val_if_fail (pv_context_get_state (context) == PV_CONTEXT_STATE_READY, FALSE);
|
||||||
|
|
||||||
priv->target = g_strdup (source);
|
priv->target = g_strdup (source);
|
||||||
priv->spec = spec;
|
priv->accepted_formats = g_bytes_ref (accepted_formats);
|
||||||
|
priv->provide = FALSE;
|
||||||
|
|
||||||
stream_set_state (stream, PV_STREAM_STATE_CONNECTING);
|
stream_set_state (stream, PV_STREAM_STATE_CONNECTING);
|
||||||
|
|
||||||
|
|
@ -476,8 +534,7 @@ do_connect_provide (PvStream *stream)
|
||||||
|
|
||||||
g_dbus_proxy_call (context->priv->client,
|
g_dbus_proxy_call (context->priv->client,
|
||||||
"CreateSourceInput",
|
"CreateSourceInput",
|
||||||
g_variant_new ("(@a{sv})",
|
g_variant_new ("(s)", g_bytes_get_data (priv->possible_formats, NULL)),
|
||||||
priv->spec),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1,
|
-1,
|
||||||
NULL, /* GCancellable *cancellable */
|
NULL, /* GCancellable *cancellable */
|
||||||
|
|
@ -498,21 +555,22 @@ do_connect_provide (PvStream *stream)
|
||||||
* Returns: %TRUE on success.
|
* Returns: %TRUE on success.
|
||||||
*/
|
*/
|
||||||
gboolean
|
gboolean
|
||||||
pv_stream_connect_provide (PvStream *stream,
|
pv_stream_connect_provide (PvStream *stream,
|
||||||
PvStreamFlags flags,
|
PvStreamFlags flags,
|
||||||
GVariant *spec)
|
GBytes *possible_formats)
|
||||||
{
|
{
|
||||||
PvStreamPrivate *priv;
|
PvStreamPrivate *priv;
|
||||||
PvContext *context;
|
PvContext *context;
|
||||||
|
|
||||||
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
||||||
g_return_val_if_fail (spec != NULL, FALSE);
|
g_return_val_if_fail (possible_formats != NULL, FALSE);
|
||||||
|
|
||||||
priv = stream->priv;
|
priv = stream->priv;
|
||||||
context = priv->context;
|
context = priv->context;
|
||||||
g_return_val_if_fail (pv_context_get_state (context) == PV_CONTEXT_STATE_READY, FALSE);
|
g_return_val_if_fail (pv_context_get_state (context) == PV_CONTEXT_STATE_READY, FALSE);
|
||||||
|
|
||||||
priv->spec = spec;
|
priv->possible_formats = g_bytes_ref (possible_formats);
|
||||||
|
priv->provide = TRUE;
|
||||||
|
|
||||||
stream_set_state (stream, PV_STREAM_STATE_CONNECTING);
|
stream_set_state (stream, PV_STREAM_STATE_CONNECTING);
|
||||||
|
|
||||||
|
|
@ -588,16 +646,15 @@ pv_stream_disconnect (PvStream *stream)
|
||||||
|
|
||||||
g_main_context_invoke (context->priv->context, (GSourceFunc) do_disconnect, stream);
|
g_main_context_invoke (context->priv->context, (GSourceFunc) do_disconnect, stream);
|
||||||
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <gst/wire-protocol.h>
|
#include <gst/wire-protocol.h>
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
on_socket_data (GSocket *socket,
|
on_socket_condition (GSocket *socket,
|
||||||
GIOCondition condition,
|
GIOCondition condition,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
PvStream *stream = user_data;
|
PvStream *stream = user_data;
|
||||||
PvStreamPrivate *priv = stream->priv;
|
PvStreamPrivate *priv = stream->priv;
|
||||||
|
|
@ -642,6 +699,9 @@ on_socket_data (GSocket *socket,
|
||||||
g_signal_emit (stream, signals[SIGNAL_NEW_BUFFER], 0, NULL);
|
g_signal_emit (stream, signals[SIGNAL_NEW_BUFFER], 0, NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case G_IO_OUT:
|
||||||
|
g_print ("can do IO\n");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -668,12 +728,11 @@ handle_socket (PvStream *stream, gint fd)
|
||||||
|
|
||||||
case PV_STREAM_MODE_BUFFER:
|
case PV_STREAM_MODE_BUFFER:
|
||||||
{
|
{
|
||||||
GSource *source;
|
if (!priv->provide) {
|
||||||
|
priv->socket_source = g_socket_create_source (priv->socket, G_IO_IN, NULL);
|
||||||
source = g_socket_create_source (priv->socket, G_IO_IN, NULL);
|
g_source_set_callback (priv->socket_source, (GSourceFunc) on_socket_condition, stream, NULL);
|
||||||
g_source_set_callback (source, (GSourceFunc) on_socket_data, stream, NULL);
|
g_source_attach (priv->socket_source, priv->context->priv->context);
|
||||||
priv->socket_id = g_source_attach (source, priv->context->priv->context);
|
}
|
||||||
g_source_unref (source);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -703,6 +762,13 @@ unhandle_socket (PvStream *stream)
|
||||||
g_object_notify (G_OBJECT (stream), "socket");
|
g_object_notify (G_OBJECT (stream), "socket");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PV_STREAM_MODE_BUFFER:
|
||||||
|
if (priv->socket_source) {
|
||||||
|
g_source_destroy (priv->socket_source);
|
||||||
|
g_clear_pointer (&priv->socket_source, g_source_unref);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -717,7 +783,7 @@ on_stream_started (GObject *source_object,
|
||||||
PvStreamPrivate *priv = stream->priv;
|
PvStreamPrivate *priv = stream->priv;
|
||||||
GUnixFDList *out_fd_list;
|
GUnixFDList *out_fd_list;
|
||||||
gint fd_idx, fd;
|
gint fd_idx, fd;
|
||||||
GVariant *out_props;
|
gchar *format;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
GVariant *result;
|
GVariant *result;
|
||||||
|
|
||||||
|
|
@ -729,12 +795,17 @@ on_stream_started (GObject *source_object,
|
||||||
goto start_failed;
|
goto start_failed;
|
||||||
|
|
||||||
g_variant_get (result,
|
g_variant_get (result,
|
||||||
"(h@a{sv})",
|
"(hs)",
|
||||||
&fd_idx,
|
&fd_idx,
|
||||||
&out_props);
|
&format);
|
||||||
|
|
||||||
g_variant_unref (result);
|
g_variant_unref (result);
|
||||||
g_variant_unref (out_props);
|
|
||||||
|
if (priv->format)
|
||||||
|
g_bytes_unref (priv->format);
|
||||||
|
priv->format = g_bytes_new (format, strlen (format) + 1);
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (stream), "format");
|
||||||
|
|
||||||
if ((fd = g_unix_fd_list_get (out_fd_list, fd_idx, &error)) < 0)
|
if ((fd = g_unix_fd_list_get (out_fd_list, fd_idx, &error)) < 0)
|
||||||
goto fd_failed;
|
goto fd_failed;
|
||||||
|
|
@ -768,14 +839,10 @@ static gboolean
|
||||||
do_start (PvStream *stream)
|
do_start (PvStream *stream)
|
||||||
{
|
{
|
||||||
PvStreamPrivate *priv = stream->priv;
|
PvStreamPrivate *priv = stream->priv;
|
||||||
GVariantBuilder builder;
|
|
||||||
|
|
||||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string ("hello"));
|
|
||||||
|
|
||||||
g_dbus_proxy_call (priv->source_output,
|
g_dbus_proxy_call (priv->source_output,
|
||||||
"Start",
|
"Start",
|
||||||
g_variant_new ("(@a{sv})", g_variant_builder_end (&builder)),
|
g_variant_new ("(s)", g_bytes_get_data (priv->format, NULL)),
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1,
|
-1,
|
||||||
NULL, /* GCancellable *cancellable */
|
NULL, /* GCancellable *cancellable */
|
||||||
|
|
@ -802,7 +869,7 @@ do_start (PvStream *stream)
|
||||||
* Returns: %TRUE on success.
|
* Returns: %TRUE on success.
|
||||||
*/
|
*/
|
||||||
gboolean
|
gboolean
|
||||||
pv_stream_start (PvStream *stream, PvStreamMode mode)
|
pv_stream_start (PvStream *stream, GBytes *format, PvStreamMode mode)
|
||||||
{
|
{
|
||||||
PvStreamPrivate *priv;
|
PvStreamPrivate *priv;
|
||||||
|
|
||||||
|
|
@ -812,6 +879,7 @@ pv_stream_start (PvStream *stream, PvStreamMode mode)
|
||||||
g_return_val_if_fail (priv->state == PV_STREAM_STATE_READY, FALSE);
|
g_return_val_if_fail (priv->state == PV_STREAM_STATE_READY, FALSE);
|
||||||
|
|
||||||
priv->mode = mode;
|
priv->mode = mode;
|
||||||
|
priv->format = g_bytes_ref (format);
|
||||||
|
|
||||||
stream_set_state (stream, PV_STREAM_STATE_STARTING);
|
stream_set_state (stream, PV_STREAM_STATE_STARTING);
|
||||||
|
|
||||||
|
|
@ -837,6 +905,8 @@ on_stream_stopped (GObject *source_object,
|
||||||
g_variant_unref (ret);
|
g_variant_unref (ret);
|
||||||
|
|
||||||
unhandle_socket (stream);
|
unhandle_socket (stream);
|
||||||
|
g_clear_pointer (&priv->format, g_free);
|
||||||
|
g_object_notify (G_OBJECT (stream), "format");
|
||||||
|
|
||||||
stream_set_state (stream, PV_STREAM_STATE_READY);
|
stream_set_state (stream, PV_STREAM_STATE_READY);
|
||||||
|
|
||||||
|
|
@ -932,6 +1002,11 @@ gboolean
|
||||||
pv_stream_provide_buffer (PvStream *stream, PvBufferInfo *info)
|
pv_stream_provide_buffer (PvStream *stream, PvBufferInfo *info)
|
||||||
{
|
{
|
||||||
PvStreamPrivate *priv;
|
PvStreamPrivate *priv;
|
||||||
|
gssize len;
|
||||||
|
GOutputVector ovec;
|
||||||
|
FDMessage msg;
|
||||||
|
gint flags = 0;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
||||||
g_return_val_if_fail (info != NULL, FALSE);
|
g_return_val_if_fail (info != NULL, FALSE);
|
||||||
|
|
@ -939,6 +1014,30 @@ pv_stream_provide_buffer (PvStream *stream, PvBufferInfo *info)
|
||||||
priv = stream->priv;
|
priv = stream->priv;
|
||||||
g_return_val_if_fail (priv->state == PV_STREAM_STATE_STREAMING, FALSE);
|
g_return_val_if_fail (priv->state == PV_STREAM_STATE_STREAMING, FALSE);
|
||||||
|
|
||||||
|
msg.flags = info->flags;
|
||||||
|
msg.seq = info->seq;
|
||||||
|
msg.pts = info->pts;
|
||||||
|
msg.dts_offset = info->dts_offset;
|
||||||
|
msg.offset = info->offset;
|
||||||
|
msg.size = info->size;
|
||||||
|
|
||||||
|
ovec.buffer = &msg;
|
||||||
|
ovec.size = sizeof (msg);
|
||||||
|
|
||||||
|
len = g_socket_send_message (priv->socket,
|
||||||
|
NULL,
|
||||||
|
&ovec,
|
||||||
|
1,
|
||||||
|
&info->message,
|
||||||
|
1,
|
||||||
|
flags,
|
||||||
|
NULL,
|
||||||
|
&error);
|
||||||
|
g_assert (len == sizeof (msg));
|
||||||
|
|
||||||
|
if (info->message)
|
||||||
|
g_object_unref (info->message);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,13 +103,15 @@ const GError * pv_stream_get_error (PvStream *stream);
|
||||||
gboolean pv_stream_connect_capture (PvStream *stream,
|
gboolean pv_stream_connect_capture (PvStream *stream,
|
||||||
const gchar *source,
|
const gchar *source,
|
||||||
PvStreamFlags flags,
|
PvStreamFlags flags,
|
||||||
GVariant *spec);
|
GBytes *accepted_formats);
|
||||||
gboolean pv_stream_connect_provide (PvStream *stream,
|
gboolean pv_stream_connect_provide (PvStream *stream,
|
||||||
PvStreamFlags flags,
|
PvStreamFlags flags,
|
||||||
GVariant *spec);
|
GBytes *possible_formats);
|
||||||
gboolean pv_stream_disconnect (PvStream *stream);
|
gboolean pv_stream_disconnect (PvStream *stream);
|
||||||
|
|
||||||
gboolean pv_stream_start (PvStream *stream, PvStreamMode mode);
|
gboolean pv_stream_start (PvStream *stream,
|
||||||
|
GBytes *format,
|
||||||
|
PvStreamMode mode);
|
||||||
gboolean pv_stream_stop (PvStream *stream);
|
gboolean pv_stream_stop (PvStream *stream);
|
||||||
|
|
||||||
gboolean pv_stream_capture_buffer (PvStream *stream,
|
gboolean pv_stream_capture_buffer (PvStream *stream,
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,8 @@ on_proxy_created (GObject *source_object,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_print ("got proxy for %s:%s\n", data->object_path, data->interface_name);
|
||||||
|
|
||||||
g_signal_connect (data->proxy,
|
g_signal_connect (data->proxy,
|
||||||
"g-properties-changed",
|
"g-properties-changed",
|
||||||
(GCallback) on_proxy_properties_changed,
|
(GCallback) on_proxy_properties_changed,
|
||||||
|
|
@ -180,6 +182,8 @@ add_interface (PvSubscribe *subscribe,
|
||||||
priv->objects = g_list_prepend (priv->objects, data);
|
priv->objects = g_list_prepend (priv->objects, data);
|
||||||
priv->pending_proxies++;
|
priv->pending_proxies++;
|
||||||
|
|
||||||
|
g_print ("making proxy for %s:%s\n", object_path, interface_name);
|
||||||
|
|
||||||
g_dbus_proxy_new (priv->connection,
|
g_dbus_proxy_new (priv->connection,
|
||||||
G_DBUS_PROXY_FLAGS_NONE,
|
G_DBUS_PROXY_FLAGS_NONE,
|
||||||
NULL, /* GDBusInterfaceInfo* */
|
NULL, /* GDBusInterfaceInfo* */
|
||||||
|
|
|
||||||
|
|
@ -53,25 +53,24 @@
|
||||||
<method name='Disconnect'/>
|
<method name='Disconnect'/>
|
||||||
<!-- CreateSourceOutput:
|
<!-- CreateSourceOutput:
|
||||||
@source: the Source1 object path or / for default
|
@source: the Source1 object path or / for default
|
||||||
@props: input properties
|
@incaps: input capabilities
|
||||||
@output: the SourceOutput1 object path
|
@output: the SourceOutput1 object path
|
||||||
|
|
||||||
Create a new output for @source with given @props
|
Create a new output for @source with given @incaps
|
||||||
-->
|
-->
|
||||||
<method name='CreateSourceOutput'>
|
<method name='CreateSourceOutput'>
|
||||||
<arg type='o' name='source' direction='in'/>
|
<arg type='o' name='source' direction='in'/>
|
||||||
<arg type='a{sv}' name='props' direction='in'/>
|
<arg type='s' name='accepted_formats' direction='in'/>
|
||||||
<arg type='o' name='output' direction='out'/>
|
<arg type='o' name='output' direction='out'/>
|
||||||
</method>
|
</method>
|
||||||
<!-- CreateSourceInput:
|
<!-- CreateSourceInput:
|
||||||
@props: input properties
|
@incaps: input capabilities
|
||||||
@source: the new Source1 object path
|
|
||||||
@input: the SourceInput1 object path
|
@input: the SourceInput1 object path
|
||||||
|
|
||||||
Create a new source and input object with given @props
|
Create a new source and input object with given @incaps
|
||||||
-->
|
-->
|
||||||
<method name='CreateSourceInput'>
|
<method name='CreateSourceInput'>
|
||||||
<arg type='a{sv}' name='props' direction='in'/>
|
<arg type='s' name='possible_formats' direction='in'/>
|
||||||
<arg type='o' name='input' direction='out'/>
|
<arg type='o' name='input' direction='out'/>
|
||||||
</method>
|
</method>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
@ -122,18 +121,12 @@
|
||||||
4 = the source is running
|
4 = the source is running
|
||||||
-->
|
-->
|
||||||
<property name='State' type='u' access='read' />
|
<property name='State' type='u' access='read' />
|
||||||
<!-- GetCapabilities:
|
<!-- Capabilities:
|
||||||
@props: input properties
|
|
||||||
@caps: result capabilities
|
|
||||||
|
|
||||||
Get a list of capabilities of this source. This includes
|
The capabilities of this source. This includes
|
||||||
supported data formats and transports. @props is used to
|
supported data formats and transports.
|
||||||
filter the amount of output capabilities
|
|
||||||
-->
|
-->
|
||||||
<method name='GetCapabilities'>
|
<property name='PossibleFormats' type='s' access='read' />
|
||||||
<arg type='a{sv}' name='props' direction='in'/>
|
|
||||||
<arg type='aa{sv}' name='caps' direction='out'/>
|
|
||||||
</method>
|
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
@ -144,34 +137,28 @@
|
||||||
start/stop the media transport.
|
start/stop the media transport.
|
||||||
-->
|
-->
|
||||||
<interface name='org.pulsevideo.SourceOutput1'>
|
<interface name='org.pulsevideo.SourceOutput1'>
|
||||||
|
<!-- Client: the owner client of this source output -->
|
||||||
|
<property name='Client' type='o' access='read' />
|
||||||
<!-- Source: the source of this source output -->
|
<!-- Source: the source of this source output -->
|
||||||
<property name='Source' type='o' access='read' />
|
<property name='Source' type='o' access='read' />
|
||||||
|
<!-- Capabilities: capabilities of the source output -->
|
||||||
|
<property name='PossibleFormats' type='s' access='read' />
|
||||||
<!-- Start:
|
<!-- Start:
|
||||||
@props: input properties
|
@incaps: input capabilities
|
||||||
@fd: output file descriptor
|
@fd: output file descriptor
|
||||||
@props: output properties
|
@outcaps: output capabilities
|
||||||
|
|
||||||
Start the datatransfer of the source with @props.
|
Start the datatransfer of the source with @incaps.
|
||||||
|
|
||||||
The result is a file descriptor that can be used to get metadata
|
The result is a file descriptor that can be used to get metadata
|
||||||
and media. @props contains the final media format and transport
|
and media. @outcaps contains the final media format and transport
|
||||||
properties.
|
properties.
|
||||||
-->
|
-->
|
||||||
<method name='Start'>
|
<method name='Start'>
|
||||||
<arg type='a{sv}' name='props' direction='in'/>
|
<arg type='s' name='requested_format' direction='in'/>
|
||||||
<arg type='h' name='fd' direction='out'/>
|
<arg type='h' name='fd' direction='out'/>
|
||||||
<arg type='a{sv}' name='props' direction='out'/>
|
<arg type='s' name='format' direction='out'/>
|
||||||
</method>
|
</method>
|
||||||
<!-- RequestReconfigure:
|
|
||||||
@props: new properties
|
|
||||||
|
|
||||||
This signal is fired when the source wants to change the format
|
|
||||||
or transport. The client should Stop and Start the source output
|
|
||||||
with new properties
|
|
||||||
-->
|
|
||||||
<signal name='RequestReconfigure'>
|
|
||||||
<arg type='a{sv}' name='props' direction='in'/>
|
|
||||||
</signal>
|
|
||||||
<!-- Stop:
|
<!-- Stop:
|
||||||
|
|
||||||
Stop data transport
|
Stop data transport
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,6 @@ static gboolean gst_pulsevideo_sink_setcaps (GstBaseSink * bsink, GstCaps * caps
|
||||||
static GstCaps *gst_pulsevideo_sink_sink_fixate (GstBaseSink * bsink,
|
static GstCaps *gst_pulsevideo_sink_sink_fixate (GstBaseSink * bsink,
|
||||||
GstCaps * caps);
|
GstCaps * caps);
|
||||||
|
|
||||||
static gboolean gst_pulsevideo_sink_propose_allocation (GstBaseSink * bsink,
|
|
||||||
GstQuery * query);
|
|
||||||
static GstFlowReturn gst_pulsevideo_sink_render (GstBaseSink * psink,
|
static GstFlowReturn gst_pulsevideo_sink_render (GstBaseSink * psink,
|
||||||
GstBuffer * buffer);
|
GstBuffer * buffer);
|
||||||
static gboolean gst_pulsevideo_sink_start (GstBaseSink * basesink);
|
static gboolean gst_pulsevideo_sink_start (GstBaseSink * basesink);
|
||||||
|
|
@ -116,7 +114,6 @@ gst_pulsevideo_sink_class_init (GstPulsevideoSinkClass * klass)
|
||||||
gstbasesink_class->start = gst_pulsevideo_sink_start;
|
gstbasesink_class->start = gst_pulsevideo_sink_start;
|
||||||
gstbasesink_class->stop = gst_pulsevideo_sink_stop;
|
gstbasesink_class->stop = gst_pulsevideo_sink_stop;
|
||||||
gstbasesink_class->render = gst_pulsevideo_sink_render;
|
gstbasesink_class->render = gst_pulsevideo_sink_render;
|
||||||
gstbasesink_class->propose_allocation = gst_pulsevideo_sink_propose_allocation;
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (pulsevideo_sink_debug, "pulsevideosink", 0,
|
GST_DEBUG_CATEGORY_INIT (pulsevideo_sink_debug, "pulsevideosink", 0,
|
||||||
"Pulsevideo Sink");
|
"Pulsevideo Sink");
|
||||||
|
|
@ -189,59 +186,6 @@ gst_pulsevideo_sink_get_property (GObject * object, guint prop_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_pulsevideo_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstPulsevideoSink *pvsink;
|
|
||||||
GstBufferPool *pool;
|
|
||||||
gboolean update;
|
|
||||||
guint size, min, max;
|
|
||||||
GstStructure *config;
|
|
||||||
GstCaps *caps = NULL;
|
|
||||||
|
|
||||||
pvsink = GST_PULSEVIDEO_SINK (bsink);
|
|
||||||
|
|
||||||
if (gst_query_get_n_allocation_pools (query) > 0) {
|
|
||||||
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
|
|
||||||
|
|
||||||
/* adjust size */
|
|
||||||
size = MAX (size, pvsink->info.size);
|
|
||||||
update = TRUE;
|
|
||||||
} else {
|
|
||||||
pool = NULL;
|
|
||||||
size = pvsink->info.size;
|
|
||||||
min = max = 0;
|
|
||||||
update = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* no downstream pool, make our own */
|
|
||||||
if (pool == NULL) {
|
|
||||||
pool = gst_video_buffer_pool_new ();
|
|
||||||
}
|
|
||||||
|
|
||||||
config = gst_buffer_pool_get_config (pool);
|
|
||||||
|
|
||||||
gst_query_parse_allocation (query, &caps, NULL);
|
|
||||||
if (caps)
|
|
||||||
gst_buffer_pool_config_set_params (config, caps, size, min, max);
|
|
||||||
|
|
||||||
if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
|
|
||||||
gst_buffer_pool_config_add_option (config,
|
|
||||||
GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
||||||
}
|
|
||||||
gst_buffer_pool_set_config (pool, config);
|
|
||||||
|
|
||||||
if (update)
|
|
||||||
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
|
|
||||||
else
|
|
||||||
gst_query_add_allocation_pool (query, pool, size, min, max);
|
|
||||||
|
|
||||||
if (pool)
|
|
||||||
gst_object_unref (pool);
|
|
||||||
|
|
||||||
return GST_BASE_SINK_CLASS (parent_class)->propose_allocation (bsink, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_new_buffer (GObject *gobject,
|
on_new_buffer (GObject *gobject,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
|
|
@ -283,46 +227,20 @@ gst_pulsevideo_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_pulsevideo_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
gst_pulsevideo_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
||||||
{
|
{
|
||||||
const GstStructure *structure;
|
|
||||||
GstPulsevideoSink *pvsink;
|
GstPulsevideoSink *pvsink;
|
||||||
GstVideoInfo info;
|
gchar *str;
|
||||||
GVariantBuilder builder;
|
GBytes *format;
|
||||||
|
|
||||||
pvsink = GST_PULSEVIDEO_SINK (bsink);
|
pvsink = GST_PULSEVIDEO_SINK (bsink);
|
||||||
|
|
||||||
structure = gst_caps_get_structure (caps, 0);
|
|
||||||
|
|
||||||
if (gst_structure_has_name (structure, "video/x-raw")) {
|
|
||||||
/* we can use the parsing code */
|
|
||||||
if (!gst_video_info_from_caps (&info, caps))
|
|
||||||
goto parse_failed;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
goto unsupported_caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* looks ok here */
|
|
||||||
pvsink->info = info;
|
|
||||||
|
|
||||||
pvsink->stream = pv_stream_new (pvsink->ctx, "test", NULL);
|
pvsink->stream = pv_stream_new (pvsink->ctx, "test", NULL);
|
||||||
g_signal_connect (pvsink->stream, "notify::state", (GCallback) on_stream_notify, pvsink);
|
g_signal_connect (pvsink->stream, "notify::state", (GCallback) on_stream_notify, pvsink);
|
||||||
g_signal_connect (pvsink->stream, "new-buffer", (GCallback) on_new_buffer, pvsink);
|
g_signal_connect (pvsink->stream, "new-buffer", (GCallback) on_new_buffer, pvsink);
|
||||||
|
|
||||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
str = gst_caps_to_string (caps);
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.encoding", g_variant_new_string ("video/x-raw"));
|
format = g_bytes_new_take (str, strlen (str) + 1);
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.format",
|
|
||||||
g_variant_new_string (gst_video_format_to_string (info.finfo->format)));
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.width", g_variant_new_int32 (info.width));
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.height", g_variant_new_int32 (info.height));
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.views", g_variant_new_int32 (info.views));
|
|
||||||
// g_variant_builder_add (&builder, "{sv}", "format.chroma-site",
|
|
||||||
// g_variant_new_string (gst_video_chroma_to_string (info.chroma_site)));
|
|
||||||
// g_variant_builder_add (&builder, "{sv}", "format.colorimetry",
|
|
||||||
// g_variant_new_take_string (gst_video_colorimetry_to_string (&info.colorimetry)));
|
|
||||||
// g_variant_builder_add (&builder, "{sv}", "format.interlace-mode",
|
|
||||||
// g_variant_new_string (gst_video_interlace_mode_to_string (info.interlace_mode)));
|
|
||||||
|
|
||||||
pv_stream_connect_provide (pvsink->stream, 0, g_variant_builder_end (&builder));
|
pv_stream_connect_provide (pvsink->stream, 0, format);
|
||||||
|
|
||||||
g_mutex_lock (&pvsink->lock);
|
g_mutex_lock (&pvsink->lock);
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
|
|
@ -338,24 +256,11 @@ gst_pulsevideo_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
||||||
}
|
}
|
||||||
g_mutex_unlock (&pvsink->lock);
|
g_mutex_unlock (&pvsink->lock);
|
||||||
|
|
||||||
pv_stream_start (pvsink->stream, PV_STREAM_MODE_BUFFER);
|
pv_stream_start (pvsink->stream, format, PV_STREAM_MODE_BUFFER);
|
||||||
|
pvsink->negotiated = TRUE;
|
||||||
GST_DEBUG_OBJECT (pvsink, "size %dx%d, %d/%d fps",
|
|
||||||
info.width, info.height, info.fps_n, info.fps_d);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
parse_failed:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (bsink, "failed to parse caps");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
unsupported_caps:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (bsink, "unsupported caps: %" GST_PTR_FORMAT, caps);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
connect_error:
|
connect_error:
|
||||||
{
|
{
|
||||||
g_mutex_unlock (&pvsink->lock);
|
g_mutex_unlock (&pvsink->lock);
|
||||||
|
|
@ -373,8 +278,7 @@ gst_pulsevideo_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
|
|
||||||
pvsink = GST_PULSEVIDEO_SINK (bsink);
|
pvsink = GST_PULSEVIDEO_SINK (bsink);
|
||||||
|
|
||||||
if (G_UNLIKELY (GST_VIDEO_INFO_FORMAT (&pvsink->info) ==
|
if (!pvsink->negotiated)
|
||||||
GST_VIDEO_FORMAT_UNKNOWN))
|
|
||||||
goto not_negotiated;
|
goto not_negotiated;
|
||||||
|
|
||||||
info.flags = 0;
|
info.flags = 0;
|
||||||
|
|
@ -424,7 +328,7 @@ gst_pulsevideo_sink_start (GstBaseSink * basesink)
|
||||||
{
|
{
|
||||||
GstPulsevideoSink *sink = GST_PULSEVIDEO_SINK (basesink);
|
GstPulsevideoSink *sink = GST_PULSEVIDEO_SINK (basesink);
|
||||||
|
|
||||||
gst_video_info_init (&sink->info);
|
sink->negotiated = FALSE;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
@ -432,6 +336,10 @@ gst_pulsevideo_sink_start (GstBaseSink * basesink)
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_pulsevideo_sink_stop (GstBaseSink * basesink)
|
gst_pulsevideo_sink_stop (GstBaseSink * basesink)
|
||||||
{
|
{
|
||||||
|
GstPulsevideoSink *sink = GST_PULSEVIDEO_SINK (basesink);
|
||||||
|
|
||||||
|
sink->negotiated = FALSE;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ struct _GstPulsevideoSink {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
|
|
||||||
/* video state */
|
/* video state */
|
||||||
GstVideoInfo info;
|
gboolean negotiated;
|
||||||
|
|
||||||
GMainContext *context;
|
GMainContext *context;
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
|
|
|
||||||
|
|
@ -80,10 +80,6 @@ static gboolean gst_pulsevideo_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
|
||||||
static GstCaps *gst_pulsevideo_src_src_fixate (GstBaseSrc * bsrc,
|
static GstCaps *gst_pulsevideo_src_src_fixate (GstBaseSrc * bsrc,
|
||||||
GstCaps * caps);
|
GstCaps * caps);
|
||||||
|
|
||||||
static gboolean gst_pulsevideo_src_query (GstBaseSrc * bsrc, GstQuery * query);
|
|
||||||
|
|
||||||
static gboolean gst_pulsevideo_src_decide_allocation (GstBaseSrc * bsrc,
|
|
||||||
GstQuery * query);
|
|
||||||
static GstFlowReturn gst_pulsevideo_src_create (GstPushSrc * psrc,
|
static GstFlowReturn gst_pulsevideo_src_create (GstPushSrc * psrc,
|
||||||
GstBuffer ** buffer);
|
GstBuffer ** buffer);
|
||||||
static gboolean gst_pulsevideo_src_start (GstBaseSrc * basesrc);
|
static gboolean gst_pulsevideo_src_start (GstBaseSrc * basesrc);
|
||||||
|
|
@ -118,10 +114,8 @@ gst_pulsevideo_src_class_init (GstPulsevideoSrcClass * klass)
|
||||||
gstbasesrc_class->get_caps = gst_pulsevideo_src_getcaps;
|
gstbasesrc_class->get_caps = gst_pulsevideo_src_getcaps;
|
||||||
gstbasesrc_class->set_caps = gst_pulsevideo_src_setcaps;
|
gstbasesrc_class->set_caps = gst_pulsevideo_src_setcaps;
|
||||||
gstbasesrc_class->fixate = gst_pulsevideo_src_src_fixate;
|
gstbasesrc_class->fixate = gst_pulsevideo_src_src_fixate;
|
||||||
gstbasesrc_class->query = gst_pulsevideo_src_query;
|
|
||||||
gstbasesrc_class->start = gst_pulsevideo_src_start;
|
gstbasesrc_class->start = gst_pulsevideo_src_start;
|
||||||
gstbasesrc_class->stop = gst_pulsevideo_src_stop;
|
gstbasesrc_class->stop = gst_pulsevideo_src_stop;
|
||||||
gstbasesrc_class->decide_allocation = gst_pulsevideo_src_decide_allocation;
|
|
||||||
|
|
||||||
gstpushsrc_class->create = gst_pulsevideo_src_create;
|
gstpushsrc_class->create = gst_pulsevideo_src_create;
|
||||||
|
|
||||||
|
|
@ -200,59 +194,6 @@ gst_pulsevideo_src_get_property (GObject * object, guint prop_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_pulsevideo_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstPulsevideoSrc *pvsrc;
|
|
||||||
GstBufferPool *pool;
|
|
||||||
gboolean update;
|
|
||||||
guint size, min, max;
|
|
||||||
GstStructure *config;
|
|
||||||
GstCaps *caps = NULL;
|
|
||||||
|
|
||||||
pvsrc = GST_PULSEVIDEO_SRC (bsrc);
|
|
||||||
|
|
||||||
if (gst_query_get_n_allocation_pools (query) > 0) {
|
|
||||||
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
|
|
||||||
|
|
||||||
/* adjust size */
|
|
||||||
size = MAX (size, pvsrc->info.size);
|
|
||||||
update = TRUE;
|
|
||||||
} else {
|
|
||||||
pool = NULL;
|
|
||||||
size = pvsrc->info.size;
|
|
||||||
min = max = 0;
|
|
||||||
update = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* no downstream pool, make our own */
|
|
||||||
if (pool == NULL) {
|
|
||||||
pool = gst_video_buffer_pool_new ();
|
|
||||||
}
|
|
||||||
|
|
||||||
config = gst_buffer_pool_get_config (pool);
|
|
||||||
|
|
||||||
gst_query_parse_allocation (query, &caps, NULL);
|
|
||||||
if (caps)
|
|
||||||
gst_buffer_pool_config_set_params (config, caps, size, min, max);
|
|
||||||
|
|
||||||
if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
|
|
||||||
gst_buffer_pool_config_add_option (config,
|
|
||||||
GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
||||||
}
|
|
||||||
gst_buffer_pool_set_config (pool, config);
|
|
||||||
|
|
||||||
if (update)
|
|
||||||
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
|
|
||||||
else
|
|
||||||
gst_query_add_allocation_pool (query, pool, size, min, max);
|
|
||||||
|
|
||||||
if (pool)
|
|
||||||
gst_object_unref (pool);
|
|
||||||
|
|
||||||
return GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_new_buffer (GObject *gobject,
|
on_new_buffer (GObject *gobject,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
|
|
@ -284,117 +225,108 @@ on_stream_notify (GObject *gobject,
|
||||||
g_mutex_unlock (&pvsrc->lock);
|
g_mutex_unlock (&pvsrc->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
source_info_callback (PvContext *c, const PvSourceInfo *info, gpointer user_data)
|
|
||||||
{
|
|
||||||
GstPulsevideoSrc *pvsrc = user_data;
|
|
||||||
|
|
||||||
if (info == NULL) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_print ("source %s %p\n", info->name, pvsrc);
|
|
||||||
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_pulsevideo_src_negotiate (GstBaseSrc * basesrc)
|
gst_pulsevideo_src_negotiate (GstBaseSrc * basesrc)
|
||||||
{
|
{
|
||||||
GstPulsevideoSrc *pvsrc = GST_PULSEVIDEO_SRC (basesrc);
|
GstPulsevideoSrc *pvsrc = GST_PULSEVIDEO_SRC (basesrc);
|
||||||
|
GstCaps *thiscaps;
|
||||||
|
GstCaps *caps = NULL;
|
||||||
|
GstCaps *peercaps = NULL;
|
||||||
|
gboolean result = FALSE;
|
||||||
|
|
||||||
pv_context_list_source_info (pvsrc->ctx,
|
/* first see what is possible on our source pad */
|
||||||
PV_SOURCE_INFO_FLAGS_CAPABILITIES,
|
thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL);
|
||||||
source_info_callback,
|
GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
|
||||||
NULL,
|
/* nothing or anything is allowed, we're done */
|
||||||
pvsrc);
|
if (thiscaps == NULL || gst_caps_is_any (thiscaps))
|
||||||
|
goto no_nego_needed;
|
||||||
|
|
||||||
return GST_BASE_SRC_CLASS (parent_class)->negotiate (basesrc);
|
if (G_UNLIKELY (gst_caps_is_empty (thiscaps)))
|
||||||
}
|
goto no_caps;
|
||||||
|
|
||||||
static GstCaps *
|
|
||||||
gst_pulsevideo_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
|
|
||||||
{
|
|
||||||
return GST_BASE_SRC_CLASS (parent_class)->get_caps (bsrc, filter);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_pulsevideo_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
|
|
||||||
{
|
|
||||||
const GstStructure *structure;
|
|
||||||
GstPulsevideoSrc *pvsrc;
|
|
||||||
GstVideoInfo info;
|
|
||||||
GVariantBuilder builder;
|
|
||||||
|
|
||||||
pvsrc = GST_PULSEVIDEO_SRC (bsrc);
|
|
||||||
|
|
||||||
structure = gst_caps_get_structure (caps, 0);
|
|
||||||
|
|
||||||
if (gst_structure_has_name (structure, "video/x-raw")) {
|
|
||||||
/* we can use the parsing code */
|
|
||||||
if (!gst_video_info_from_caps (&info, caps))
|
|
||||||
goto parse_failed;
|
|
||||||
|
|
||||||
|
/* get the peer caps */
|
||||||
|
peercaps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (basesrc), thiscaps);
|
||||||
|
GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
|
||||||
|
if (peercaps) {
|
||||||
|
/* The result is already a subset of our caps */
|
||||||
|
caps = peercaps;
|
||||||
|
gst_caps_unref (thiscaps);
|
||||||
} else {
|
} else {
|
||||||
goto unsupported_caps;
|
/* no peer, work with our own caps then */
|
||||||
|
caps = thiscaps;
|
||||||
}
|
}
|
||||||
|
if (caps && !gst_caps_is_empty (caps)) {
|
||||||
|
GBytes *accepted, *possible;
|
||||||
|
gchar *str;
|
||||||
|
|
||||||
/* looks ok here */
|
GST_DEBUG_OBJECT (basesrc, "have caps: %" GST_PTR_FORMAT, caps);
|
||||||
pvsrc->info = info;
|
/* open a connection with these caps */
|
||||||
|
str = gst_caps_to_string (caps);
|
||||||
|
accepted = g_bytes_new_take (str, strlen (str) + 1);
|
||||||
|
pv_stream_connect_capture (pvsrc->stream, NULL, 0, accepted);
|
||||||
|
|
||||||
pvsrc->stream = pv_stream_new (pvsrc->ctx, "test", NULL);
|
g_mutex_lock (&pvsrc->lock);
|
||||||
g_signal_connect (pvsrc->stream, "notify::state", (GCallback) on_stream_notify, pvsrc);
|
while (TRUE) {
|
||||||
g_signal_connect (pvsrc->stream, "new-buffer", (GCallback) on_new_buffer, pvsrc);
|
PvStreamState state = pv_stream_get_state (pvsrc->stream);
|
||||||
|
|
||||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
if (state == PV_STREAM_STATE_READY)
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.encoding", g_variant_new_string ("video/x-raw"));
|
break;
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.format",
|
|
||||||
g_variant_new_string (gst_video_format_to_string (info.finfo->format)));
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.width", g_variant_new_int32 (info.width));
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.height", g_variant_new_int32 (info.height));
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "format.views", g_variant_new_int32 (info.views));
|
|
||||||
// g_variant_builder_add (&builder, "{sv}", "format.chroma-site",
|
|
||||||
// g_variant_new_string (gst_video_chroma_to_string (info.chroma_site)));
|
|
||||||
// g_variant_builder_add (&builder, "{sv}", "format.colorimetry",
|
|
||||||
// g_variant_new_take_string (gst_video_colorimetry_to_string (&info.colorimetry)));
|
|
||||||
// g_variant_builder_add (&builder, "{sv}", "format.interlace-mode",
|
|
||||||
// g_variant_new_string (gst_video_interlace_mode_to_string (info.interlace_mode)));
|
|
||||||
|
|
||||||
pv_stream_connect_capture (pvsrc->stream, NULL, 0, g_variant_builder_end (&builder));
|
if (state == PV_STREAM_STATE_ERROR)
|
||||||
|
goto connect_error;
|
||||||
|
|
||||||
g_mutex_lock (&pvsrc->lock);
|
g_cond_wait (&pvsrc->cond, &pvsrc->lock);
|
||||||
while (TRUE) {
|
}
|
||||||
PvStreamState state = pv_stream_get_state (pvsrc->stream);
|
g_mutex_unlock (&pvsrc->lock);
|
||||||
|
|
||||||
if (state == PV_STREAM_STATE_READY)
|
g_object_get (pvsrc->stream, "possible-formats", &possible, NULL);
|
||||||
break;
|
if (possible) {
|
||||||
|
GstCaps *newcaps;
|
||||||
|
|
||||||
if (state == PV_STREAM_STATE_ERROR)
|
newcaps = gst_caps_from_string (g_bytes_get_data (possible, NULL));
|
||||||
goto connect_error;
|
if (newcaps)
|
||||||
|
caps = newcaps;
|
||||||
g_cond_wait (&pvsrc->cond, &pvsrc->lock);
|
}
|
||||||
|
/* now fixate */
|
||||||
|
GST_DEBUG_OBJECT (basesrc, "server fixated caps: %" GST_PTR_FORMAT, caps);
|
||||||
|
if (gst_caps_is_any (caps)) {
|
||||||
|
GST_DEBUG_OBJECT (basesrc, "any caps, we stop");
|
||||||
|
/* hmm, still anything, so element can do anything and
|
||||||
|
* nego is not needed */
|
||||||
|
result = TRUE;
|
||||||
|
} else {
|
||||||
|
caps = gst_pulsevideo_src_src_fixate (basesrc, caps);
|
||||||
|
GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);
|
||||||
|
if (gst_caps_is_fixed (caps)) {
|
||||||
|
/* yay, fixed caps, use those then, it's possible that the subclass does
|
||||||
|
* not accept this caps after all and we have to fail. */
|
||||||
|
result = gst_base_src_set_caps (basesrc, caps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
} else {
|
||||||
|
if (caps)
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
GST_DEBUG_OBJECT (basesrc, "no common caps");
|
||||||
}
|
}
|
||||||
g_mutex_unlock (&pvsrc->lock);
|
pvsrc->negotiated = result;
|
||||||
|
return result;
|
||||||
|
|
||||||
pv_stream_start (pvsrc->stream, PV_STREAM_MODE_BUFFER);
|
no_nego_needed:
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pvsrc, "size %dx%d, %d/%d fps",
|
|
||||||
info.width, info.height, info.fps_n, info.fps_d);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
parse_failed:
|
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (bsrc, "failed to parse caps");
|
GST_DEBUG_OBJECT (basesrc, "no negotiation needed");
|
||||||
return FALSE;
|
if (thiscaps)
|
||||||
|
gst_caps_unref (thiscaps);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
unsupported_caps:
|
no_caps:
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (bsrc, "unsupported caps: %" GST_PTR_FORMAT, caps);
|
GST_ELEMENT_ERROR (basesrc, STREAM, FORMAT,
|
||||||
return FALSE;
|
("No supported formats found"),
|
||||||
|
("This element did not produce valid caps"));
|
||||||
|
if (thiscaps)
|
||||||
|
gst_caps_unref (thiscaps);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
connect_error:
|
connect_error:
|
||||||
{
|
{
|
||||||
|
|
@ -403,74 +335,25 @@ connect_error:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static GstCaps *
|
||||||
gst_pulsevideo_src_query (GstBaseSrc * bsrc, GstQuery * query)
|
gst_pulsevideo_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
|
||||||
{
|
{
|
||||||
gboolean res = FALSE;
|
return GST_BASE_SRC_CLASS (parent_class)->get_caps (bsrc, filter);
|
||||||
GstPulsevideoSrc *src;
|
}
|
||||||
|
|
||||||
src = GST_PULSEVIDEO_SRC (bsrc);
|
static gboolean
|
||||||
|
gst_pulsevideo_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
|
||||||
|
{
|
||||||
|
GstPulsevideoSrc *pvsrc;
|
||||||
|
gchar *str;
|
||||||
|
GBytes *format;
|
||||||
|
|
||||||
switch (GST_QUERY_TYPE (query)) {
|
pvsrc = GST_PULSEVIDEO_SRC (bsrc);
|
||||||
case GST_QUERY_CONVERT:
|
|
||||||
{
|
|
||||||
GstFormat src_fmt, dest_fmt;
|
|
||||||
gint64 src_val, dest_val;
|
|
||||||
|
|
||||||
gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
|
str = gst_caps_to_string (caps);
|
||||||
res =
|
format = g_bytes_new_take (str, strlen (str) + 1);
|
||||||
gst_video_info_convert (&src->info, src_fmt, src_val, dest_fmt,
|
|
||||||
&dest_val);
|
|
||||||
gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GST_QUERY_LATENCY:
|
|
||||||
{
|
|
||||||
if (src->info.fps_n > 0) {
|
|
||||||
GstClockTime latency;
|
|
||||||
|
|
||||||
latency =
|
return pv_stream_start (pvsrc->stream, format, PV_STREAM_MODE_BUFFER);
|
||||||
gst_util_uint64_scale (GST_SECOND, src->info.fps_d,
|
|
||||||
src->info.fps_n);
|
|
||||||
gst_query_set_latency (query,
|
|
||||||
gst_base_src_is_live (GST_BASE_SRC_CAST (src)), latency,
|
|
||||||
GST_CLOCK_TIME_NONE);
|
|
||||||
GST_DEBUG_OBJECT (src, "Reporting latency of %" GST_TIME_FORMAT,
|
|
||||||
GST_TIME_ARGS (latency));
|
|
||||||
res = TRUE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GST_QUERY_DURATION:{
|
|
||||||
if (bsrc->num_buffers != -1) {
|
|
||||||
GstFormat format;
|
|
||||||
|
|
||||||
gst_query_parse_duration (query, &format, NULL);
|
|
||||||
switch (format) {
|
|
||||||
case GST_FORMAT_TIME:{
|
|
||||||
gint64 dur = gst_util_uint64_scale_int_round (bsrc->num_buffers
|
|
||||||
* GST_SECOND, src->info.fps_d, src->info.fps_n);
|
|
||||||
res = TRUE;
|
|
||||||
gst_query_set_duration (query, GST_FORMAT_TIME, dur);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
case GST_FORMAT_BYTES:
|
|
||||||
res = TRUE;
|
|
||||||
gst_query_set_duration (query, GST_FORMAT_BYTES,
|
|
||||||
bsrc->num_buffers * src->info.size);
|
|
||||||
goto done;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* fall through */
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
done:
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
|
|
@ -481,8 +364,7 @@ gst_pulsevideo_src_create (GstPushSrc * psrc, GstBuffer ** buffer)
|
||||||
|
|
||||||
pvsrc = GST_PULSEVIDEO_SRC (psrc);
|
pvsrc = GST_PULSEVIDEO_SRC (psrc);
|
||||||
|
|
||||||
if (G_UNLIKELY (GST_VIDEO_INFO_FORMAT (&pvsrc->info) ==
|
if (!pvsrc->negotiated)
|
||||||
GST_VIDEO_FORMAT_UNKNOWN))
|
|
||||||
goto not_negotiated;
|
goto not_negotiated;
|
||||||
|
|
||||||
g_mutex_lock (&pvsrc->lock);
|
g_mutex_lock (&pvsrc->lock);
|
||||||
|
|
@ -516,10 +398,6 @@ not_negotiated:
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_pulsevideo_src_start (GstBaseSrc * basesrc)
|
gst_pulsevideo_src_start (GstBaseSrc * basesrc)
|
||||||
{
|
{
|
||||||
GstPulsevideoSrc *src = GST_PULSEVIDEO_SRC (basesrc);
|
|
||||||
|
|
||||||
gst_video_info_init (&src->info);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -585,6 +463,10 @@ gst_pulsevideo_src_open (GstPulsevideoSrc * pvsrc)
|
||||||
}
|
}
|
||||||
g_mutex_unlock (&pvsrc->lock);
|
g_mutex_unlock (&pvsrc->lock);
|
||||||
|
|
||||||
|
pvsrc->stream = pv_stream_new (pvsrc->ctx, "test", NULL);
|
||||||
|
g_signal_connect (pvsrc->stream, "notify::state", (GCallback) on_stream_notify, pvsrc);
|
||||||
|
g_signal_connect (pvsrc->stream, "new-buffer", (GCallback) on_new_buffer, pvsrc);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
/* ERRORS */
|
/* ERRORS */
|
||||||
|
|
@ -607,13 +489,13 @@ gst_pulsevideo_src_change_state (GstElement * element, GstStateChange transition
|
||||||
g_print ("context %p\n", this->context);
|
g_print ("context %p\n", this->context);
|
||||||
this->loop = g_main_loop_new (this->context, FALSE);
|
this->loop = g_main_loop_new (this->context, FALSE);
|
||||||
this->thread = g_thread_new ("pulsevideo", (GThreadFunc) handle_mainloop, this);
|
this->thread = g_thread_new ("pulsevideo", (GThreadFunc) handle_mainloop, this);
|
||||||
break;
|
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
||||||
if (!gst_pulsevideo_src_open (this)) {
|
if (!gst_pulsevideo_src_open (this)) {
|
||||||
ret = GST_STATE_CHANGE_FAILURE;
|
ret = GST_STATE_CHANGE_FAILURE;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||||
/* uncork and start recording */
|
/* uncork and start recording */
|
||||||
break;
|
break;
|
||||||
|
|
@ -630,6 +512,7 @@ gst_pulsevideo_src_change_state (GstElement * element, GstStateChange transition
|
||||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
|
this->negotiated = FALSE;
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||||
g_main_loop_quit (this->loop);
|
g_main_loop_quit (this->loop);
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,7 @@ struct _GstPulsevideoSrc {
|
||||||
|
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
|
|
||||||
/* video state */
|
gboolean negotiated;
|
||||||
GstVideoInfo info;
|
|
||||||
|
|
||||||
GMainContext *context;
|
GMainContext *context;
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
|
@ -32,6 +33,8 @@ struct _PvV4l2SourcePrivate
|
||||||
GstElement *filter;
|
GstElement *filter;
|
||||||
GstElement *sink;
|
GstElement *sink;
|
||||||
|
|
||||||
|
GstCaps *possible_formats;
|
||||||
|
|
||||||
GSocket *socket;
|
GSocket *socket;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -90,18 +93,20 @@ setup_pipeline (PvV4l2Source *source)
|
||||||
gst_object_unref (bus);
|
gst_object_unref (bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static GstCaps *
|
||||||
collect_capabilities (PvSource * source)
|
collect_caps (PvSource * source, GstCaps *filter)
|
||||||
{
|
{
|
||||||
PvV4l2SourcePrivate *priv = PV_V4L2_SOURCE (source)->priv;
|
PvV4l2SourcePrivate *priv = PV_V4L2_SOURCE (source)->priv;
|
||||||
GstCaps *res;
|
GstCaps *res;
|
||||||
GstQuery *query;
|
GstQuery *query;
|
||||||
|
|
||||||
query = gst_query_new_caps (NULL);
|
query = gst_query_new_caps (filter);
|
||||||
gst_element_query (priv->src, query);
|
gst_element_query (priv->src, query);
|
||||||
gst_query_parse_caps_result (query, &res);
|
gst_query_parse_caps_result (query, &res);
|
||||||
g_print ("%s\n", gst_caps_to_string (res));
|
gst_caps_ref (res);
|
||||||
gst_query_unref (query);
|
gst_query_unref (query);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
|
@ -116,7 +121,6 @@ v4l2_set_state (PvSource *source, PvSourceState state)
|
||||||
|
|
||||||
case PV_SOURCE_STATE_INIT:
|
case PV_SOURCE_STATE_INIT:
|
||||||
gst_element_set_state (priv->pipeline, GST_STATE_READY);
|
gst_element_set_state (priv->pipeline, GST_STATE_READY);
|
||||||
collect_capabilities (source);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PV_SOURCE_STATE_IDLE:
|
case PV_SOURCE_STATE_IDLE:
|
||||||
|
|
@ -134,8 +138,8 @@ v4l2_set_state (PvSource *source, PvSourceState state)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GVariant *
|
static GBytes *
|
||||||
v4l2_get_capabilities (PvSource *source, GVariant *props)
|
v4l2_get_capabilities (PvSource *source, GBytes *filter)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -149,6 +153,8 @@ on_socket_notify (GObject *gobject,
|
||||||
PvV4l2SourcePrivate *priv = source->priv;
|
PvV4l2SourcePrivate *priv = source->priv;
|
||||||
GSocket *socket;
|
GSocket *socket;
|
||||||
guint num_handles;
|
guint num_handles;
|
||||||
|
GstCaps *caps;
|
||||||
|
GBytes *requested_format;
|
||||||
|
|
||||||
g_object_get (gobject, "socket", &socket, NULL);
|
g_object_get (gobject, "socket", &socket, NULL);
|
||||||
|
|
||||||
|
|
@ -162,6 +168,18 @@ on_socket_notify (GObject *gobject,
|
||||||
}
|
}
|
||||||
priv->socket = 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_get (priv->sink, "num-handles", &num_handles, NULL);
|
g_object_get (priv->sink, "num-handles", &num_handles, NULL);
|
||||||
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);
|
||||||
|
|
@ -171,53 +189,45 @@ on_socket_notify (GObject *gobject,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PvSourceOutput *
|
static PvSourceOutput *
|
||||||
v4l2_create_source_output (PvSource *source, GVariant *props, const gchar *prefix)
|
v4l2_create_source_output (PvSource *source,
|
||||||
|
const gchar *client_path,
|
||||||
|
GBytes *format_filter,
|
||||||
|
const gchar *prefix)
|
||||||
{
|
{
|
||||||
PvV4l2SourcePrivate *priv = PV_V4L2_SOURCE (source)->priv;
|
PvV4l2SourcePrivate *priv = PV_V4L2_SOURCE (source)->priv;
|
||||||
PvSourceOutput *output;
|
PvSourceOutput *output;
|
||||||
GVariantDict dict;
|
GstCaps *caps, *filtered;
|
||||||
GstCaps *caps;
|
gchar *str;
|
||||||
const gchar *str;
|
|
||||||
gint32 i32;
|
|
||||||
|
|
||||||
g_variant_dict_init (&dict, props);
|
str = (gchar *) g_bytes_get_data (format_filter, NULL);
|
||||||
if (!g_variant_dict_lookup (&dict, "format.encoding", "&s", &str))
|
g_print ("input filter %s\n", str);
|
||||||
goto invalid_encoding;
|
caps = gst_caps_from_string (str);
|
||||||
|
if (caps == NULL)
|
||||||
caps = gst_caps_new_empty_simple (str);
|
goto invalid_caps;
|
||||||
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.format", "&s", &str))
|
|
||||||
gst_caps_set_simple (caps, "format", G_TYPE_STRING, str, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.width", "i", &i32))
|
|
||||||
gst_caps_set_simple (caps, "width", G_TYPE_INT, (gint) i32, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.height", "i", &i32))
|
|
||||||
gst_caps_set_simple (caps, "height", G_TYPE_INT, (gint) i32, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.views", "i", &i32))
|
|
||||||
gst_caps_set_simple (caps, "views", G_TYPE_INT, (gint) i32, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.chroma-site", "&s", &str))
|
|
||||||
gst_caps_set_simple (caps, "chroma-site", G_TYPE_STRING, str, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.colorimetry", "&s", &str))
|
|
||||||
gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, str, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.interlace-mode", "&s", &str))
|
|
||||||
gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, str, NULL);
|
|
||||||
|
|
||||||
g_print ("caps %s\n", gst_caps_to_string (caps));
|
|
||||||
|
|
||||||
g_object_set (priv->filter, "caps", caps, NULL);
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
|
|
||||||
output = PV_SOURCE_CLASS (pv_v4l2_source_parent_class)->create_source_output (source, props, prefix);
|
|
||||||
|
|
||||||
gst_element_set_state (priv->pipeline, GST_STATE_READY);
|
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;
|
||||||
|
|
||||||
|
str = gst_caps_to_string (filtered);
|
||||||
|
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);
|
||||||
|
|
||||||
g_signal_connect (output, "notify::socket", (GCallback) on_socket_notify, source);
|
g_signal_connect (output, "notify::socket", (GCallback) on_socket_notify, source);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
|
||||||
/* ERRORS */
|
/* ERRORS */
|
||||||
invalid_encoding:
|
invalid_caps:
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
no_format:
|
||||||
{
|
{
|
||||||
g_variant_dict_clear (&dict);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
|
||||||
#include "server/pv-daemon.h"
|
#include <server/pv-daemon.h>
|
||||||
#include "pv-client-source.h"
|
#include "pv-client-source.h"
|
||||||
|
|
||||||
#define PV_CLIENT_SOURCE_GET_PRIVATE(obj) \
|
#define PV_CLIENT_SOURCE_GET_PRIVATE(obj) \
|
||||||
|
|
@ -102,7 +102,7 @@ collect_capabilities (PvSource * source)
|
||||||
query = gst_query_new_caps (NULL);
|
query = gst_query_new_caps (NULL);
|
||||||
gst_element_query (priv->src, query);
|
gst_element_query (priv->src, query);
|
||||||
gst_query_parse_caps_result (query, &res);
|
gst_query_parse_caps_result (query, &res);
|
||||||
g_print ("%s\n", gst_caps_to_string (res));
|
g_print ("client source caps: %s\n", gst_caps_to_string (res));
|
||||||
gst_query_unref (query);
|
gst_query_unref (query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,8 +136,8 @@ client_set_state (PvSource *source, PvSourceState state)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GVariant *
|
static GBytes *
|
||||||
client_get_capabilities (PvSource *source, GVariant *props)
|
client_get_capabilities (PvSource *source, GBytes *filter)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +151,7 @@ on_socket_notify (GObject *gobject,
|
||||||
PvClientSourcePrivate *priv = source->priv;
|
PvClientSourcePrivate *priv = source->priv;
|
||||||
GSocket *socket;
|
GSocket *socket;
|
||||||
guint num_handles;
|
guint num_handles;
|
||||||
|
GBytes *requested_format;
|
||||||
|
|
||||||
g_object_get (gobject, "socket", &socket, NULL);
|
g_object_get (gobject, "socket", &socket, NULL);
|
||||||
|
|
||||||
|
|
@ -164,6 +165,13 @@ on_socket_notify (GObject *gobject,
|
||||||
}
|
}
|
||||||
priv->socket = socket;
|
priv->socket = socket;
|
||||||
|
|
||||||
|
/* force format on input */
|
||||||
|
g_object_get (priv->input, "format", &requested_format, NULL);
|
||||||
|
g_assert (requested_format != NULL);
|
||||||
|
g_print ("final format %s\n", (gchar *) g_bytes_get_data (requested_format, NULL));
|
||||||
|
g_object_set (gobject, "format", requested_format, NULL);
|
||||||
|
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);
|
||||||
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);
|
||||||
|
|
@ -173,55 +181,24 @@ on_socket_notify (GObject *gobject,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PvSourceOutput *
|
static PvSourceOutput *
|
||||||
client_create_source_output (PvSource *source, GVariant *props, const gchar *prefix)
|
client_create_source_output (PvSource *source,
|
||||||
|
const gchar *client_path,
|
||||||
|
GBytes *format_filter,
|
||||||
|
const gchar *prefix)
|
||||||
{
|
{
|
||||||
PvClientSourcePrivate *priv = PV_CLIENT_SOURCE (source)->priv;
|
PvClientSourcePrivate *priv = PV_CLIENT_SOURCE (source)->priv;
|
||||||
PvSourceOutput *output;
|
PvSourceOutput *output;
|
||||||
GVariantDict dict;
|
|
||||||
GstCaps *caps;
|
|
||||||
const gchar *str;
|
|
||||||
gint32 i32;
|
|
||||||
|
|
||||||
g_variant_dict_init (&dict, props);
|
/* propose format of input */
|
||||||
if (!g_variant_dict_lookup (&dict, "format.encoding", "&s", &str))
|
g_object_get (priv->input, "format", &format_filter, NULL);
|
||||||
goto invalid_encoding;
|
|
||||||
|
|
||||||
caps = gst_caps_new_empty_simple (str);
|
output = PV_SOURCE_CLASS (pv_client_source_parent_class)->create_source_output (source, client_path, format_filter, prefix);
|
||||||
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.format", "&s", &str))
|
|
||||||
gst_caps_set_simple (caps, "format", G_TYPE_STRING, str, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.width", "i", &i32))
|
|
||||||
gst_caps_set_simple (caps, "width", G_TYPE_INT, (gint) i32, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.height", "i", &i32))
|
|
||||||
gst_caps_set_simple (caps, "height", G_TYPE_INT, (gint) i32, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.views", "i", &i32))
|
|
||||||
gst_caps_set_simple (caps, "views", G_TYPE_INT, (gint) i32, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.chroma-site", "&s", &str))
|
|
||||||
gst_caps_set_simple (caps, "chroma-site", G_TYPE_STRING, str, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.colorimetry", "&s", &str))
|
|
||||||
gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, str, NULL);
|
|
||||||
if (g_variant_dict_lookup (&dict, "format.interlace-mode", "&s", &str))
|
|
||||||
gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, str, NULL);
|
|
||||||
|
|
||||||
g_print ("caps %s\n", gst_caps_to_string (caps));
|
|
||||||
|
|
||||||
g_object_set (priv->filter, "caps", caps, NULL);
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
|
|
||||||
output = PV_SOURCE_CLASS (pv_client_source_parent_class)->create_source_output (source, props, prefix);
|
|
||||||
|
|
||||||
gst_element_set_state (priv->pipeline, GST_STATE_READY);
|
gst_element_set_state (priv->pipeline, GST_STATE_READY);
|
||||||
|
|
||||||
g_signal_connect (output, "notify::socket", (GCallback) on_socket_notify, source);
|
g_signal_connect (output, "notify::socket", (GCallback) on_socket_notify, source);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
invalid_encoding:
|
|
||||||
{
|
|
||||||
g_variant_dict_clear (&dict);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
|
@ -245,14 +222,31 @@ on_input_socket_notify (GObject *gobject,
|
||||||
PvClientSource *source = user_data;
|
PvClientSource *source = user_data;
|
||||||
PvClientSourcePrivate *priv = source->priv;
|
PvClientSourcePrivate *priv = source->priv;
|
||||||
GSocket *socket;
|
GSocket *socket;
|
||||||
|
GBytes *requested_format;
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
g_object_get (gobject, "socket", &socket, NULL);
|
g_object_get (gobject, "socket", &socket, NULL);
|
||||||
g_print ("input socket %p\n", socket);
|
g_print ("input socket %p\n", socket);
|
||||||
|
|
||||||
|
g_object_get (gobject, "requested-format", &requested_format, NULL);
|
||||||
|
g_assert (requested_format != NULL);
|
||||||
|
g_print ("final format %s\n", (gchar *) g_bytes_get_data (requested_format, NULL));
|
||||||
|
g_object_set (gobject, "format", 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 (priv->src, "socket", socket, NULL);
|
g_object_set (priv->src, "socket", socket, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
PvSourceOutput *
|
PvSourceOutput *
|
||||||
pv_client_source_get_source_input (PvClientSource *source, GVariant *props, const gchar *prefix)
|
pv_client_source_get_source_input (PvClientSource *source,
|
||||||
|
const gchar *client_path,
|
||||||
|
GBytes *format_filter,
|
||||||
|
const gchar *prefix)
|
||||||
{
|
{
|
||||||
PvClientSourcePrivate *priv;
|
PvClientSourcePrivate *priv;
|
||||||
|
|
||||||
|
|
@ -260,7 +254,7 @@ pv_client_source_get_source_input (PvClientSource *source, GVariant *props, cons
|
||||||
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), props, prefix);
|
priv->input = PV_SOURCE_CLASS (pv_client_source_parent_class)->create_source_output (PV_SOURCE (source), client_path, format_filter, prefix);
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,9 @@ GType pv_client_source_get_type (void);
|
||||||
PvSource * pv_client_source_new (PvDaemon *daemon);
|
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,
|
||||||
GVariant *props,
|
const gchar *client_path,
|
||||||
const gchar *prefix);
|
GBytes *format_filter,
|
||||||
|
const gchar *prefix);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include "client/pulsevideo.h"
|
#include "client/pulsevideo.h"
|
||||||
|
|
||||||
#include "client/pv-enumtypes.h"
|
#include "client/pv-enumtypes.h"
|
||||||
|
|
@ -31,6 +32,7 @@ struct _PvClientPrivate
|
||||||
PvDaemon *daemon;
|
PvDaemon *daemon;
|
||||||
gchar *sender;
|
gchar *sender;
|
||||||
gchar *object_path;
|
gchar *object_path;
|
||||||
|
GVariant *properties;
|
||||||
|
|
||||||
PvClient1 *client1;
|
PvClient1 *client1;
|
||||||
};
|
};
|
||||||
|
|
@ -46,6 +48,7 @@ enum
|
||||||
PROP_DAEMON,
|
PROP_DAEMON,
|
||||||
PROP_SENDER,
|
PROP_SENDER,
|
||||||
PROP_OBJECT_PATH,
|
PROP_OBJECT_PATH,
|
||||||
|
PROP_PROPERTIES,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -70,6 +73,10 @@ pv_client_get_property (GObject *_object,
|
||||||
g_value_set_string (value, priv->object_path);
|
g_value_set_string (value, priv->object_path);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_PROPERTIES:
|
||||||
|
g_value_set_variant (value, priv->properties);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (client, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (client, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
|
@ -98,6 +105,12 @@ pv_client_set_property (GObject *_object,
|
||||||
priv->object_path = g_value_dup_string (value);
|
priv->object_path = g_value_dup_string (value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_PROPERTIES:
|
||||||
|
if (priv->properties)
|
||||||
|
g_variant_unref (priv->properties);
|
||||||
|
priv->properties = g_value_dup_variant (value);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (client, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (client, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
|
@ -108,7 +121,7 @@ static gboolean
|
||||||
handle_create_source_output (PvClient1 *interface,
|
handle_create_source_output (PvClient1 *interface,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
const gchar *arg_source,
|
const gchar *arg_source,
|
||||||
GVariant *arg_properties,
|
const gchar *arg_accepted_formats,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
PvClient *client = user_data;
|
PvClient *client = user_data;
|
||||||
|
|
@ -116,12 +129,15 @@ handle_create_source_output (PvClient1 *interface,
|
||||||
PvSource *source;
|
PvSource *source;
|
||||||
PvSourceOutput *output;
|
PvSourceOutput *output;
|
||||||
const gchar *object_path, *sender;
|
const gchar *object_path, *sender;
|
||||||
|
GBytes *formats;
|
||||||
|
|
||||||
source = pv_daemon_find_source (priv->daemon, arg_source, arg_properties);
|
formats = g_bytes_new (arg_accepted_formats, strlen (arg_accepted_formats) + 1);
|
||||||
|
|
||||||
|
source = pv_daemon_find_source (priv->daemon, arg_source, priv->properties, formats);
|
||||||
if (source == NULL)
|
if (source == NULL)
|
||||||
goto no_source;
|
goto no_source;
|
||||||
|
|
||||||
output = pv_source_create_source_output (source, arg_properties, priv->object_path);
|
output = pv_source_create_source_output (source, priv->object_path, formats, priv->object_path);
|
||||||
if (output == NULL)
|
if (output == NULL)
|
||||||
goto no_output;
|
goto no_output;
|
||||||
|
|
||||||
|
|
@ -153,7 +169,7 @@ no_output:
|
||||||
static gboolean
|
static gboolean
|
||||||
handle_create_source_input (PvClient1 *interface,
|
handle_create_source_input (PvClient1 *interface,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
GVariant *arg_properties,
|
const gchar *arg_possible_formats,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
PvClient *client = user_data;
|
PvClient *client = user_data;
|
||||||
|
|
@ -161,6 +177,7 @@ handle_create_source_input (PvClient1 *interface,
|
||||||
PvSource *source;
|
PvSource *source;
|
||||||
PvSourceOutput *input;
|
PvSourceOutput *input;
|
||||||
const gchar *source_input_path, *sender;
|
const gchar *source_input_path, *sender;
|
||||||
|
GBytes *formats;
|
||||||
|
|
||||||
source = pv_client_source_new (priv->daemon);
|
source = pv_client_source_new (priv->daemon);
|
||||||
if (source == NULL)
|
if (source == NULL)
|
||||||
|
|
@ -170,7 +187,12 @@ handle_create_source_input (PvClient1 *interface,
|
||||||
|
|
||||||
pv_daemon_track_object (priv->daemon, sender, G_OBJECT (source));
|
pv_daemon_track_object (priv->daemon, sender, G_OBJECT (source));
|
||||||
|
|
||||||
input = pv_client_source_get_source_input (PV_CLIENT_SOURCE (source), arg_properties, priv->object_path);
|
formats = g_bytes_new (arg_possible_formats, strlen (arg_possible_formats) + 1);
|
||||||
|
|
||||||
|
input = pv_client_source_get_source_input (PV_CLIENT_SOURCE (source),
|
||||||
|
priv->object_path,
|
||||||
|
formats,
|
||||||
|
priv->object_path);
|
||||||
if (input == NULL)
|
if (input == NULL)
|
||||||
goto no_input;
|
goto no_input;
|
||||||
|
|
||||||
|
|
@ -179,7 +201,7 @@ handle_create_source_input (PvClient1 *interface,
|
||||||
source_input_path = pv_source_output_get_object_path (input);
|
source_input_path = pv_source_output_get_object_path (input);
|
||||||
g_dbus_method_invocation_return_value (invocation,
|
g_dbus_method_invocation_return_value (invocation,
|
||||||
g_variant_new ("(o)",
|
g_variant_new ("(o)",
|
||||||
source_input_path));
|
source_input_path));
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
|
@ -240,9 +262,13 @@ static void
|
||||||
pv_client_finalize (GObject * object)
|
pv_client_finalize (GObject * object)
|
||||||
{
|
{
|
||||||
PvClient *client = PV_CLIENT (object);
|
PvClient *client = PV_CLIENT (object);
|
||||||
|
PvClientPrivate *priv = client->priv;
|
||||||
|
|
||||||
client_unregister_object (client);
|
client_unregister_object (client);
|
||||||
|
|
||||||
|
if (priv->properties)
|
||||||
|
g_variant_unref (priv->properties);
|
||||||
|
|
||||||
G_OBJECT_CLASS (pv_client_parent_class)->finalize (object);
|
G_OBJECT_CLASS (pv_client_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,6 +324,15 @@ pv_client_class_init (PvClientClass * klass)
|
||||||
G_PARAM_READWRITE |
|
G_PARAM_READWRITE |
|
||||||
G_PARAM_CONSTRUCT_ONLY |
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_PROPERTIES,
|
||||||
|
g_param_spec_variant ("properties",
|
||||||
|
"Properties",
|
||||||
|
"Client properties",
|
||||||
|
G_VARIANT_TYPE_DICTIONARY,
|
||||||
|
NULL,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -317,12 +352,19 @@ pv_client_init (PvClient * client)
|
||||||
* Returns: a new #PvClient
|
* Returns: a new #PvClient
|
||||||
*/
|
*/
|
||||||
PvClient *
|
PvClient *
|
||||||
pv_client_new (PvDaemon * daemon, const gchar *sender, const gchar *prefix)
|
pv_client_new (PvDaemon *daemon,
|
||||||
|
const gchar *sender,
|
||||||
|
const gchar *prefix,
|
||||||
|
GVariant *properties)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (PV_IS_DAEMON (daemon), NULL);
|
g_return_val_if_fail (PV_IS_DAEMON (daemon), NULL);
|
||||||
g_return_val_if_fail (g_variant_is_object_path (prefix), NULL);
|
g_return_val_if_fail (g_variant_is_object_path (prefix), NULL);
|
||||||
|
|
||||||
return g_object_new (PV_TYPE_CLIENT, "daemon", daemon, "sender", sender, "object-path", prefix, NULL);
|
return g_object_new (PV_TYPE_CLIENT, "daemon", daemon,
|
||||||
|
"sender", sender,
|
||||||
|
"object-path", prefix,
|
||||||
|
"properties", properties,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,10 @@ struct _PvClientClass {
|
||||||
/* normal GObject stuff */
|
/* normal GObject stuff */
|
||||||
GType pv_client_get_type (void);
|
GType pv_client_get_type (void);
|
||||||
|
|
||||||
PvClient * pv_client_new (PvDaemon *daemon, const gchar *sender, const gchar *prefix);
|
PvClient * pv_client_new (PvDaemon *daemon,
|
||||||
|
const gchar *sender,
|
||||||
|
const gchar *prefix,
|
||||||
|
GVariant *properties);
|
||||||
|
|
||||||
const gchar * pv_client_get_object_path (PvClient *client);
|
const gchar * pv_client_get_object_path (PvClient *client);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ handle_connect_client (PvDaemon1 *interface,
|
||||||
sender = g_dbus_method_invocation_get_sender (invocation);
|
sender = g_dbus_method_invocation_get_sender (invocation);
|
||||||
|
|
||||||
g_print ("connect client %s\n", sender);
|
g_print ("connect client %s\n", sender);
|
||||||
client = pv_client_new (daemon, sender, PV_DBUS_OBJECT_PREFIX);
|
client = pv_client_new (daemon, sender, PV_DBUS_OBJECT_PREFIX, arg_properties);
|
||||||
|
|
||||||
pv_daemon_track_object (daemon, sender, G_OBJECT (client));
|
pv_daemon_track_object (daemon, sender, G_OBJECT (client));
|
||||||
|
|
||||||
|
|
@ -368,7 +368,10 @@ pv_daemon_remove_source (PvDaemon *daemon, PvSource *source)
|
||||||
}
|
}
|
||||||
|
|
||||||
PvSource *
|
PvSource *
|
||||||
pv_daemon_find_source (PvDaemon *daemon, const gchar *name, GVariant *props)
|
pv_daemon_find_source (PvDaemon *daemon,
|
||||||
|
const gchar *name,
|
||||||
|
GVariant *props,
|
||||||
|
GBytes *format_filter)
|
||||||
{
|
{
|
||||||
PvDaemonPrivate *priv;
|
PvDaemonPrivate *priv;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,10 @@ void pv_daemon_track_object (PvDaemon *daemon, const gchar *sen
|
||||||
|
|
||||||
void pv_daemon_add_source (PvDaemon *daemon, PvSource *source);
|
void pv_daemon_add_source (PvDaemon *daemon, PvSource *source);
|
||||||
void pv_daemon_remove_source (PvDaemon *daemon, PvSource *source);
|
void pv_daemon_remove_source (PvDaemon *daemon, PvSource *source);
|
||||||
PvSource * pv_daemon_find_source (PvDaemon *daemon, const gchar *name, GVariant *props);
|
PvSource * pv_daemon_find_source (PvDaemon *daemon,
|
||||||
|
const gchar *name,
|
||||||
|
GVariant *props,
|
||||||
|
GBytes *format_filter);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#include <gio/gunixfdlist.h>
|
#include <gio/gunixfdlist.h>
|
||||||
|
|
@ -31,9 +32,15 @@
|
||||||
struct _PvSourceOutputPrivate
|
struct _PvSourceOutputPrivate
|
||||||
{
|
{
|
||||||
PvDaemon *daemon;
|
PvDaemon *daemon;
|
||||||
|
PvSourceOutput1 *iface;
|
||||||
|
|
||||||
gchar *object_path;
|
gchar *object_path;
|
||||||
gchar *source;
|
gchar *client_path;
|
||||||
|
gchar *source_path;
|
||||||
|
|
||||||
|
GBytes *possible_formats;
|
||||||
|
GBytes *requested_format;
|
||||||
|
GBytes *format;
|
||||||
|
|
||||||
GSocket *socket;
|
GSocket *socket;
|
||||||
};
|
};
|
||||||
|
|
@ -48,7 +55,11 @@ enum
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_DAEMON,
|
PROP_DAEMON,
|
||||||
PROP_OBJECT_PATH,
|
PROP_OBJECT_PATH,
|
||||||
PROP_SOURCE,
|
PROP_CLIENT_PATH,
|
||||||
|
PROP_SOURCE_PATH,
|
||||||
|
PROP_POSSIBLE_FORMATS,
|
||||||
|
PROP_REQUESTED_FORMAT,
|
||||||
|
PROP_FORMAT,
|
||||||
PROP_SOCKET,
|
PROP_SOCKET,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -70,8 +81,24 @@ pv_source_output_get_property (GObject *_object,
|
||||||
g_value_set_string (value, priv->object_path);
|
g_value_set_string (value, priv->object_path);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PROP_SOURCE:
|
case PROP_CLIENT_PATH:
|
||||||
g_value_set_string (value, priv->source);
|
g_value_set_string (value, priv->client_path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_SOURCE_PATH:
|
||||||
|
g_value_set_string (value, priv->source_path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_POSSIBLE_FORMATS:
|
||||||
|
g_value_set_boxed (value, priv->possible_formats);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_REQUESTED_FORMAT:
|
||||||
|
g_value_set_boxed (value, priv->requested_format);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_FORMAT:
|
||||||
|
g_value_set_boxed (value, priv->format);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PROP_SOCKET:
|
case PROP_SOCKET:
|
||||||
|
|
@ -102,8 +129,28 @@ pv_source_output_set_property (GObject *_object,
|
||||||
priv->object_path = g_value_dup_string (value);
|
priv->object_path = g_value_dup_string (value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PROP_SOURCE:
|
case PROP_CLIENT_PATH:
|
||||||
priv->source = g_value_dup_string (value);
|
priv->client_path = g_value_dup_string (value);
|
||||||
|
g_object_set (priv->iface, "client", priv->client_path, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_SOURCE_PATH:
|
||||||
|
priv->source_path = g_value_dup_string (value);
|
||||||
|
g_object_set (priv->iface, "source", priv->source_path, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_POSSIBLE_FORMATS:
|
||||||
|
if (priv->possible_formats)
|
||||||
|
g_bytes_unref (priv->possible_formats);
|
||||||
|
priv->possible_formats = g_value_dup_boxed (value);
|
||||||
|
g_object_set (priv->iface, "possible-formats",
|
||||||
|
g_bytes_get_data (priv->possible_formats, NULL), NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_FORMAT:
|
||||||
|
if (priv->format)
|
||||||
|
g_bytes_unref (priv->format);
|
||||||
|
priv->format = g_value_dup_boxed (value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -115,32 +162,45 @@ pv_source_output_set_property (GObject *_object,
|
||||||
static gboolean
|
static gboolean
|
||||||
handle_start (PvSourceOutput1 *interface,
|
handle_start (PvSourceOutput1 *interface,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
GVariant *arg_properties,
|
const gchar *arg_requested_format,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
PvSourceOutput *output = user_data;
|
PvSourceOutput *output = user_data;
|
||||||
PvSourceOutputPrivate *priv = output->priv;
|
PvSourceOutputPrivate *priv = output->priv;
|
||||||
GUnixFDList *fdlist;
|
GUnixFDList *fdlist;
|
||||||
GVariantBuilder props;
|
|
||||||
gint fd[2];
|
gint fd[2];
|
||||||
|
|
||||||
|
priv->requested_format = g_bytes_new (arg_requested_format, strlen (arg_requested_format) + 1);
|
||||||
|
|
||||||
socketpair (AF_UNIX, SOCK_STREAM, 0, fd);
|
socketpair (AF_UNIX, SOCK_STREAM, 0, fd);
|
||||||
priv->socket = g_socket_new_from_fd (fd[0], NULL);
|
priv->socket = g_socket_new_from_fd (fd[0], NULL);
|
||||||
g_object_notify (G_OBJECT (output), "socket");
|
g_object_notify (G_OBJECT (output), "socket");
|
||||||
|
|
||||||
g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}"));
|
if (priv->format == NULL)
|
||||||
g_variant_builder_add (&props, "{sv}", "name", g_variant_new_string ("hello"));
|
goto no_format;
|
||||||
|
|
||||||
fdlist = g_unix_fd_list_new ();
|
fdlist = g_unix_fd_list_new ();
|
||||||
g_unix_fd_list_append (fdlist, fd[1], NULL);
|
g_unix_fd_list_append (fdlist, fd[1], NULL);
|
||||||
|
|
||||||
g_dbus_method_invocation_return_value_with_unix_fd_list (invocation,
|
g_dbus_method_invocation_return_value_with_unix_fd_list (invocation,
|
||||||
g_variant_new ("(h@a{sv})",
|
g_variant_new ("(hs)",
|
||||||
0,
|
0,
|
||||||
g_variant_builder_end (&props)),
|
g_bytes_get_data (priv->format, NULL)),
|
||||||
fdlist);
|
fdlist);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
/* error */
|
||||||
|
no_format:
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_dbus_error (invocation,
|
||||||
|
"org.pulsevideo.Error", "No format");
|
||||||
|
close (fd[0]);
|
||||||
|
close (fd[1]);
|
||||||
|
g_clear_pointer (&priv->requested_format, g_bytes_unref);
|
||||||
|
g_clear_object (&priv->socket);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -150,6 +210,8 @@ stop_transfer (PvSourceOutput *output)
|
||||||
|
|
||||||
if (priv->socket) {
|
if (priv->socket) {
|
||||||
g_clear_object (&priv->socket);
|
g_clear_object (&priv->socket);
|
||||||
|
g_clear_pointer (&priv->requested_format, g_bytes_unref);
|
||||||
|
g_clear_pointer (&priv->format, g_bytes_unref);
|
||||||
g_object_notify (G_OBJECT (output), "socket");
|
g_object_notify (G_OBJECT (output), "socket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,17 +255,7 @@ output_register_object (PvSourceOutput *output, const gchar *prefix)
|
||||||
skel = pv_object_skeleton_new (name);
|
skel = pv_object_skeleton_new (name);
|
||||||
g_free (name);
|
g_free (name);
|
||||||
|
|
||||||
{
|
pv_object_skeleton_set_source_output1 (skel, priv->iface);
|
||||||
PvSourceOutput1 *iface;
|
|
||||||
|
|
||||||
iface = pv_source_output1_skeleton_new ();
|
|
||||||
g_object_set (iface, "source", priv->source, NULL);
|
|
||||||
g_signal_connect (iface, "handle-start", (GCallback) handle_start, output);
|
|
||||||
g_signal_connect (iface, "handle-stop", (GCallback) handle_stop, output);
|
|
||||||
g_signal_connect (iface, "handle-remove", (GCallback) handle_remove, output);
|
|
||||||
pv_object_skeleton_set_source_output1 (skel, iface);
|
|
||||||
g_object_unref (iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (priv->object_path);
|
g_free (priv->object_path);
|
||||||
priv->object_path = pv_daemon_export_uniquely (priv->daemon, G_DBUS_OBJECT_SKELETON (skel));
|
priv->object_path = pv_daemon_export_uniquely (priv->daemon, G_DBUS_OBJECT_SKELETON (skel));
|
||||||
|
|
@ -228,8 +280,10 @@ pv_source_output_finalize (GObject * object)
|
||||||
output_unregister_object (output);
|
output_unregister_object (output);
|
||||||
|
|
||||||
g_object_unref (priv->daemon);
|
g_object_unref (priv->daemon);
|
||||||
|
g_object_unref (priv->iface);
|
||||||
|
g_free (priv->client_path);
|
||||||
g_free (priv->object_path);
|
g_free (priv->object_path);
|
||||||
g_free (priv->source);
|
g_free (priv->source_path);
|
||||||
|
|
||||||
G_OBJECT_CLASS (pv_source_output_parent_class)->finalize (object);
|
G_OBJECT_CLASS (pv_source_output_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
@ -278,15 +332,52 @@ pv_source_output_class_init (PvSourceOutputClass * klass)
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class,
|
g_object_class_install_property (gobject_class,
|
||||||
PROP_SOURCE,
|
PROP_CLIENT_PATH,
|
||||||
g_param_spec_string ("source",
|
g_param_spec_string ("client-path",
|
||||||
"Source",
|
"Client Path",
|
||||||
|
"The client object path",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_SOURCE_PATH,
|
||||||
|
g_param_spec_string ("source-path",
|
||||||
|
"Source Path",
|
||||||
"The source object path",
|
"The source object path",
|
||||||
NULL,
|
NULL,
|
||||||
G_PARAM_READWRITE |
|
G_PARAM_READWRITE |
|
||||||
G_PARAM_CONSTRUCT_ONLY |
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_POSSIBLE_FORMATS,
|
||||||
|
g_param_spec_boxed ("possible-formats",
|
||||||
|
"Possible Formats",
|
||||||
|
"The possbile formats of the stream",
|
||||||
|
G_TYPE_BYTES,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_REQUESTED_FORMAT,
|
||||||
|
g_param_spec_boxed ("requested-format",
|
||||||
|
"Requested Format",
|
||||||
|
"The requested format of the stream",
|
||||||
|
G_TYPE_BYTES,
|
||||||
|
G_PARAM_READABLE |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_FORMAT,
|
||||||
|
g_param_spec_boxed ("format",
|
||||||
|
"Format",
|
||||||
|
"The format of the stream",
|
||||||
|
G_TYPE_BYTES,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class,
|
g_object_class_install_property (gobject_class,
|
||||||
PROP_SOCKET,
|
PROP_SOCKET,
|
||||||
g_param_spec_object ("socket",
|
g_param_spec_object ("socket",
|
||||||
|
|
@ -300,7 +391,12 @@ pv_source_output_class_init (PvSourceOutputClass * klass)
|
||||||
static void
|
static void
|
||||||
pv_source_output_init (PvSourceOutput * output)
|
pv_source_output_init (PvSourceOutput * output)
|
||||||
{
|
{
|
||||||
output->priv = PV_SOURCE_OUTPUT_GET_PRIVATE (output);
|
PvSourceOutputPrivate *priv = output->priv = PV_SOURCE_OUTPUT_GET_PRIVATE (output);
|
||||||
|
|
||||||
|
priv->iface = pv_source_output1_skeleton_new ();
|
||||||
|
g_signal_connect (priv->iface, "handle-start", (GCallback) handle_start, output);
|
||||||
|
g_signal_connect (priv->iface, "handle-stop", (GCallback) handle_stop, output);
|
||||||
|
g_signal_connect (priv->iface, "handle-remove", (GCallback) handle_remove, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
const gchar *
|
const gchar *
|
||||||
|
|
|
||||||
|
|
@ -128,24 +128,6 @@ pv_source_set_property (GObject *_object,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
handle_get_capabilities (PvSource1 *interface,
|
|
||||||
GDBusMethodInvocation *invocation,
|
|
||||||
GVariant *arg_properties,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
PvSource *source = user_data;
|
|
||||||
GVariant *out_caps;
|
|
||||||
|
|
||||||
out_caps = pv_source_get_capabilities (source, arg_properties);
|
|
||||||
|
|
||||||
g_dbus_method_invocation_return_value (invocation,
|
|
||||||
g_variant_new ("(@aa{sv})", out_caps));
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
source_register_object (PvSource *source)
|
source_register_object (PvSource *source)
|
||||||
{
|
{
|
||||||
|
|
@ -160,9 +142,6 @@ source_register_object (PvSource *source)
|
||||||
"state", priv->state,
|
"state", priv->state,
|
||||||
"properties", priv->properties,
|
"properties", priv->properties,
|
||||||
NULL);
|
NULL);
|
||||||
g_signal_connect (priv->iface, "handle-get-capabilities",
|
|
||||||
(GCallback) handle_get_capabilities,
|
|
||||||
source);
|
|
||||||
pv_object_skeleton_set_source1 (skel, priv->iface);
|
pv_object_skeleton_set_source1 (skel, priv->iface);
|
||||||
|
|
||||||
g_free (priv->object_path);
|
g_free (priv->object_path);
|
||||||
|
|
@ -202,19 +181,26 @@ pv_source_finalize (GObject * object)
|
||||||
|
|
||||||
g_free (priv->object_path);
|
g_free (priv->object_path);
|
||||||
g_free (priv->name);
|
g_free (priv->name);
|
||||||
g_variant_unref (priv->properties);
|
if (priv->properties)
|
||||||
|
g_variant_unref (priv->properties);
|
||||||
|
|
||||||
G_OBJECT_CLASS (pv_source_parent_class)->finalize (object);
|
G_OBJECT_CLASS (pv_source_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PvSourceOutput *
|
static PvSourceOutput *
|
||||||
default_create_source_output (PvSource *source, GVariant *props, const gchar *prefix)
|
default_create_source_output (PvSource *source,
|
||||||
|
const gchar *client_path,
|
||||||
|
GBytes *format_filter,
|
||||||
|
const gchar *prefix)
|
||||||
{
|
{
|
||||||
PvSourcePrivate *priv = source->priv;
|
PvSourcePrivate *priv = source->priv;
|
||||||
|
|
||||||
return g_object_new (PV_TYPE_SOURCE_OUTPUT, "daemon", priv->daemon,
|
return g_object_new (PV_TYPE_SOURCE_OUTPUT, "daemon", priv->daemon,
|
||||||
"object-path", prefix,
|
"object-path", prefix,
|
||||||
"source", priv->object_path, NULL);
|
"client-path", client_path,
|
||||||
|
"source-path", priv->object_path,
|
||||||
|
"possible-formats", format_filter,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
|
@ -281,7 +267,7 @@ pv_source_class_init (PvSourceClass * klass)
|
||||||
g_param_spec_variant ("properties",
|
g_param_spec_variant ("properties",
|
||||||
"Properties",
|
"Properties",
|
||||||
"The properties of the source",
|
"The properties of the source",
|
||||||
G_VARIANT_TYPE_VARIANT,
|
G_VARIANT_TYPE_DICTIONARY,
|
||||||
NULL,
|
NULL,
|
||||||
G_PARAM_READWRITE |
|
G_PARAM_READWRITE |
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
@ -297,18 +283,18 @@ pv_source_init (PvSource * source)
|
||||||
source->priv = PV_SOURCE_GET_PRIVATE (source);
|
source->priv = PV_SOURCE_GET_PRIVATE (source);
|
||||||
}
|
}
|
||||||
|
|
||||||
GVariant *
|
GBytes *
|
||||||
pv_source_get_capabilities (PvSource *source, GVariant *props)
|
pv_source_get_capabilities (PvSource *source, GBytes *filter)
|
||||||
{
|
{
|
||||||
PvSourceClass *klass;
|
PvSourceClass *klass;
|
||||||
GVariant *res;
|
GBytes *res;
|
||||||
|
|
||||||
g_return_val_if_fail (PV_IS_SOURCE (source), NULL);
|
g_return_val_if_fail (PV_IS_SOURCE (source), NULL);
|
||||||
|
|
||||||
klass = PV_SOURCE_GET_CLASS (source);
|
klass = PV_SOURCE_GET_CLASS (source);
|
||||||
|
|
||||||
if (klass->get_capabilities)
|
if (klass->get_capabilities)
|
||||||
res = klass->get_capabilities (source, props);
|
res = klass->get_capabilities (source, filter);
|
||||||
else
|
else
|
||||||
res = NULL;
|
res = NULL;
|
||||||
|
|
||||||
|
|
@ -365,7 +351,10 @@ pv_source_report_error (PvSource *source, GError *error)
|
||||||
}
|
}
|
||||||
|
|
||||||
PvSourceOutput *
|
PvSourceOutput *
|
||||||
pv_source_create_source_output (PvSource *source, GVariant *props, const gchar *prefix)
|
pv_source_create_source_output (PvSource *source,
|
||||||
|
const gchar *client_path,
|
||||||
|
GBytes *format_filter,
|
||||||
|
const gchar *prefix)
|
||||||
{
|
{
|
||||||
PvSourceClass *klass;
|
PvSourceClass *klass;
|
||||||
PvSourceOutput *res;
|
PvSourceOutput *res;
|
||||||
|
|
@ -375,7 +364,7 @@ pv_source_create_source_output (PvSource *source, GVariant *props, const gchar *
|
||||||
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, props, prefix);
|
res = klass->create_source_output (source, client_path, format_filter, prefix);
|
||||||
else
|
else
|
||||||
res = NULL;
|
res = NULL;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,12 +64,16 @@ struct _PvSource {
|
||||||
struct _PvSourceClass {
|
struct _PvSourceClass {
|
||||||
GObjectClass parent_class;
|
GObjectClass parent_class;
|
||||||
|
|
||||||
GVariant * (*get_capabilities) (PvSource *source, GVariant *props);
|
GBytes * (*get_capabilities) (PvSource *source, GBytes *filter);
|
||||||
|
|
||||||
gboolean (*set_state) (PvSource *source, PvSourceState);
|
gboolean (*set_state) (PvSource *source, PvSourceState);
|
||||||
|
|
||||||
PvSourceOutput * (*create_source_output) (PvSource *source, GVariant *props, const gchar *prefix);
|
PvSourceOutput * (*create_source_output) (PvSource *source,
|
||||||
gboolean (*release_source_output) (PvSource *source, PvSourceOutput *output);
|
const gchar *client_path,
|
||||||
|
GBytes *format_filter,
|
||||||
|
const gchar *prefix);
|
||||||
|
gboolean (*release_source_output) (PvSource *source,
|
||||||
|
PvSourceOutput *output);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* normal GObject stuff */
|
/* normal GObject stuff */
|
||||||
|
|
@ -77,13 +81,16 @@ GType pv_source_get_type (void);
|
||||||
|
|
||||||
const gchar * pv_source_get_object_path (PvSource *source);
|
const gchar * pv_source_get_object_path (PvSource *source);
|
||||||
|
|
||||||
GVariant * pv_source_get_capabilities (PvSource *source, GVariant *props);
|
GBytes * pv_source_get_capabilities (PvSource *source, GBytes *filter);
|
||||||
|
|
||||||
gboolean pv_source_set_state (PvSource *source, PvSourceState state);
|
gboolean pv_source_set_state (PvSource *source, PvSourceState state);
|
||||||
void pv_source_update_state (PvSource *source, PvSourceState state);
|
void pv_source_update_state (PvSource *source, PvSourceState state);
|
||||||
void pv_source_report_error (PvSource *source, GError *error);
|
void pv_source_report_error (PvSource *source, GError *error);
|
||||||
|
|
||||||
PvSourceOutput * pv_source_create_source_output (PvSource *source, GVariant *props, const gchar *prefix);
|
PvSourceOutput * pv_source_create_source_output (PvSource *source,
|
||||||
|
const gchar *client_path,
|
||||||
|
GBytes *format_filter,
|
||||||
|
const gchar *prefix);
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
|
@ -61,8 +62,13 @@ on_stream_notify (GObject *gobject,
|
||||||
g_main_loop_quit (loop);
|
g_main_loop_quit (loop);
|
||||||
break;
|
break;
|
||||||
case PV_STREAM_STATE_READY:
|
case PV_STREAM_STATE_READY:
|
||||||
pv_stream_start (s, PV_STREAM_MODE_SOCKET);
|
{
|
||||||
|
GBytes *format;
|
||||||
|
|
||||||
|
format = g_bytes_new_static (CAPS, strlen (CAPS) + 1);
|
||||||
|
pv_stream_start (s, format, PV_STREAM_MODE_SOCKET);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case PV_STREAM_STATE_STREAMING:
|
case PV_STREAM_STATE_STREAMING:
|
||||||
{
|
{
|
||||||
PvBufferInfo info;
|
PvBufferInfo info;
|
||||||
|
|
@ -93,14 +99,14 @@ on_state_notify (GObject *gobject,
|
||||||
case PV_CONTEXT_STATE_READY:
|
case PV_CONTEXT_STATE_READY:
|
||||||
{
|
{
|
||||||
PvStream *stream;
|
PvStream *stream;
|
||||||
GVariantBuilder builder;
|
GBytes *format;
|
||||||
|
|
||||||
stream = pv_stream_new (c, "test", NULL);
|
stream = pv_stream_new (c, "test", NULL);
|
||||||
g_signal_connect (stream, "notify::state", (GCallback) on_stream_notify, stream);
|
g_signal_connect (stream, "notify::state", (GCallback) on_stream_notify, stream);
|
||||||
g_signal_connect (stream, "notify::socket", (GCallback) on_socket_notify, stream);
|
g_signal_connect (stream, "notify::socket", (GCallback) on_socket_notify, stream);
|
||||||
|
|
||||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
format = g_bytes_new_static (CAPS, strlen (CAPS) + 1);
|
||||||
pv_stream_connect_capture (stream, NULL, 0, g_variant_builder_end (&builder));
|
pv_stream_connect_capture (stream, NULL, 0, format);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue