mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	Add autoconnect property to the ports Remove the upload-node, clients that want to upload media just create a node with an (unconnected) output port. Other clients can then connect to this new output port. Copy formats on newly linked ports.
		
			
				
	
	
		
			855 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			855 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Pinos
 | 
						|
 * 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 <sys/socket.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include <gio/gunixfdlist.h>
 | 
						|
#include <gio/gunixfdmessage.h>
 | 
						|
 | 
						|
#include "pinos/server/daemon.h"
 | 
						|
#include "pinos/client/pinos.h"
 | 
						|
#include "pinos/client/context.h"
 | 
						|
#include "pinos/client/stream.h"
 | 
						|
#include "pinos/client/enumtypes.h"
 | 
						|
 | 
						|
#include "pinos/client/private.h"
 | 
						|
 | 
						|
struct _PinosStreamPrivate
 | 
						|
{
 | 
						|
  PinosContext *context;
 | 
						|
  gchar *name;
 | 
						|
  PinosProperties *properties;
 | 
						|
 | 
						|
  guint id;
 | 
						|
 | 
						|
  PinosStreamState state;
 | 
						|
  GError *error;
 | 
						|
 | 
						|
  PinosDirection direction;
 | 
						|
  gchar *path;
 | 
						|
  GBytes *possible_formats;
 | 
						|
  PinosStreamFlags flags;
 | 
						|
 | 
						|
  GBytes *format;
 | 
						|
 | 
						|
  PinosNode *node;
 | 
						|
  PinosPort *port;
 | 
						|
  gboolean disconnecting;
 | 
						|
 | 
						|
  PinosStreamMode mode;
 | 
						|
};
 | 
						|
 | 
						|
#define PINOS_STREAM_GET_PRIVATE(obj)  \
 | 
						|
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_STREAM, PinosStreamPrivate))
 | 
						|
 | 
						|
G_DEFINE_TYPE (PinosStream, pinos_stream, G_TYPE_OBJECT);
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
  PROP_0,
 | 
						|
  PROP_CONTEXT,
 | 
						|
  PROP_NAME,
 | 
						|
  PROP_PROPERTIES,
 | 
						|
  PROP_STATE,
 | 
						|
  PROP_POSSIBLE_FORMATS,
 | 
						|
  PROP_FORMAT,
 | 
						|
};
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
  SIGNAL_NEW_BUFFER,
 | 
						|
  LAST_SIGNAL
 | 
						|
};
 | 
						|
 | 
						|
static guint signals[LAST_SIGNAL] = { 0 };
 | 
						|
 | 
						|
static void
 | 
						|
pinos_stream_get_property (GObject    *_object,
 | 
						|
                           guint       prop_id,
 | 
						|
                           GValue     *value,
 | 
						|
                           GParamSpec *pspec)
 | 
						|
{
 | 
						|
  PinosStream *stream = PINOS_STREAM (_object);
 | 
						|
  PinosStreamPrivate *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_PROPERTIES:
 | 
						|
      g_value_set_boxed (value, priv->properties);
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_STATE:
 | 
						|
      g_value_set_enum (value, priv->state);
 | 
						|
      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;
 | 
						|
 | 
						|
    default:
 | 
						|
      G_OBJECT_WARN_INVALID_PROPERTY_ID (stream, prop_id, pspec);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pinos_stream_set_property (GObject      *_object,
 | 
						|
                           guint         prop_id,
 | 
						|
                           const GValue *value,
 | 
						|
                           GParamSpec   *pspec)
 | 
						|
{
 | 
						|
  PinosStream *stream = PINOS_STREAM (_object);
 | 
						|
  PinosStreamPrivate *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;
 | 
						|
 | 
						|
    case PROP_PROPERTIES:
 | 
						|
      if (priv->properties)
 | 
						|
        pinos_properties_free (priv->properties);
 | 
						|
      priv->properties = g_value_dup_boxed (value);
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_FORMAT:
 | 
						|
      if (priv->format)
 | 
						|
        g_bytes_unref (priv->format);
 | 
						|
      priv->format = g_value_dup_boxed (value);
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      G_OBJECT_WARN_INVALID_PROPERTY_ID (stream, prop_id, pspec);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
do_notify_state (PinosStream *stream)
 | 
						|
{
 | 
						|
  g_object_notify (G_OBJECT (stream), "state");
 | 
						|
  g_object_unref (stream);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
stream_set_state (PinosStream      *stream,
 | 
						|
                  PinosStreamState  state,
 | 
						|
                  GError           *error)
 | 
						|
{
 | 
						|
  if (stream->priv->state != state) {
 | 
						|
    if (error) {
 | 
						|
      g_clear_error (&stream->priv->error);
 | 
						|
      stream->priv->error = error;
 | 
						|
    }
 | 
						|
    stream->priv->state = state;
 | 
						|
    g_main_context_invoke (stream->priv->context->priv->context,
 | 
						|
                          (GSourceFunc) do_notify_state,
 | 
						|
                          g_object_ref (stream));
 | 
						|
  } else {
 | 
						|
    if (error)
 | 
						|
      g_error_free (error);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
subscription_cb (PinosSubscribe         *subscribe,
 | 
						|
                 PinosSubscriptionEvent  event,
 | 
						|
                 PinosSubscriptionFlags  flags,
 | 
						|
                 GDBusProxy             *object,
 | 
						|
                 gpointer                user_data)
 | 
						|
{
 | 
						|
  PinosStream *stream = PINOS_STREAM (user_data);
 | 
						|
  PinosStreamPrivate *priv = stream->priv;
 | 
						|
 | 
						|
  switch (flags) {
 | 
						|
    case PINOS_SUBSCRIPTION_FLAG_NODE:
 | 
						|
      if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE) {
 | 
						|
        if (object == priv->node && !priv->disconnecting) {
 | 
						|
          stream_set_state (stream,
 | 
						|
                            PINOS_STREAM_STATE_ERROR,
 | 
						|
                            g_error_new_literal (G_IO_ERROR,
 | 
						|
                                                 G_IO_ERROR_CLOSED,
 | 
						|
                                                 "Node disappeared"));
 | 
						|
        }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case PINOS_SUBSCRIPTION_FLAG_PORT:
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pinos_stream_constructed (GObject * object)
 | 
						|
{
 | 
						|
  PinosStream *stream = PINOS_STREAM (object);
 | 
						|
  PinosStreamPrivate *priv = stream->priv;
 | 
						|
 | 
						|
  priv->id = g_signal_connect (priv->context->priv->subscribe,
 | 
						|
                    "subscription-event",
 | 
						|
                    (GCallback) subscription_cb,
 | 
						|
                    stream);
 | 
						|
 | 
						|
  G_OBJECT_CLASS (pinos_stream_parent_class)->constructed (object);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pinos_stream_finalize (GObject * object)
 | 
						|
{
 | 
						|
  PinosStream *stream = PINOS_STREAM (object);
 | 
						|
  PinosStreamPrivate *priv = stream->priv;
 | 
						|
 | 
						|
  g_debug ("free stream %p", stream);
 | 
						|
 | 
						|
  g_clear_object (&priv->node);
 | 
						|
 | 
						|
  if (priv->possible_formats)
 | 
						|
    g_bytes_unref (priv->possible_formats);
 | 
						|
  if (priv->format)
 | 
						|
    g_bytes_unref (priv->format);
 | 
						|
 | 
						|
  g_free (priv->path);
 | 
						|
  if (priv->possible_formats)
 | 
						|
    g_bytes_unref (priv->possible_formats);
 | 
						|
 | 
						|
  g_clear_error (&priv->error);
 | 
						|
 | 
						|
  if (priv->properties)
 | 
						|
    pinos_properties_free (priv->properties);
 | 
						|
  g_signal_handler_disconnect (priv->context->priv->subscribe, priv->id);
 | 
						|
  g_clear_object (&priv->context);
 | 
						|
  g_free (priv->name);
 | 
						|
 | 
						|
  G_OBJECT_CLASS (pinos_stream_parent_class)->finalize (object);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pinos_stream_class_init (PinosStreamClass * klass)
 | 
						|
{
 | 
						|
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 | 
						|
 | 
						|
  g_type_class_add_private (klass, sizeof (PinosStreamPrivate));
 | 
						|
 | 
						|
  gobject_class->constructed = pinos_stream_constructed;
 | 
						|
  gobject_class->finalize = pinos_stream_finalize;
 | 
						|
  gobject_class->set_property = pinos_stream_set_property;
 | 
						|
  gobject_class->get_property = pinos_stream_get_property;
 | 
						|
 | 
						|
  /**
 | 
						|
   * PinosStream:context
 | 
						|
   *
 | 
						|
   * The context of the stream.
 | 
						|
   */
 | 
						|
  g_object_class_install_property (gobject_class,
 | 
						|
                                   PROP_CONTEXT,
 | 
						|
                                   g_param_spec_object ("context",
 | 
						|
                                                        "Context",
 | 
						|
                                                        "The context",
 | 
						|
                                                        PINOS_TYPE_CONTEXT,
 | 
						|
                                                        G_PARAM_READWRITE |
 | 
						|
                                                        G_PARAM_CONSTRUCT_ONLY |
 | 
						|
                                                        G_PARAM_STATIC_STRINGS));
 | 
						|
  /**
 | 
						|
   * PinosStream: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));
 | 
						|
  /**
 | 
						|
   * PinosStream:properties
 | 
						|
   *
 | 
						|
   * The properties of the stream as specified at construction time.
 | 
						|
   */
 | 
						|
  g_object_class_install_property (gobject_class,
 | 
						|
                                   PROP_PROPERTIES,
 | 
						|
                                   g_param_spec_boxed ("properties",
 | 
						|
                                                       "Properties",
 | 
						|
                                                       "The properties of the stream",
 | 
						|
                                                       PINOS_TYPE_PROPERTIES,
 | 
						|
                                                       G_PARAM_READWRITE |
 | 
						|
                                                       G_PARAM_CONSTRUCT_ONLY |
 | 
						|
                                                       G_PARAM_STATIC_STRINGS));
 | 
						|
  /**
 | 
						|
   * PinosStream: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",
 | 
						|
                                                      PINOS_TYPE_STREAM_STATE,
 | 
						|
                                                      PINOS_STREAM_STATE_UNCONNECTED,
 | 
						|
                                                      G_PARAM_READABLE |
 | 
						|
                                                      G_PARAM_STATIC_STRINGS));
 | 
						|
  /**
 | 
						|
   * PinosStream: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));
 | 
						|
  /**
 | 
						|
   * PinosStream: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_READWRITE |
 | 
						|
                                                       G_PARAM_STATIC_STRINGS));
 | 
						|
  /**
 | 
						|
   * PinosStream:new-buffer
 | 
						|
   *
 | 
						|
   * When doing pinos_stream_start() with #PINOS_STREAM_MODE_BUFFER, this signal
 | 
						|
   * will be fired whenever a new buffer can be obtained with
 | 
						|
   * pinos_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
 | 
						|
pinos_stream_init (PinosStream * stream)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv = stream->priv = PINOS_STREAM_GET_PRIVATE (stream);
 | 
						|
 | 
						|
  g_debug ("new stream %p", stream);
 | 
						|
 | 
						|
  priv->state = PINOS_STREAM_STATE_UNCONNECTED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_state_as_string:
 | 
						|
 * @state: a #PinosStreamState
 | 
						|
 *
 | 
						|
 * Return the string representation of @state.
 | 
						|
 *
 | 
						|
 * Returns: the string representation of @state.
 | 
						|
 */
 | 
						|
const gchar *
 | 
						|
pinos_stream_state_as_string (PinosStreamState state)
 | 
						|
{
 | 
						|
  GEnumValue *val;
 | 
						|
 | 
						|
  val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_STREAM_STATE)),
 | 
						|
                          state);
 | 
						|
 | 
						|
  return val == NULL ? "invalid-state" : val->value_nick;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_new:
 | 
						|
 * @context: a #PinosContext
 | 
						|
 * @name: a stream name
 | 
						|
 * @properties: (transfer full): stream properties
 | 
						|
 *
 | 
						|
 * Make a new unconnected #PinosStream
 | 
						|
 *
 | 
						|
 * Returns: a new unconnected #PinosStream
 | 
						|
 */
 | 
						|
PinosStream *
 | 
						|
pinos_stream_new (PinosContext    *context,
 | 
						|
                  const gchar     *name,
 | 
						|
                  PinosProperties *props)
 | 
						|
{
 | 
						|
  PinosStream *stream;
 | 
						|
 | 
						|
  g_return_val_if_fail (PINOS_IS_CONTEXT (context), NULL);
 | 
						|
  g_return_val_if_fail (name != NULL, NULL);
 | 
						|
 | 
						|
  if (props == NULL) {
 | 
						|
    props = pinos_properties_new ("media.name", name, NULL);
 | 
						|
  } else if (!pinos_properties_get (props, "media.name")) {
 | 
						|
    pinos_properties_set (props, "media.name", name);
 | 
						|
  }
 | 
						|
 | 
						|
  stream = g_object_new (PINOS_TYPE_STREAM,
 | 
						|
                       "context", context,
 | 
						|
                       "name", name,
 | 
						|
                       "properties", props,
 | 
						|
                       NULL);
 | 
						|
 | 
						|
  pinos_properties_free (props);
 | 
						|
 | 
						|
  return stream;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_get_state:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 *
 | 
						|
 * Get the state of @stream.
 | 
						|
 *
 | 
						|
 * Returns: the state of @stream
 | 
						|
 */
 | 
						|
PinosStreamState
 | 
						|
pinos_stream_get_state (PinosStream *stream)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PINOS_IS_STREAM (stream), PINOS_STREAM_STATE_ERROR);
 | 
						|
 | 
						|
  return stream->priv->state;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_get_error:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 *
 | 
						|
 * Get the error of @stream.
 | 
						|
 *
 | 
						|
 * Returns: the error of @stream or %NULL when there is no error
 | 
						|
 */
 | 
						|
const GError *
 | 
						|
pinos_stream_get_error (PinosStream *stream)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PINOS_IS_STREAM (stream), NULL);
 | 
						|
 | 
						|
  return stream->priv->error;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_received_buffer (PinosPort *port,
 | 
						|
                    gpointer user_data)
 | 
						|
{
 | 
						|
  PinosStream *stream = user_data;
 | 
						|
 | 
						|
  g_signal_emit (stream, signals[SIGNAL_NEW_BUFFER], 0, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_port_notify (GObject    *object,
 | 
						|
                GParamSpec *pspec,
 | 
						|
                gpointer    user_data)
 | 
						|
{
 | 
						|
  PinosPort *port = PINOS_PORT (object);
 | 
						|
  PinosStream *stream = user_data;
 | 
						|
  PinosStreamPrivate *priv = stream->priv;
 | 
						|
 | 
						|
  if (pspec == NULL || strcmp (g_param_spec_get_name (pspec), "format")) {
 | 
						|
    g_clear_pointer (&priv->format, g_bytes_unref);
 | 
						|
    g_object_get (port, "format", &priv->format, NULL);
 | 
						|
    g_object_notify (G_OBJECT (stream), "format");
 | 
						|
  }
 | 
						|
  if (pspec == NULL || strcmp (g_param_spec_get_name (pspec), "possible-formats")) {
 | 
						|
    g_clear_pointer (&priv->possible_formats, g_bytes_unref);
 | 
						|
    g_object_get (port, "possible-formats", &priv->possible_formats, NULL);
 | 
						|
    g_object_notify (G_OBJECT (stream), "possible-formats");
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_port_created (GObject      *source_object,
 | 
						|
                 GAsyncResult *res,
 | 
						|
                 gpointer      user_data)
 | 
						|
{
 | 
						|
  PinosStream *stream = user_data;
 | 
						|
  PinosStreamPrivate *priv = stream->priv;
 | 
						|
  GError *error = NULL;
 | 
						|
 | 
						|
  g_assert (priv->node ==  PINOS_NODE (source_object));
 | 
						|
 | 
						|
  priv->port = pinos_node_create_port_finish (priv->node,
 | 
						|
                                              res,
 | 
						|
                                              &error);
 | 
						|
  if (priv->port == NULL)
 | 
						|
    goto create_failed;
 | 
						|
 | 
						|
  on_port_notify (G_OBJECT (priv->port), NULL, stream);
 | 
						|
  g_signal_connect (priv->port, "notify", (GCallback) on_port_notify, stream);
 | 
						|
 | 
						|
  pinos_port_set_received_buffer_cb (priv->port, on_received_buffer, stream, NULL);
 | 
						|
 | 
						|
  stream_set_state (stream, PINOS_STREAM_STATE_READY, NULL);
 | 
						|
  g_object_unref (stream);
 | 
						|
 | 
						|
  return;
 | 
						|
 | 
						|
  /* ERRORS */
 | 
						|
create_failed:
 | 
						|
  {
 | 
						|
    g_warning ("failed to create port: %s", error->message);
 | 
						|
    stream_set_state (stream, PINOS_STREAM_STATE_ERROR, error);
 | 
						|
    g_object_unref (stream);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_node_created (GObject      *source_object,
 | 
						|
                 GAsyncResult *res,
 | 
						|
                 gpointer      user_data)
 | 
						|
{
 | 
						|
  PinosStream *stream = user_data;
 | 
						|
  PinosStreamPrivate *priv = stream->priv;
 | 
						|
  PinosContext *context = priv->context;
 | 
						|
  GError *error = NULL;
 | 
						|
 | 
						|
  priv->node = pinos_context_create_node_finish (context, res, &error);
 | 
						|
  if (priv->node == NULL)
 | 
						|
    goto create_failed;
 | 
						|
 | 
						|
  if (priv->properties == NULL)
 | 
						|
    priv->properties = pinos_properties_new (NULL, NULL);
 | 
						|
 | 
						|
  if (priv->flags & PINOS_STREAM_FLAG_AUTOCONNECT)
 | 
						|
    pinos_properties_set (priv->properties, "autoconnect", "1");
 | 
						|
  else
 | 
						|
    pinos_properties_set (priv->properties, "autoconnect", "0");
 | 
						|
 | 
						|
  pinos_node_create_port (priv->node,
 | 
						|
                          priv->direction,
 | 
						|
                          "client-port",
 | 
						|
                          priv->possible_formats,
 | 
						|
                          priv->properties,
 | 
						|
                          NULL, /* GCancellable *cancellable */
 | 
						|
                          on_port_created,
 | 
						|
                          stream);
 | 
						|
  return;
 | 
						|
 | 
						|
  /* ERRORS */
 | 
						|
create_failed:
 | 
						|
  {
 | 
						|
    g_warning ("failed to create  node: %s", error->message);
 | 
						|
    stream_set_state (stream, PINOS_STREAM_STATE_ERROR, error);
 | 
						|
    g_object_unref (stream);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
do_connect (PinosStream *stream)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv = stream->priv;
 | 
						|
  PinosContext *context = priv->context;
 | 
						|
 | 
						|
  pinos_context_create_node (context,
 | 
						|
                             "client-node",
 | 
						|
                             "client-node",
 | 
						|
                             priv->properties,
 | 
						|
                             NULL, /* GCancellable *cancellable */
 | 
						|
                             on_node_created,
 | 
						|
                             stream);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_connect:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 * @direction: the stream direction
 | 
						|
 * @port_path: the port path to connect to or %NULL to get the default port
 | 
						|
 * @flags: a #PinosStreamFlags
 | 
						|
 * @possible_formats: (transfer full): a #GBytes with possible accepted formats
 | 
						|
 *
 | 
						|
 * Connect @stream for input or output on @port_path.
 | 
						|
 *
 | 
						|
 * Returns: %TRUE on success.
 | 
						|
 */
 | 
						|
gboolean
 | 
						|
pinos_stream_connect (PinosStream      *stream,
 | 
						|
                      PinosDirection    direction,
 | 
						|
                      const gchar      *port_path,
 | 
						|
                      PinosStreamFlags  flags,
 | 
						|
                      GBytes           *possible_formats)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv;
 | 
						|
  PinosContext *context;
 | 
						|
 | 
						|
  g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE);
 | 
						|
  g_return_val_if_fail (possible_formats != NULL, FALSE);
 | 
						|
 | 
						|
  priv = stream->priv;
 | 
						|
  context = priv->context;
 | 
						|
  g_return_val_if_fail (pinos_context_get_state (context) == PINOS_CONTEXT_STATE_CONNECTED, FALSE);
 | 
						|
  g_return_val_if_fail (pinos_stream_get_state (stream) == PINOS_STREAM_STATE_UNCONNECTED, FALSE);
 | 
						|
 | 
						|
  priv->direction = direction;
 | 
						|
  g_free (priv->path);
 | 
						|
  priv->path = g_strdup (port_path);
 | 
						|
  if (priv->possible_formats)
 | 
						|
    g_bytes_unref (priv->possible_formats);
 | 
						|
  priv->flags = flags;
 | 
						|
  priv->possible_formats = possible_formats;
 | 
						|
 | 
						|
  stream_set_state (stream, PINOS_STREAM_STATE_CONNECTING, NULL);
 | 
						|
 | 
						|
  g_main_context_invoke (context->priv->context,
 | 
						|
                         (GSourceFunc) do_connect,
 | 
						|
                         g_object_ref (stream));
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
do_start (PinosStream *stream)
 | 
						|
{
 | 
						|
  stream_set_state (stream, PINOS_STREAM_STATE_STREAMING, NULL);
 | 
						|
  g_object_unref (stream);
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_start:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 * @format: (transfer full): a #GBytes with format
 | 
						|
 * @mode: a #PinosStreamMode
 | 
						|
 *
 | 
						|
 * Start capturing from @stream in @format.
 | 
						|
 *
 | 
						|
 * When @mode is #PINOS_STREAM_MODE_SOCKET, you should connect to the notify::socket
 | 
						|
 * signal to obtain a readable socket with metadata and data.
 | 
						|
 *
 | 
						|
 * When @mode is #PINOS_STREAM_MODE_BUFFER, you should connect to the new-buffer
 | 
						|
 * signal and use pinos_stream_capture_buffer() to get the latest metadata and
 | 
						|
 * data.
 | 
						|
 *
 | 
						|
 * Returns: %TRUE on success.
 | 
						|
 */
 | 
						|
gboolean
 | 
						|
pinos_stream_start (PinosStream     *stream,
 | 
						|
                    GBytes          *format,
 | 
						|
                    PinosStreamMode  mode)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv;
 | 
						|
 | 
						|
  g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE);
 | 
						|
 | 
						|
  priv = stream->priv;
 | 
						|
  g_return_val_if_fail (priv->state == PINOS_STREAM_STATE_READY, FALSE);
 | 
						|
 | 
						|
  priv->mode = mode;
 | 
						|
  priv->format = format;
 | 
						|
 | 
						|
  stream_set_state (stream, PINOS_STREAM_STATE_STARTING, NULL);
 | 
						|
 | 
						|
  g_main_context_invoke (priv->context->priv->context,
 | 
						|
                         (GSourceFunc) do_start,
 | 
						|
                         g_object_ref (stream));
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
do_stop (PinosStream *stream)
 | 
						|
{
 | 
						|
  stream_set_state (stream, PINOS_STREAM_STATE_READY, NULL);
 | 
						|
  g_object_unref (stream);
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_stop:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 *
 | 
						|
 * Stop capturing from @stream.
 | 
						|
 *
 | 
						|
 * Returns: %TRUE on success.
 | 
						|
 */
 | 
						|
gboolean
 | 
						|
pinos_stream_stop (PinosStream *stream)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv;
 | 
						|
 | 
						|
  g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE);
 | 
						|
 | 
						|
  priv = stream->priv;
 | 
						|
  g_return_val_if_fail (priv->state == PINOS_STREAM_STATE_STREAMING, FALSE);
 | 
						|
 | 
						|
  g_main_context_invoke (priv->context->priv->context,
 | 
						|
                         (GSourceFunc) do_stop,
 | 
						|
                         g_object_ref (stream));
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
do_disconnect (PinosStream *stream)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv = stream->priv;
 | 
						|
 | 
						|
  pinos_node_remove (priv->node);
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_disconnect:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 *
 | 
						|
 * Disconnect @stream.
 | 
						|
 *
 | 
						|
 * Returns: %TRUE on success
 | 
						|
 */
 | 
						|
gboolean
 | 
						|
pinos_stream_disconnect (PinosStream *stream)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv;
 | 
						|
  PinosContext *context;
 | 
						|
 | 
						|
  g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE);
 | 
						|
  priv = stream->priv;
 | 
						|
  g_return_val_if_fail (priv->state >= PINOS_STREAM_STATE_READY, FALSE);
 | 
						|
  g_return_val_if_fail (priv->node != NULL, FALSE);
 | 
						|
  context = priv->context;
 | 
						|
  g_return_val_if_fail (pinos_context_get_state (context) >= PINOS_CONTEXT_STATE_CONNECTED, FALSE);
 | 
						|
  g_return_val_if_fail (!priv->disconnecting, FALSE);
 | 
						|
 | 
						|
  priv->disconnecting = TRUE;
 | 
						|
 | 
						|
  g_main_context_invoke (context->priv->context,
 | 
						|
                         (GSourceFunc) do_disconnect,
 | 
						|
                         g_object_ref (stream));
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_peek_buffer:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 *
 | 
						|
 * Get the current buffer from @stream. This function should be called from
 | 
						|
 * the new-buffer signal callback.
 | 
						|
 *
 | 
						|
 * Returns: a #PinosBuffer or %NULL when there is no buffer.
 | 
						|
 */
 | 
						|
PinosBuffer *
 | 
						|
pinos_stream_peek_buffer (PinosStream  *stream)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv;
 | 
						|
 | 
						|
  g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE);
 | 
						|
 | 
						|
  priv = stream->priv;
 | 
						|
  //g_return_val_if_fail (priv->state == PINOS_STREAM_STATE_STREAMING, FALSE);
 | 
						|
 | 
						|
  return pinos_port_peek_buffer (priv->port);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_buffer_builder_init:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 * @builder: a #PinosBufferBuilder
 | 
						|
 *
 | 
						|
 * Get a #PinosBufferBuilder for @stream.
 | 
						|
 *
 | 
						|
 * Returns: a #PinosBuffer or %NULL when there is no buffer.
 | 
						|
 */
 | 
						|
void
 | 
						|
pinos_stream_buffer_builder_init (PinosStream  *stream, PinosBufferBuilder *builder)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv;
 | 
						|
 | 
						|
  g_return_if_fail (PINOS_IS_STREAM (stream));
 | 
						|
  priv = stream->priv;
 | 
						|
 | 
						|
  pinos_port_buffer_builder_init (priv->port, builder);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * pinos_stream_send_buffer:
 | 
						|
 * @stream: a #PinosStream
 | 
						|
 * @buffer: a #PinosBuffer
 | 
						|
 *
 | 
						|
 * Send a buffer to @stream.
 | 
						|
 *
 | 
						|
 * For provider streams, this function should be called whenever there is a new frame
 | 
						|
 * available.
 | 
						|
 *
 | 
						|
 * For capture streams, this functions should be called for each fd-payload that
 | 
						|
 * should be released.
 | 
						|
 *
 | 
						|
 * Returns: %TRUE when @buffer was handled
 | 
						|
 */
 | 
						|
gboolean
 | 
						|
pinos_stream_send_buffer (PinosStream *stream,
 | 
						|
                          PinosBuffer *buffer)
 | 
						|
{
 | 
						|
  PinosStreamPrivate *priv;
 | 
						|
  GError *error = NULL;
 | 
						|
 | 
						|
  g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE);
 | 
						|
  g_return_val_if_fail (buffer != NULL, FALSE);
 | 
						|
 | 
						|
  priv = stream->priv;
 | 
						|
  g_return_val_if_fail (priv->state == PINOS_STREAM_STATE_STREAMING, FALSE);
 | 
						|
 | 
						|
  if (!pinos_port_send_buffer (priv->port, buffer, &error))
 | 
						|
    goto send_error;
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
 | 
						|
  /* ERRORS */
 | 
						|
send_error:
 | 
						|
  {
 | 
						|
    g_warning ("failed to send message: %s", error->message);
 | 
						|
    stream_set_state (stream, PINOS_STREAM_STATE_ERROR, error);
 | 
						|
    g_clear_error (&error);
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
}
 |