mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-02-15 22:05:28 -05:00
Initial commit
This commit is contained in:
commit
3fba92fb74
41 changed files with 5544 additions and 0 deletions
2
src/client/.gitignore
vendored
Normal file
2
src/client/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pv-enumtypes.c
|
||||
pv-enumtypes.h
|
||||
28
src/client/pulsevideo.h
Normal file
28
src/client/pulsevideo.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* Pulsevideo
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PULSEVIDEO_H__
|
||||
#define __PULSEVIDEO_H__
|
||||
|
||||
#include <client/pv-context.h>
|
||||
#include <client/pv-stream.h>
|
||||
#include <client/pv-subscribe.h>
|
||||
|
||||
#endif /* __PULSEVIDEO_H__ */
|
||||
|
||||
502
src/client/pv-context.c
Normal file
502
src/client/pv-context.c
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
/* Pulsevideo
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "server/pv-daemon.h"
|
||||
|
||||
#include "client/pv-context.h"
|
||||
#include "client/pv-enumtypes.h"
|
||||
#include "client/pv-subscribe.h"
|
||||
|
||||
#include "dbus/org-pulsevideo.h"
|
||||
|
||||
struct _PvContextPrivate
|
||||
{
|
||||
gchar *name;
|
||||
GVariant *properties;
|
||||
|
||||
guint id;
|
||||
GDBusConnection *connection;
|
||||
|
||||
PvContextFlags flags;
|
||||
PvContextState state;
|
||||
|
||||
PvDaemon1 *daemon;
|
||||
|
||||
gchar *client_path;
|
||||
PvClient1 *client;
|
||||
|
||||
PvSubscriptionFlags subscription_mask;
|
||||
GDBusObjectManager *client_manager;
|
||||
};
|
||||
|
||||
|
||||
#define PV_CONTEXT_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_CONTEXT, PvContextPrivate))
|
||||
|
||||
G_DEFINE_TYPE (PvContext, pv_context, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_NAME,
|
||||
PROP_PROPERTIES,
|
||||
PROP_STATE,
|
||||
PROP_SUBSCRIPTION_MASK,
|
||||
PROP_CONNECTION,
|
||||
PROP_CLIENT_PATH
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_SUBSCRIPTION_EVENT,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
#include "pv-subscribe.c"
|
||||
|
||||
static void
|
||||
pv_context_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PvContext *context = PV_CONTEXT (_object);
|
||||
PvContextPrivate *priv = context->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, priv->name);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
g_value_set_variant (value, priv->properties);
|
||||
break;
|
||||
|
||||
case PROP_STATE:
|
||||
g_value_set_enum (value, priv->state);
|
||||
break;
|
||||
|
||||
case PROP_SUBSCRIPTION_MASK:
|
||||
g_value_set_flags (value, priv->subscription_mask);
|
||||
break;
|
||||
|
||||
case PROP_CONNECTION:
|
||||
g_value_set_object (value, priv->connection);
|
||||
break;
|
||||
|
||||
case PROP_CLIENT_PATH:
|
||||
g_value_set_string (value, priv->client_path);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (context, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pv_context_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PvContext *context = PV_CONTEXT (_object);
|
||||
PvContextPrivate *priv = context->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_NAME:
|
||||
g_free (priv->name);
|
||||
priv->name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
if (priv->properties)
|
||||
g_variant_unref (priv->properties);
|
||||
priv->properties = g_value_dup_variant (value);
|
||||
break;
|
||||
|
||||
case PROP_SUBSCRIPTION_MASK:
|
||||
priv->subscription_mask = g_value_get_flags (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (context, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pv_context_class_init (PvContextClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PvContextPrivate));
|
||||
|
||||
gobject_class->set_property = pv_context_set_property;
|
||||
gobject_class->get_property = pv_context_get_property;
|
||||
|
||||
/**
|
||||
* PvContext:name
|
||||
*
|
||||
* The application name of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_NAME,
|
||||
g_param_spec_string ("name",
|
||||
"Name",
|
||||
"The application name",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PvContext:properties
|
||||
*
|
||||
* Properties of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_PROPERTIES,
|
||||
g_param_spec_variant ("properties",
|
||||
"Properties",
|
||||
"Extra properties",
|
||||
G_VARIANT_TYPE_VARIANT,
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PvContext:state
|
||||
*
|
||||
* The state of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STATE,
|
||||
g_param_spec_enum ("state",
|
||||
"State",
|
||||
"The context state",
|
||||
PV_TYPE_CONTEXT_STATE,
|
||||
PV_CONTEXT_STATE_UNCONNECTED,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PvContext:subscription-mask
|
||||
*
|
||||
* A mask for what object notifications will be signaled with
|
||||
* PvContext:subscription-event
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SUBSCRIPTION_MASK,
|
||||
g_param_spec_flags ("subscription-mask",
|
||||
"Subscription Mask",
|
||||
"The object to receive subscription events of",
|
||||
PV_TYPE_SUBSCRIPTION_FLAGS,
|
||||
0,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PvContext:connection
|
||||
*
|
||||
* The connection of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CONNECTION,
|
||||
g_param_spec_object ("connection",
|
||||
"Connection",
|
||||
"The DBus connection",
|
||||
G_TYPE_DBUS_CONNECTION,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PvContext:client-path
|
||||
*
|
||||
* The client object path of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CLIENT_PATH,
|
||||
g_param_spec_string ("client-path",
|
||||
"Client Path",
|
||||
"The client object path",
|
||||
NULL,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PvContext:subscription-event
|
||||
* @context: The #PvContext emitting the signal.
|
||||
* @event: A #PvSubscriptionEvent
|
||||
* @flags: #PvSubscriptionFlags indicating the object
|
||||
* @path: the object path
|
||||
*
|
||||
* Notify about a new object that was added/removed/modified.
|
||||
*/
|
||||
signals[SIGNAL_SUBSCRIPTION_EVENT] = g_signal_new ("subscription-event",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE,
|
||||
3,
|
||||
PV_TYPE_SUBSCRIPTION_EVENT,
|
||||
PV_TYPE_SUBSCRIPTION_FLAGS,
|
||||
G_TYPE_STRING);
|
||||
}
|
||||
|
||||
static void
|
||||
pv_context_init (PvContext * context)
|
||||
{
|
||||
PvContextPrivate *priv = context->priv = PV_CONTEXT_GET_PRIVATE (context);
|
||||
|
||||
priv->state = PV_CONTEXT_STATE_UNCONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_context_new:
|
||||
* @name: an application name
|
||||
* @properties: optional properties
|
||||
*
|
||||
* Make a new unconnected #PvContext
|
||||
*
|
||||
* Returns: a new unconnected #PvContext
|
||||
*/
|
||||
PvContext *
|
||||
pv_context_new (const gchar *name, GVariant *properties)
|
||||
{
|
||||
return g_object_new (PV_TYPE_CONTEXT, "name", name, "properties", properties, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
context_set_state (PvContext *context, PvContextState state)
|
||||
{
|
||||
if (context->priv->state != state) {
|
||||
context->priv->state = state;
|
||||
g_object_notify (G_OBJECT (context), "state");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_context_get_state:
|
||||
* @context: a #PvContext
|
||||
*
|
||||
* Get the state of @context.
|
||||
*
|
||||
* Returns: the state of @context
|
||||
*/
|
||||
PvContextState
|
||||
pv_context_get_state (PvContext *context)
|
||||
{
|
||||
PvContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PV_IS_CONTEXT (context), PV_CONTEXT_STATE_ERROR);
|
||||
priv = context->priv;
|
||||
|
||||
return priv->state;
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_proxy (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
PvContextPrivate *priv = context->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
priv->client = pv_client1_proxy_new_finish (res, &error);
|
||||
if (priv->client == NULL) {
|
||||
context_set_state (context, PV_CONTEXT_STATE_ERROR);
|
||||
g_error ("failed to get client proxy: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
context_set_state (context, PV_CONTEXT_STATE_READY);
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_connected (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
PvContextPrivate *priv = context->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!pv_daemon1_call_connect_client_finish (priv->daemon, &priv->client_path, res, &error)) {
|
||||
context_set_state (context, PV_CONTEXT_STATE_ERROR);
|
||||
g_error ("failed to connect client: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
pv_client1_proxy_new (priv->connection,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
PV_DBUS_SERVICE,
|
||||
priv->client_path,
|
||||
NULL,
|
||||
on_client_proxy,
|
||||
context);
|
||||
}
|
||||
|
||||
static void
|
||||
on_daemon_connected (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
PvContextPrivate *priv = context->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
priv->daemon = pv_daemon1_proxy_new_finish (res, &error);
|
||||
if (priv->daemon == NULL) {
|
||||
context_set_state (context, PV_CONTEXT_STATE_ERROR);
|
||||
g_error ("failed to get daemon: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
context_set_state (context, PV_CONTEXT_STATE_REGISTERING);
|
||||
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
||||
g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string ("hello"));
|
||||
|
||||
pv_daemon1_call_connect_client (priv->daemon,
|
||||
g_variant_builder_end (&builder), /* GVariant *arg_properties */
|
||||
NULL, /* GCancellable *cancellable */
|
||||
on_client_connected,
|
||||
context);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_name_appeared (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
const gchar *name_owner,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
PvContextPrivate *priv = context->priv;
|
||||
|
||||
priv->connection = connection;
|
||||
|
||||
install_subscription (context);
|
||||
|
||||
pv_daemon1_proxy_new (priv->connection,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
name,
|
||||
PV_DBUS_OBJECT_SERVER,
|
||||
NULL,
|
||||
on_daemon_connected,
|
||||
user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
on_name_vanished (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
PvContextPrivate *priv = context->priv;
|
||||
|
||||
uninstall_subscription (context);
|
||||
|
||||
priv->connection = connection;
|
||||
|
||||
if (priv->flags & PV_CONTEXT_FLAGS_NOFAIL) {
|
||||
context_set_state (context, PV_CONTEXT_STATE_CONNECTING);
|
||||
} else {
|
||||
context_set_state (context, PV_CONTEXT_STATE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_context_connect:
|
||||
* @context: a #PvContext
|
||||
* @flags: #PvContextFlags
|
||||
*
|
||||
* Connect to the daemon with @flags
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pv_context_connect (PvContext *context, PvContextFlags flags)
|
||||
{
|
||||
PvContextPrivate *priv;
|
||||
GBusNameWatcherFlags nw_flags;
|
||||
|
||||
g_return_val_if_fail (PV_IS_CONTEXT (context), FALSE);
|
||||
|
||||
priv = context->priv;
|
||||
g_return_val_if_fail (priv->connection == NULL, FALSE);
|
||||
|
||||
priv->flags = flags;
|
||||
|
||||
context_set_state (context, PV_CONTEXT_STATE_CONNECTING);
|
||||
|
||||
nw_flags = G_BUS_NAME_WATCHER_FLAGS_NONE;
|
||||
if (!(flags & PV_CONTEXT_FLAGS_NOAUTOSPAWN))
|
||||
nw_flags = G_BUS_NAME_WATCHER_FLAGS_AUTO_START;
|
||||
|
||||
priv->id = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
PV_DBUS_SERVICE,
|
||||
nw_flags,
|
||||
on_name_appeared,
|
||||
on_name_vanished,
|
||||
context,
|
||||
NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_context_get_connection:
|
||||
* @context: a #PvContext
|
||||
*
|
||||
* Get the #GDBusConnection of @context.
|
||||
*
|
||||
* Returns: the #GDBusConnection of @context or %NULL when not connected.
|
||||
*/
|
||||
GDBusConnection *
|
||||
pv_context_get_connection (PvContext *context)
|
||||
{
|
||||
g_return_val_if_fail (PV_IS_CONTEXT (context), NULL);
|
||||
|
||||
return context->priv->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_context_get_client_path:
|
||||
* @context: a #PvContext
|
||||
*
|
||||
* Get the client object path that @context is registered with
|
||||
*
|
||||
* Returns: the client object path of @context or %NULL when not
|
||||
* registered.
|
||||
*/
|
||||
const gchar *
|
||||
pv_context_get_client_path (PvContext *context)
|
||||
{
|
||||
g_return_val_if_fail (PV_IS_CONTEXT (context), NULL);
|
||||
|
||||
return context->priv->client_path;
|
||||
}
|
||||
|
||||
110
src/client/pv-context.h
Normal file
110
src/client/pv-context.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* Pulsevideo
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PV_CONTEXT_H__
|
||||
#define __PV_CONTEXT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PV_TYPE_CONTEXT (pv_context_get_type ())
|
||||
#define PV_IS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_CONTEXT))
|
||||
#define PV_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_CONTEXT))
|
||||
#define PV_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_CONTEXT, PvContextClass))
|
||||
#define PV_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_CONTEXT, PvContext))
|
||||
#define PV_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_CONTEXT, PvContextClass))
|
||||
#define PV_CONTEXT_CAST(obj) ((PvContext*)(obj))
|
||||
#define PV_CONTEXT_CLASS_CAST(klass) ((PvContextClass*)(klass))
|
||||
|
||||
typedef struct _PvContext PvContext;
|
||||
typedef struct _PvContextClass PvContextClass;
|
||||
typedef struct _PvContextPrivate PvContextPrivate;
|
||||
|
||||
/**
|
||||
* PvContextFlags:
|
||||
* @PV_CONTEXT_FLAGS_NONE: no flags
|
||||
* @PV_CONTEXT_FLAGS_NOAUTOSPAWN: disable autostart of the daemon
|
||||
* @PV_CONTEXT_FLAGS_NOFAIL: Don't fail if the daemon is not available,
|
||||
* instead enter PV_CONTEXT_CONNECTING state and wait for the daemon
|
||||
* to appear.
|
||||
*
|
||||
* Context flags passed to pv_context_connect()
|
||||
*/
|
||||
typedef enum {
|
||||
PV_CONTEXT_FLAGS_NONE = 0,
|
||||
PV_CONTEXT_FLAGS_NOAUTOSPAWN = (1 << 0),
|
||||
PV_CONTEXT_FLAGS_NOFAIL = (1 << 1)
|
||||
} PvContextFlags;
|
||||
|
||||
/**
|
||||
* PvContextState:
|
||||
* @PV_CONTEXT_STATE_UNCONNECTED: not connected
|
||||
* @PV_CONTEXT_STATE_CONNECTING: connecting to daemon
|
||||
* @PV_CONTEXT_STATE_REGISTERING: registering with daemon
|
||||
* @PV_CONTEXT_STATE_READY: context is ready
|
||||
* @PV_CONTEXT_STATE_ERROR: context is in error
|
||||
*
|
||||
* The state of a #PvContext
|
||||
*/
|
||||
typedef enum {
|
||||
PV_CONTEXT_STATE_UNCONNECTED = 0,
|
||||
PV_CONTEXT_STATE_CONNECTING = 1,
|
||||
PV_CONTEXT_STATE_REGISTERING = 2,
|
||||
PV_CONTEXT_STATE_READY = 3,
|
||||
PV_CONTEXT_STATE_ERROR = 4
|
||||
} PvContextState;
|
||||
|
||||
/**
|
||||
* PvContext:
|
||||
*
|
||||
* Pulsevideo context object class.
|
||||
*/
|
||||
struct _PvContext {
|
||||
GObject object;
|
||||
|
||||
PvContextPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PvContextClass:
|
||||
*
|
||||
* Pulsevideo context object class.
|
||||
*/
|
||||
struct _PvContextClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pv_context_get_type (void);
|
||||
|
||||
PvContext * pv_context_new (const gchar *name, GVariant *properties);
|
||||
|
||||
gboolean pv_context_connect (PvContext *context, PvContextFlags flags);
|
||||
|
||||
GDBusConnection * pv_context_get_connection (PvContext *context);
|
||||
const gchar * pv_context_get_client_path (PvContext *context);
|
||||
|
||||
PvContextState pv_context_get_state (PvContext *context);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PV_CONTEXT_H__ */
|
||||
|
||||
731
src/client/pv-stream.c
Normal file
731
src/client/pv-stream.c
Normal file
|
|
@ -0,0 +1,731 @@
|
|||
/* Pulsevideo
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gio/gunixfdlist.h>
|
||||
|
||||
#include "server/pv-daemon.h"
|
||||
#include "client/pv-context.h"
|
||||
#include "client/pv-stream.h"
|
||||
#include "client/pv-enumtypes.h"
|
||||
|
||||
#include "dbus/org-pulsevideo.h"
|
||||
|
||||
struct _PvStreamPrivate
|
||||
{
|
||||
PvContext *context;
|
||||
gchar *name;
|
||||
gchar *target;
|
||||
PvStreamState state;
|
||||
|
||||
PvCapture1 *capture;
|
||||
gchar *source_output_path;
|
||||
PvSourceOutput1 *source_output;
|
||||
|
||||
GSocket *socket;
|
||||
PvStreamMode mode;
|
||||
guint socket_id;
|
||||
|
||||
PvBufferInfo info;
|
||||
};
|
||||
|
||||
#define PV_STREAM_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_STREAM, PvStreamPrivate))
|
||||
|
||||
G_DEFINE_TYPE (PvStream, pv_stream, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CONTEXT,
|
||||
PROP_NAME,
|
||||
PROP_STATE,
|
||||
PROP_SOCKET
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_NEW_BUFFER,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
pv_stream_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PvStream *stream = PV_STREAM (_object);
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CONTEXT:
|
||||
g_value_set_object (value, priv->context);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, priv->name);
|
||||
break;
|
||||
|
||||
case PROP_STATE:
|
||||
g_value_set_enum (value, priv->state);
|
||||
break;
|
||||
|
||||
case PROP_SOCKET:
|
||||
g_value_set_object (value, priv->socket);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (stream, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pv_stream_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PvStream *stream = PV_STREAM (_object);
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CONTEXT:
|
||||
priv->context = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
priv->name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (stream, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pv_stream_finalize (GObject * object)
|
||||
{
|
||||
PvStream *stream = PV_STREAM (object);
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
|
||||
g_free (priv->name);
|
||||
|
||||
G_OBJECT_CLASS (pv_stream_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pv_stream_class_init (PvStreamClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PvStreamPrivate));
|
||||
|
||||
gobject_class->finalize = pv_stream_finalize;
|
||||
gobject_class->set_property = pv_stream_set_property;
|
||||
gobject_class->get_property = pv_stream_get_property;
|
||||
|
||||
/**
|
||||
* PvStream:context
|
||||
*
|
||||
* The context of the stream.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CONTEXT,
|
||||
g_param_spec_object ("context",
|
||||
"Context",
|
||||
"The context",
|
||||
PV_TYPE_CONTEXT,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PvStream:name
|
||||
*
|
||||
* The name of the stream as specified at construction time.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_NAME,
|
||||
g_param_spec_string ("name",
|
||||
"Name",
|
||||
"The name of the stream",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PvStream:state
|
||||
*
|
||||
* The state of the stream. Use the notify::state signal to be notified
|
||||
* of state changes.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STATE,
|
||||
g_param_spec_enum ("state",
|
||||
"State",
|
||||
"The stream state",
|
||||
PV_TYPE_STREAM_STATE,
|
||||
PV_STREAM_STATE_UNCONNECTED,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* PvStream:socket
|
||||
*
|
||||
* The socket of the stream. When doing pv_stream_start() with
|
||||
* #PV_STREAM_MODE_SOCKET, the socket will contain a data stream with
|
||||
* meta data and anciliary data containing fds with the data.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SOCKET,
|
||||
g_param_spec_object ("socket",
|
||||
"Socket",
|
||||
"The stream socket",
|
||||
G_TYPE_SOCKET,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
|
||||
/**
|
||||
* PvStream:new-buffer
|
||||
*
|
||||
* When doing pv_stream_start() with #PV_STREAM_MODE_BUFFER, this signal
|
||||
* will be fired whenever a new buffer can be obtained with
|
||||
* pv_stream_capture_buffer().
|
||||
*/
|
||||
signals[SIGNAL_NEW_BUFFER] = g_signal_new ("new-buffer",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE,
|
||||
0,
|
||||
G_TYPE_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
pv_stream_init (PvStream * stream)
|
||||
{
|
||||
PvStreamPrivate *priv = stream->priv = PV_STREAM_GET_PRIVATE (stream);
|
||||
|
||||
priv->state = PV_STREAM_STATE_UNCONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_stream_new:
|
||||
* @context: a #PvContext
|
||||
* @name: a stream name
|
||||
*
|
||||
* Make a new unconnected #PvStream
|
||||
*
|
||||
* Returns: a new unconnected #PvStream
|
||||
*/
|
||||
PvStream *
|
||||
pv_stream_new (PvContext * context, const gchar *name)
|
||||
{
|
||||
g_return_val_if_fail (PV_IS_CONTEXT (context), NULL);
|
||||
g_return_val_if_fail (name != NULL, NULL);
|
||||
|
||||
return g_object_new (PV_TYPE_STREAM, "context", context, "name", name, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
stream_set_state (PvStream *stream, PvStreamState state)
|
||||
{
|
||||
if (stream->priv->state != state) {
|
||||
stream->priv->state = state;
|
||||
g_object_notify (G_OBJECT (stream), "state");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_stream_get_state:
|
||||
* @stream: a #PvStream
|
||||
*
|
||||
* Get the state of @stream.
|
||||
*
|
||||
* Returns: the state of @stream
|
||||
*/
|
||||
PvStreamState
|
||||
pv_stream_get_state (PvStream *stream)
|
||||
{
|
||||
g_return_val_if_fail (PV_IS_STREAM (stream), PV_STREAM_STATE_ERROR);
|
||||
|
||||
return stream->priv->state;
|
||||
}
|
||||
|
||||
static void
|
||||
on_source_output1_proxy (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvStream *stream = user_data;
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
priv->source_output = pv_source_output1_proxy_new_finish (res, &error);
|
||||
if (priv->source_output == NULL) {
|
||||
stream_set_state (stream, PV_STREAM_STATE_ERROR);
|
||||
g_error ("failed to get source output proxy: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
stream_set_state (stream, PV_STREAM_STATE_READY);
|
||||
}
|
||||
|
||||
static void
|
||||
on_source_output_created (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvStream *stream = user_data;
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
PvContext *context = priv->context;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!pv_capture1_call_create_source_output_finish (priv->capture,
|
||||
&priv->source_output_path, res, &error)) {
|
||||
stream_set_state (stream, PV_STREAM_STATE_ERROR);
|
||||
g_print ("failed to get connect capture: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
pv_source_output1_proxy_new (pv_context_get_connection (context),
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
PV_DBUS_SERVICE,
|
||||
priv->source_output_path,
|
||||
NULL,
|
||||
on_source_output1_proxy,
|
||||
stream);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
create_source_output (PvStream *stream)
|
||||
{
|
||||
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"));
|
||||
|
||||
pv_capture1_call_create_source_output (priv->capture,
|
||||
priv->target ? priv->target : "/", /* const gchar *arg_source */
|
||||
g_variant_builder_end (&builder), /* GVariant *arg_props */
|
||||
NULL, /* GCancellable *cancellable */
|
||||
on_source_output_created,
|
||||
stream);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_source_output_removed (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvStream *stream = user_data;
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!pv_capture1_call_remove_source_output_finish (priv->capture,
|
||||
res, &error)) {
|
||||
stream_set_state (stream, PV_STREAM_STATE_ERROR);
|
||||
g_print ("failed to disconnect: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
g_clear_pointer (&priv->source_output_path, g_free);
|
||||
g_clear_object (&priv->source_output);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_source_output (PvStream *stream)
|
||||
{
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
|
||||
pv_capture1_call_remove_source_output (priv->capture,
|
||||
priv->source_output_path,
|
||||
NULL, /* GCancellable *cancellable */
|
||||
on_source_output_removed,
|
||||
stream);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_capture_proxy (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvStream *stream = user_data;
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
priv->capture = pv_capture1_proxy_new_finish (res, &error);
|
||||
if (priv->capture == NULL) {
|
||||
stream_set_state (stream, PV_STREAM_STATE_ERROR);
|
||||
g_error ("failed to get capture proxy: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
create_source_output (stream);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pv_stream_connect_capture:
|
||||
* @stream: a #PvStream
|
||||
* @device: the device name to connect to
|
||||
* @flags: a #PvStreamFlags
|
||||
*
|
||||
* Connect @stream for capturing from @device.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pv_stream_connect_capture (PvStream *stream,
|
||||
const gchar *source,
|
||||
PvStreamFlags flags)
|
||||
{
|
||||
PvStreamPrivate *priv;
|
||||
PvContext *context;
|
||||
|
||||
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
||||
priv = stream->priv;
|
||||
context = priv->context;
|
||||
g_return_val_if_fail (pv_context_get_state (context) == PV_CONTEXT_STATE_READY, FALSE);
|
||||
|
||||
priv->target = g_strdup (source);
|
||||
|
||||
stream_set_state (stream, PV_STREAM_STATE_CONNECTING);
|
||||
|
||||
if (priv->capture == NULL) {
|
||||
pv_capture1_proxy_new (pv_context_get_connection (context),
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
PV_DBUS_SERVICE,
|
||||
pv_context_get_client_path (context),
|
||||
NULL,
|
||||
on_capture_proxy,
|
||||
stream);
|
||||
|
||||
return TRUE;
|
||||
} else {
|
||||
return create_source_output (stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_stream_disconnect:
|
||||
* @stream: a #PvStream
|
||||
*
|
||||
* Disconnect @stream.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*/
|
||||
gboolean
|
||||
pv_stream_disconnect (PvStream *stream)
|
||||
{
|
||||
PvStreamPrivate *priv;
|
||||
PvContext *context;
|
||||
|
||||
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
||||
priv = stream->priv;
|
||||
g_return_val_if_fail (priv->state >= PV_STREAM_STATE_READY, FALSE);
|
||||
g_return_val_if_fail (priv->capture != NULL, FALSE);
|
||||
context = priv->context;
|
||||
g_return_val_if_fail (pv_context_get_state (context) == PV_CONTEXT_STATE_READY, FALSE);
|
||||
|
||||
remove_source_output (stream);
|
||||
|
||||
stream_set_state (stream, PV_STREAM_STATE_UNCONNECTED);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
guint64 offset;
|
||||
guint64 size;
|
||||
} FDMessage;
|
||||
|
||||
static gboolean
|
||||
on_socket_data (GSocket *socket,
|
||||
GIOCondition condition,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvStream *stream = user_data;
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
|
||||
switch (condition) {
|
||||
case G_IO_IN:
|
||||
{
|
||||
gssize len;
|
||||
GInputVector ivec;
|
||||
FDMessage msg;
|
||||
GSocketControlMessage **messages = NULL;
|
||||
gint num_messages = 0;
|
||||
gint flags = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
ivec.buffer = &msg;
|
||||
ivec.size = sizeof (msg);
|
||||
|
||||
len = g_socket_receive_message (socket,
|
||||
NULL,
|
||||
&ivec,
|
||||
1,
|
||||
&messages,
|
||||
&num_messages,
|
||||
&flags,
|
||||
NULL,
|
||||
&error);
|
||||
|
||||
g_assert (len == sizeof (msg));
|
||||
|
||||
if (priv->info.message)
|
||||
g_object_unref (priv->info.message);
|
||||
priv->info.offset = msg.offset;
|
||||
priv->info.size = msg.size;
|
||||
priv->info.message = num_messages > 0 ? messages[0] : NULL;
|
||||
|
||||
g_signal_emit (stream, SIGNAL_NEW_BUFFER, 0, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_socket (PvStream *stream, gint fd)
|
||||
{
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
g_print ("got fd %d\n", fd);
|
||||
priv->socket = g_socket_new_from_fd (fd, &error);
|
||||
if (priv->socket == NULL) {
|
||||
stream_set_state (stream, PV_STREAM_STATE_ERROR);
|
||||
g_error ("failed to create socket: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (priv->mode) {
|
||||
case PV_STREAM_MODE_SOCKET:
|
||||
g_object_notify (G_OBJECT (stream), "socket");
|
||||
break;
|
||||
|
||||
case PV_STREAM_MODE_BUFFER:
|
||||
{
|
||||
GSource *source;
|
||||
|
||||
source = g_socket_create_source (priv->socket, G_IO_IN, NULL);
|
||||
g_source_set_callback (source, (GSourceFunc) on_socket_data, stream, NULL);
|
||||
priv->socket_id = g_source_attach (source, NULL);
|
||||
g_source_unref (source);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unhandle_socket (PvStream *stream)
|
||||
{
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
|
||||
switch (priv->mode) {
|
||||
case PV_STREAM_MODE_SOCKET:
|
||||
g_clear_object (&priv->socket);
|
||||
g_object_notify (G_OBJECT (stream), "socket");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_stream_acquired (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvStream *stream = user_data;
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
GUnixFDList *out_fd_list;
|
||||
gint fd_idx, fd;
|
||||
GVariant *out_props;
|
||||
GError *error = NULL;
|
||||
GVariant *result;
|
||||
|
||||
result = g_dbus_proxy_call_with_unix_fd_list_finish (G_DBUS_PROXY (priv->source_output), &out_fd_list, res, &error);
|
||||
if (result == NULL) {
|
||||
stream_set_state (stream, PV_STREAM_STATE_ERROR);
|
||||
g_error ("failed to acquire: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_get (result,
|
||||
"(h@a{sv})",
|
||||
&fd_idx,
|
||||
&out_props);
|
||||
|
||||
g_variant_unref (result);
|
||||
g_variant_unref (out_props);
|
||||
|
||||
if ((fd = g_unix_fd_list_get (out_fd_list, fd_idx, &error)) < 0) {
|
||||
stream_set_state (stream, PV_STREAM_STATE_ERROR);
|
||||
g_error ("failed to get FD: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
handle_socket (stream, fd);
|
||||
|
||||
stream_set_state (stream, PV_STREAM_STATE_STREAMING);
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_stream_start:
|
||||
* @stream: a #PvStream
|
||||
* @mode: a #PvStreamMode
|
||||
*
|
||||
* Start capturing from @stream.
|
||||
*
|
||||
* When @mode is #PV_STREAM_MODE_SOCKET, you should connect to the notify::socket
|
||||
* signal to obtain a readable socket with metadata and data.
|
||||
*
|
||||
* When @mode is ##PV_STREAM_MODE_BUFFER, you should connect to the new-buffer
|
||||
* signal and use pv_stream_capture_buffer() to get the latest metadata and
|
||||
* data.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pv_stream_start (PvStream *stream, PvStreamMode mode)
|
||||
{
|
||||
PvStreamPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
||||
|
||||
priv = stream->priv;
|
||||
g_return_val_if_fail (priv->state == PV_STREAM_STATE_READY, FALSE);
|
||||
|
||||
priv->mode = mode;
|
||||
|
||||
stream_set_state (stream, PV_STREAM_STATE_STARTING);
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
||||
g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string ("hello"));
|
||||
|
||||
pv_source_output1_call_acquire (priv->source_output,
|
||||
g_variant_builder_end (&builder), /* GVariant *arg_properties */
|
||||
NULL, /* GCancellable *cancellable */
|
||||
on_stream_acquired,
|
||||
stream);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_stream_released (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvStream *stream = user_data;
|
||||
PvStreamPrivate *priv = stream->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!pv_source_output1_call_release_finish (priv->source_output,
|
||||
res, &error)) {
|
||||
stream_set_state (stream, PV_STREAM_STATE_ERROR);
|
||||
g_error ("failed to release: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
unhandle_socket (stream);
|
||||
|
||||
stream_set_state (stream, PV_STREAM_STATE_READY);
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_stream_stop:
|
||||
* @stream: a #PvStream
|
||||
*
|
||||
* Stop capturing from @stream.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pv_stream_stop (PvStream *stream)
|
||||
{
|
||||
PvStreamPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
||||
|
||||
priv = stream->priv;
|
||||
g_return_val_if_fail (priv->state == PV_STREAM_STATE_STREAMING, FALSE);
|
||||
|
||||
pv_source_output1_call_release (priv->source_output,
|
||||
NULL, /* GCancellable *cancellable */
|
||||
on_stream_released,
|
||||
stream);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pv_stream_capture_buffer:
|
||||
* @stream: a #PvStream
|
||||
* @info: a #PvBufferInfo
|
||||
*
|
||||
* Capture the next buffer from @stream. This function should be called every
|
||||
* time after the new-buffer callback has been emitted.
|
||||
*
|
||||
* Returns: %TRUE when @info contains valid information
|
||||
*/
|
||||
gboolean
|
||||
pv_stream_capture_buffer (PvStream *stream, PvBufferInfo *info)
|
||||
{
|
||||
PvStreamPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PV_IS_STREAM (stream), FALSE);
|
||||
g_return_val_if_fail (info != NULL, FALSE);
|
||||
|
||||
priv = stream->priv;
|
||||
g_return_val_if_fail (priv->state == PV_STREAM_STATE_STREAMING, FALSE);
|
||||
|
||||
*info = priv->info;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
113
src/client/pv-stream.h
Normal file
113
src/client/pv-stream.h
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/* Pulsevideo
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PV_STREAM_H__
|
||||
#define __PV_STREAM_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "pv-context.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PV_TYPE_STREAM (pv_stream_get_type ())
|
||||
#define PV_IS_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_STREAM))
|
||||
#define PV_IS_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_STREAM))
|
||||
#define PV_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_STREAM, PvStreamClass))
|
||||
#define PV_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_STREAM, PvStream))
|
||||
#define PV_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_STREAM, PvStreamClass))
|
||||
#define PV_STREAM_CAST(obj) ((PvStream*)(obj))
|
||||
#define PV_STREAM_CLASS_CAST(klass) ((PvStreamClass*)(klass))
|
||||
|
||||
typedef struct _PvStream PvStream;
|
||||
typedef struct _PvStreamClass PvStreamClass;
|
||||
typedef struct _PvStreamPrivate PvStreamPrivate;
|
||||
|
||||
typedef enum {
|
||||
PV_STREAM_STATE_UNCONNECTED = 0,
|
||||
PV_STREAM_STATE_CONNECTING = 1,
|
||||
PV_STREAM_STATE_READY = 2,
|
||||
PV_STREAM_STATE_STARTING = 3,
|
||||
PV_STREAM_STATE_STREAMING = 4,
|
||||
PV_STREAM_STATE_ERROR = 5
|
||||
} PvStreamState;
|
||||
|
||||
|
||||
typedef enum {
|
||||
PV_STREAM_FLAGS_NONE = 0,
|
||||
} PvStreamFlags;
|
||||
|
||||
typedef struct {
|
||||
gint64 timestamp;
|
||||
guint64 seq;
|
||||
guint64 offset;
|
||||
guint64 size;
|
||||
GSocketControlMessage *message;
|
||||
} PvBufferInfo;
|
||||
|
||||
typedef enum {
|
||||
PV_STREAM_MODE_SOCKET = 0,
|
||||
PV_STREAM_MODE_BUFFER = 1,
|
||||
} PvStreamMode;
|
||||
|
||||
/**
|
||||
* PvStream:
|
||||
*
|
||||
* Pulsevideo stream object class.
|
||||
*/
|
||||
struct _PvStream {
|
||||
GObject object;
|
||||
|
||||
PvStreamPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PvStreamClass:
|
||||
*
|
||||
* Pulsevideo stream object class.
|
||||
*/
|
||||
struct _PvStreamClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pv_stream_get_type (void);
|
||||
|
||||
|
||||
PvStream * pv_stream_new (PvContext * context,
|
||||
const gchar *name);
|
||||
|
||||
PvStreamState pv_stream_get_state (PvStream *stream);
|
||||
|
||||
gboolean pv_stream_connect_capture (PvStream *stream,
|
||||
const gchar *source,
|
||||
PvStreamFlags flags);
|
||||
gboolean pv_stream_disconnect (PvStream *stream);
|
||||
|
||||
gboolean pv_stream_start (PvStream *stream, PvStreamMode mode);
|
||||
gboolean pv_stream_stop (PvStream *stream);
|
||||
|
||||
gboolean pv_stream_capture_buffer (PvStream *stream,
|
||||
PvBufferInfo *info);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PV_STREAM_H__ */
|
||||
|
||||
183
src/client/pv-subscribe.c
Normal file
183
src/client/pv-subscribe.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/* Pulsevideo
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
static void
|
||||
notify_subscription (PvContext *context,
|
||||
GDBusObject *object,
|
||||
GDBusInterface *interface,
|
||||
PvSubscriptionEvent event)
|
||||
{
|
||||
PvContextPrivate *priv = context->priv;
|
||||
|
||||
if (priv->subscription_mask & PV_SUBSCRIPTION_FLAGS_CLIENT) {
|
||||
if ((interface == NULL && pv_object_peek_client1 (PV_OBJECT (object))) ||
|
||||
PV_IS_CLIENT1_PROXY (interface))
|
||||
g_signal_emit (context, signals[SIGNAL_SUBSCRIPTION_EVENT], 0, event,
|
||||
PV_SUBSCRIPTION_FLAGS_CLIENT, g_dbus_object_get_object_path (object));
|
||||
}
|
||||
if (priv->subscription_mask & PV_SUBSCRIPTION_FLAGS_DEVICE) {
|
||||
if ((interface == NULL && pv_object_peek_device1 (PV_OBJECT (object))) ||
|
||||
PV_IS_DEVICE1_PROXY (interface))
|
||||
g_signal_emit (context, signals[SIGNAL_SUBSCRIPTION_EVENT], 0, event,
|
||||
PV_SUBSCRIPTION_FLAGS_DEVICE, g_dbus_object_get_object_path (object));
|
||||
}
|
||||
if (priv->subscription_mask & PV_SUBSCRIPTION_FLAGS_SOURCE) {
|
||||
if ((interface == NULL && pv_object_peek_source1 (PV_OBJECT (object))) ||
|
||||
PV_IS_SOURCE1_PROXY (interface))
|
||||
g_signal_emit (context, signals[SIGNAL_SUBSCRIPTION_EVENT], 0, event,
|
||||
PV_SUBSCRIPTION_FLAGS_SOURCE, g_dbus_object_get_object_path (object));
|
||||
}
|
||||
if (priv->subscription_mask & PV_SUBSCRIPTION_FLAGS_SOURCE_OUTPUT) {
|
||||
if ((interface == NULL && pv_object_peek_source_output1 (PV_OBJECT (object))) ||
|
||||
PV_IS_SOURCE_OUTPUT1_PROXY (interface))
|
||||
g_signal_emit (context, signals[SIGNAL_SUBSCRIPTION_EVENT], 0, event,
|
||||
PV_SUBSCRIPTION_FLAGS_SOURCE_OUTPUT, g_dbus_object_get_object_path (object));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_manager_interface_added (GDBusObjectManager *manager,
|
||||
GDBusObject *object,
|
||||
GDBusInterface *interface,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
notify_subscription (context, object, interface, PV_SUBSCRIPTION_EVENT_NEW);
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_manager_interface_removed (GDBusObjectManager *manager,
|
||||
GDBusObject *object,
|
||||
GDBusInterface *interface,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
notify_subscription (context, object, interface, PV_SUBSCRIPTION_EVENT_REMOVE);
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_manager_object_added (GDBusObjectManager *manager,
|
||||
GDBusObject *object,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
notify_subscription (context, object, NULL, PV_SUBSCRIPTION_EVENT_NEW);
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_manager_object_removed (GDBusObjectManager *manager,
|
||||
GDBusObject *object,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
notify_subscription (context, object, NULL, PV_SUBSCRIPTION_EVENT_REMOVE);
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_manager_properties_changed (GDBusObjectManagerClient *manager,
|
||||
GDBusObjectProxy *object_proxy,
|
||||
GDBusProxy *interface_proxy,
|
||||
GVariant *changed_properties,
|
||||
GStrv invalidated_properties,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_print ("properties changed\n");
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_manager_signal (GDBusObjectManagerClient *manager,
|
||||
GDBusObjectProxy *object_proxy,
|
||||
GDBusProxy *interface_proxy,
|
||||
gchar *sender_name,
|
||||
gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_print ("proxy signal %s\n", signal_name);
|
||||
}
|
||||
|
||||
static void
|
||||
connect_client_signals (PvContext *context)
|
||||
{
|
||||
PvContextPrivate *priv = context->priv;
|
||||
|
||||
g_signal_connect (priv->client_manager, "interface-added",
|
||||
(GCallback) on_client_manager_interface_added, context);
|
||||
g_signal_connect (priv->client_manager, "interface-removed",
|
||||
(GCallback) on_client_manager_interface_removed, context);
|
||||
g_signal_connect (priv->client_manager, "object-added",
|
||||
(GCallback) on_client_manager_object_added, context);
|
||||
g_signal_connect (priv->client_manager, "object-removed",
|
||||
(GCallback) on_client_manager_object_removed, context);
|
||||
g_signal_connect (priv->client_manager, "interface-proxy-signal",
|
||||
(GCallback) on_client_manager_signal, context);
|
||||
g_signal_connect (priv->client_manager, "interface-proxy-properties-changed",
|
||||
(GCallback) on_client_manager_properties_changed, context);
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_manager_ready (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PvContext *context = user_data;
|
||||
PvContextPrivate *priv = context->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
priv->client_manager = pv_object_manager_client_new_finish (res, &error);
|
||||
if (priv->client_manager == NULL)
|
||||
goto manager_error;
|
||||
|
||||
connect_client_signals (context);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
manager_error:
|
||||
{
|
||||
g_warning ("could not create client manager: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
install_subscription (PvContext *context)
|
||||
{
|
||||
PvContextPrivate *priv = context->priv;
|
||||
|
||||
if (priv->client_manager)
|
||||
return;
|
||||
|
||||
pv_object_manager_client_new (pv_context_get_connection (context),
|
||||
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
|
||||
PV_DBUS_SERVICE,
|
||||
PV_DBUS_OBJECT_PREFIX,
|
||||
NULL,
|
||||
on_client_manager_ready,
|
||||
context);
|
||||
}
|
||||
|
||||
static void
|
||||
uninstall_subscription (PvContext *context)
|
||||
{
|
||||
PvContextPrivate *priv = context->priv;
|
||||
|
||||
g_clear_object (&priv->client_manager);
|
||||
}
|
||||
45
src/client/pv-subscribe.h
Normal file
45
src/client/pv-subscribe.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* Pulsevideo
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PV_SUBSCRIBE_H__
|
||||
#define __PV_SUBSCRIBE_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
PV_SUBSCRIPTION_FLAGS_CLIENT = (1 << 0),
|
||||
PV_SUBSCRIPTION_FLAGS_DEVICE = (1 << 1),
|
||||
PV_SUBSCRIPTION_FLAGS_SOURCE = (1 << 2),
|
||||
PV_SUBSCRIPTION_FLAGS_SOURCE_OUTPUT = (1 << 3),
|
||||
|
||||
PV_SUBSCRIPTION_FLAGS_ALL = 0xf
|
||||
} PvSubscriptionFlags;
|
||||
|
||||
typedef enum {
|
||||
PV_SUBSCRIPTION_EVENT_NEW = 0,
|
||||
PV_SUBSCRIPTION_EVENT_CHANGE = 1,
|
||||
PV_SUBSCRIPTION_EVENT_REMOVE = 2,
|
||||
} PvSubscriptionEvent;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PV_SUBSCRIBE_H__ */
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue