/*
 * Farstream - Farstream Shm UDP Transmitter
 *
 * Copyright 2007-2008 Collabora Ltd.
 *  @author: Olivier Crete <olivier.crete@collabora.co.uk>
 * Copyright 2007-2008 Nokia Corp.
 *
 * fs-shm-transmitter.c - A Farstream shm UDP transmitter
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 */

/**
 * SECTION:fs-shm-transmitter
 * @short_description: A transmitter for shm UDP
 *
 * This transmitter provides shm udp
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "fs-shm-transmitter.h"
#include "fs-shm-stream-transmitter.h"

#include <farstream/fs-conference.h>
#include <farstream/fs-plugin.h>

#include <string.h>

GST_DEBUG_CATEGORY (fs_shm_transmitter_debug);
#define GST_CAT_DEFAULT fs_shm_transmitter_debug

/* Signals */
enum
{
  LAST_SIGNAL
};

/* props */
enum
{
  PROP_0,
  PROP_GST_SINK,
  PROP_GST_SRC,
  PROP_COMPONENTS,
  PROP_DO_TIMESTAMP,
};

struct _FsShmTransmitterPrivate
{
  /* We hold references to this element */
  GstElement *gst_sink;
  GstElement *gst_src;

  /* We don't hold a reference to these elements, they are owned
     by the bins */
  /* They are tables of pointers, one per component */
  GstElement **funnels;
  GstElement **tees;

  gboolean do_timestamp;
};

#define FS_SHM_TRANSMITTER_GET_PRIVATE(o)  \
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_SHM_TRANSMITTER,   \
      FsShmTransmitterPrivate))

static void fs_shm_transmitter_class_init (
    FsShmTransmitterClass *klass);
static void fs_shm_transmitter_init (FsShmTransmitter *self);
static void fs_shm_transmitter_constructed (GObject *object);
static void fs_shm_transmitter_dispose (GObject *object);
static void fs_shm_transmitter_finalize (GObject *object);

static void fs_shm_transmitter_get_property (GObject *object,
                                                guint prop_id,
                                                GValue *value,
                                                GParamSpec *pspec);
static void fs_shm_transmitter_set_property (GObject *object,
                                                guint prop_id,
                                                const GValue *value,
                                                GParamSpec *pspec);

static FsStreamTransmitter *fs_shm_transmitter_new_stream_transmitter (
    FsTransmitter *transmitter, FsParticipant *participant,
    guint n_parameters, GParameter *parameters, GError **error);
static GType fs_shm_transmitter_get_stream_transmitter_type (
    FsTransmitter *transmitter);


static GObjectClass *parent_class = NULL;
//static guint signals[LAST_SIGNAL] = { 0 };

/*
 * Private bin subclass
 */

enum {
  BIN_SIGNAL_READY,
  BIN_SIGNAL_DISCONNECTED,
  BIN_LAST_SIGNAL
};

static guint bin_signals[BIN_LAST_SIGNAL] = { 0 };
static GType shm_bin_type = 0;
gpointer shm_bin_parent_class = NULL;

typedef struct _FsShmBin
{
  GstBin parent;
} FsShmBin;

typedef struct _FsShmBinClass
{
  GstBinClass parent_class;
} FsShmBinClass;

static void fs_shm_bin_init (FsShmBin *self)
{
}

static GstElement *
fs_shm_bin_new (void)
{
  return g_object_new (shm_bin_type, NULL);
}

static void
fs_shm_bin_handle_message (GstBin *bin, GstMessage *message)
{
  GstState old, new, pending;
  GError *gerror;
  gchar *msg;

  switch (GST_MESSAGE_TYPE (message))
  {
    case GST_MESSAGE_STATE_CHANGED:
      gst_message_parse_state_changed (message, &old, &new, &pending);

      if (old == GST_STATE_PAUSED && new == GST_STATE_PLAYING)
        g_signal_emit (bin, bin_signals[BIN_SIGNAL_READY], 0,
            GST_MESSAGE_SRC (message));
      break;
    case GST_MESSAGE_ERROR:
      gst_message_parse_error (message, &gerror, &msg);

      if (g_error_matches (gerror, GST_RESOURCE_ERROR,
              GST_RESOURCE_ERROR_READ))
      {
        g_signal_emit (bin, bin_signals[BIN_SIGNAL_DISCONNECTED], 0,
            GST_MESSAGE_SRC (message));
        gst_message_unref (message);
        return;
      }
      break;
    default:
      break;
  }

  GST_BIN_CLASS (shm_bin_parent_class)->handle_message (bin, message);
}

static void fs_shm_bin_class_init (FsShmBinClass *klass)
{
  GstBinClass *bin_class = GST_BIN_CLASS (klass);

  shm_bin_parent_class = g_type_class_peek_parent (klass);

  bin_signals[BIN_SIGNAL_READY] =
    g_signal_new ("ready", G_TYPE_FROM_CLASS (klass),
        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
        g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);

  bin_signals[BIN_SIGNAL_DISCONNECTED] =
    g_signal_new ("disconnected", G_TYPE_FROM_CLASS (klass),
        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
        g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);

  bin_class->handle_message = GST_DEBUG_FUNCPTR (fs_shm_bin_handle_message);
}

/*
 * Lets register the plugin
 */

static GType type = 0;

GType
fs_shm_transmitter_get_type (void)
{
  g_assert (type);
  return type;
}

static GType
fs_shm_transmitter_register_type (FsPlugin *module)
{
  static const GTypeInfo info = {
    sizeof (FsShmTransmitterClass),
    NULL,
    NULL,
    (GClassInitFunc) fs_shm_transmitter_class_init,
    NULL,
    NULL,
    sizeof (FsShmTransmitter),
    0,
    (GInstanceInitFunc) fs_shm_transmitter_init
  };

  static const GTypeInfo bin_info = {
    sizeof (FsShmBinClass),
    NULL,
    NULL,
    (GClassInitFunc) fs_shm_bin_class_init,
    NULL,
    NULL,
    sizeof (FsShmBin),
    0,
    (GInstanceInitFunc) fs_shm_bin_init
  };


  GST_DEBUG_CATEGORY_INIT (fs_shm_transmitter_debug,
      "fsshmtransmitter", 0,
      "Farstream shm UDP transmitter");

  fs_shm_stream_transmitter_register_type (module);

  type = g_type_register_static (FS_TYPE_TRANSMITTER, "FsShmTransmitter",
      &info, 0);

  shm_bin_type = g_type_register_static (
    GST_TYPE_BIN, "FsShmBin", &bin_info, 0);

  return type;
}

FS_INIT_PLUGIN (shm, transmitter)

static void
fs_shm_transmitter_class_init (FsShmTransmitterClass *klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  FsTransmitterClass *transmitter_class = FS_TRANSMITTER_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  gobject_class->set_property = fs_shm_transmitter_set_property;
  gobject_class->get_property = fs_shm_transmitter_get_property;

  gobject_class->constructed = fs_shm_transmitter_constructed;

  g_object_class_override_property (gobject_class, PROP_GST_SRC, "gst-src");
  g_object_class_override_property (gobject_class, PROP_GST_SINK, "gst-sink");
  g_object_class_override_property (gobject_class, PROP_COMPONENTS,
    "components");
  g_object_class_override_property (gobject_class, PROP_DO_TIMESTAMP,
    "do-timestamp");

  transmitter_class->new_stream_transmitter =
    fs_shm_transmitter_new_stream_transmitter;
  transmitter_class->get_stream_transmitter_type =
    fs_shm_transmitter_get_stream_transmitter_type;

  gobject_class->dispose = fs_shm_transmitter_dispose;
  gobject_class->finalize = fs_shm_transmitter_finalize;

  g_type_class_add_private (klass, sizeof (FsShmTransmitterPrivate));
}

static void
fs_shm_transmitter_init (FsShmTransmitter *self)
{

  /* member init */
  self->priv = FS_SHM_TRANSMITTER_GET_PRIVATE (self);

  self->components = 2;
  self->priv->do_timestamp = TRUE;
}

static void
fs_shm_transmitter_constructed (GObject *object)
{
  FsShmTransmitter *self = FS_SHM_TRANSMITTER_CAST (object);
  FsTransmitter *trans = FS_TRANSMITTER_CAST (self);
  GstPad *pad = NULL, *pad2 = NULL;
  GstPad *ghostpad = NULL;
  gchar *padname;
  GstPadLinkReturn ret;
  int c; /* component_id */


  /* We waste one space in order to have the index be the component_id */
  self->priv->funnels = g_new0 (GstElement *, self->components+1);
  self->priv->tees = g_new0 (GstElement *, self->components+1);

  /* First we need the src elemnet */

  self->priv->gst_src = fs_shm_bin_new ();

  if (!self->priv->gst_src) {
    trans->construction_error = g_error_new (FS_ERROR,
      FS_ERROR_CONSTRUCTION,
      "Could not build the transmitter src bin");
    return;
  }

  gst_object_ref (self->priv->gst_src);


  /* Second, we do the sink element */

  self->priv->gst_sink = fs_shm_bin_new ();

  if (!self->priv->gst_sink) {
    trans->construction_error = g_error_new (FS_ERROR,
      FS_ERROR_CONSTRUCTION,
      "Could not build the transmitter sink bin");
    return;
  }

  g_object_set (G_OBJECT (self->priv->gst_sink),
      "async-handling", TRUE,
      NULL);

  gst_object_ref (self->priv->gst_sink);

  for (c = 1; c <= self->components; c++) {
    GstElement *fakesink = NULL;

    /* Lets create the RTP source funnel */

    self->priv->funnels[c] = gst_element_factory_make ("funnel", NULL);

    if (!self->priv->funnels[c]) {
      trans->construction_error = g_error_new (FS_ERROR,
        FS_ERROR_CONSTRUCTION,
        "Could not make the funnel element");
      return;
    }

    if (!gst_bin_add (GST_BIN (self->priv->gst_src),
        self->priv->funnels[c])) {
      trans->construction_error = g_error_new (FS_ERROR,
        FS_ERROR_CONSTRUCTION,
        "Could not add the funnel element to the transmitter src bin");
    }

    pad = gst_element_get_static_pad (self->priv->funnels[c], "src");
    padname = g_strdup_printf ("src_%u", c);
    ghostpad = gst_ghost_pad_new (padname, pad);
    g_free (padname);
    gst_object_unref (pad);

    gst_pad_set_active (ghostpad, TRUE);
    gst_element_add_pad (self->priv->gst_src, ghostpad);


    /* Lets create the RTP sink tee */

    self->priv->tees[c] = gst_element_factory_make ("tee", NULL);

    if (!self->priv->tees[c]) {
      trans->construction_error = g_error_new (FS_ERROR,
        FS_ERROR_CONSTRUCTION,
        "Could not make the tee element");
      return;
    }

    if (!gst_bin_add (GST_BIN (self->priv->gst_sink),
        self->priv->tees[c])) {
      trans->construction_error = g_error_new (FS_ERROR,
        FS_ERROR_CONSTRUCTION,
        "Could not add the tee element to the transmitter sink bin");
    }

    pad = gst_element_get_static_pad (self->priv->tees[c], "sink");
    padname = g_strdup_printf ("sink_%u", c);
    ghostpad = gst_ghost_pad_new (padname, pad);
    g_free (padname);
    gst_object_unref (pad);

    gst_pad_set_active (ghostpad, TRUE);
    gst_element_add_pad (self->priv->gst_sink, ghostpad);

    fakesink = gst_element_factory_make ("fakesink", NULL);

    if (!fakesink) {
      trans->construction_error = g_error_new (FS_ERROR,
        FS_ERROR_CONSTRUCTION,
        "Could not make the fakesink element");
      return;
    }

    g_object_set (fakesink,
        "async", FALSE,
        "sync" , FALSE,
        NULL);

    if (!gst_bin_add (GST_BIN (self->priv->gst_sink), fakesink))
    {
      gst_object_unref (fakesink);
      trans->construction_error = g_error_new (FS_ERROR,
          FS_ERROR_CONSTRUCTION,
          "Could not add the fakesink element to the transmitter sink bin");
      return;
    }

    pad = gst_element_get_request_pad (self->priv->tees[c], "src_%u");
    pad2 = gst_element_get_static_pad (fakesink, "sink");

    ret = gst_pad_link (pad, pad2);

    gst_object_unref (pad2);
    gst_object_unref (pad);

    if (GST_PAD_LINK_FAILED(ret)) {
      trans->construction_error = g_error_new (FS_ERROR,
          FS_ERROR_CONSTRUCTION,
          "Could not link the tee to the fakesink");
      return;
    }
  }

  GST_CALL_PARENT (G_OBJECT_CLASS, constructed, (object));
}

static void
fs_shm_transmitter_dispose (GObject *object)
{
  FsShmTransmitter *self = FS_SHM_TRANSMITTER (object);

  if (self->priv->gst_src) {
    gst_object_unref (self->priv->gst_src);
    self->priv->gst_src = NULL;
  }

  if (self->priv->gst_sink) {
    gst_object_unref (self->priv->gst_sink);
    self->priv->gst_sink = NULL;
  }

  parent_class->dispose (object);
}

static void
fs_shm_transmitter_finalize (GObject *object)
{
  FsShmTransmitter *self = FS_SHM_TRANSMITTER (object);

  if (self->priv->funnels) {
    g_free (self->priv->funnels);
    self->priv->funnels = NULL;
  }

  if (self->priv->tees) {
    g_free (self->priv->tees);
    self->priv->tees = NULL;
  }

  parent_class->finalize (object);
}

static void
fs_shm_transmitter_get_property (GObject *object,
                             guint prop_id,
                             GValue *value,
                             GParamSpec *pspec)
{
  FsShmTransmitter *self = FS_SHM_TRANSMITTER (object);

  switch (prop_id) {
    case PROP_GST_SINK:
      g_value_set_object (value, self->priv->gst_sink);
      break;
    case PROP_GST_SRC:
      g_value_set_object (value, self->priv->gst_src);
      break;
    case PROP_COMPONENTS:
      g_value_set_uint (value, self->components);
      break;
    case PROP_DO_TIMESTAMP:
      g_value_set_boolean (value, self->priv->do_timestamp);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
fs_shm_transmitter_set_property (GObject *object,
                                    guint prop_id,
                                    const GValue *value,
                                    GParamSpec *pspec)
{
  FsShmTransmitter *self = FS_SHM_TRANSMITTER (object);

  switch (prop_id) {
    case PROP_COMPONENTS:
      self->components = g_value_get_uint (value);
      break;
    case PROP_DO_TIMESTAMP:
      self->priv->do_timestamp = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}


/**
 * fs_shm_transmitter_new_stream_shm_transmitter:
 * @transmitter: a #FsTranmitter
 * @participant: the #FsParticipant for which the #FsStream using this
 * new #FsStreamTransmitter is created
 *
 * This function will create a new #FsStreamTransmitter element for a
 * specific participant for this #FsShmTransmitter
 *
 * Returns: a new #FsStreamTransmitter
 */

static FsStreamTransmitter *
fs_shm_transmitter_new_stream_transmitter (FsTransmitter *transmitter,
  FsParticipant *participant, guint n_parameters, GParameter *parameters,
  GError **error)
{
  FsShmTransmitter *self = FS_SHM_TRANSMITTER (transmitter);

  return FS_STREAM_TRANSMITTER (fs_shm_stream_transmitter_newv (
        self, n_parameters, parameters, error));
}

static GType
fs_shm_transmitter_get_stream_transmitter_type (
    FsTransmitter *transmitter)
{
  return FS_TYPE_SHM_STREAM_TRANSMITTER;
}


struct _ShmSrc {
  guint component;
  gchar *path;
  GstElement *src;
  GstPad *funnelpad;

  got_buffer got_buffer_func;
  connection disconnected_func;
  gpointer cb_data;
  gulong buffer_probe;
};


static GstPadProbeReturn
src_buffer_probe_cb (GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
{
  ShmSrc *shm = user_data;
  GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);

  shm->got_buffer_func (buffer, shm->component, shm->cb_data);

  return TRUE;
}


static void
disconnected_cb (GstBin *bin, GstElement *elem, ShmSrc *shm)
{
  if (elem != shm->src)
    return;

  shm->disconnected_func (shm->component, 0, shm->cb_data);
}


ShmSrc *
fs_shm_transmitter_get_shm_src (FsShmTransmitter *self,
    guint component,
    const gchar *path,
    got_buffer got_buffer_func,
    connection disconnected_func,
    gpointer cb_data,
    GError **error)
{
  ShmSrc *shm = g_slice_new0 (ShmSrc);
  GstElement *elem;
  GstPad *pad;

  shm->component = component;
  shm->got_buffer_func = got_buffer_func;
  shm->disconnected_func = disconnected_func;
  shm->cb_data = cb_data;

  shm->path = g_strdup (path);

  elem = gst_element_factory_make ("shmsrc", NULL);
  if (!elem)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not make shmsrc");
    goto error;
  }

  g_object_set (elem,
      "socket-path", path,
      "do-timestamp", self->priv->do_timestamp,
      "is-live", TRUE,
      NULL);

  if (shm->disconnected_func)
    g_signal_connect (self->priv->gst_src, "disconnected",
        G_CALLBACK (disconnected_cb), shm);

  if (!gst_bin_add (GST_BIN (self->priv->gst_src), elem))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not add recvonly filter to bin");
    gst_object_unref (elem);
    goto error;
  }

  shm->src = elem;

  shm->funnelpad = gst_element_get_request_pad (self->priv->funnels[component],
      "sink_%u");

  if (!shm->funnelpad)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not get funnelpad");
    goto error;
  }

  pad = gst_element_get_static_pad (shm->src, "src");
  if (GST_PAD_LINK_FAILED (gst_pad_link (pad, shm->funnelpad)))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link tee"
        " and valve");
    gst_object_unref (pad);
    goto error;
  }

  gst_object_unref (pad);

  if (got_buffer_func)
    shm->buffer_probe = gst_pad_add_probe (shm->funnelpad,
        GST_PAD_PROBE_TYPE_BUFFER,
        src_buffer_probe_cb, shm, NULL);

  if (!gst_element_sync_state_with_parent (shm->src))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not sync the state of the new shmsrc with its parent");
    goto error;
  }

  return shm;

 error:
  fs_shm_transmitter_check_shm_src (self, shm, NULL);
  return NULL;
}

/*
 * Returns: %TRUE if the path is the same, other %FALSE and freeds the ShmSrc
 */

gboolean
fs_shm_transmitter_check_shm_src (FsShmTransmitter *self, ShmSrc *shm,
    const gchar *path)
{
  if (path && !strcmp (path, shm->path))
    return TRUE;

  if (shm->buffer_probe)
    gst_pad_remove_probe (shm->funnelpad, shm->buffer_probe);
  shm->buffer_probe = 0;

  if (shm->funnelpad) {
    gst_element_release_request_pad (self->priv->funnels[shm->component],
        shm->funnelpad);
    gst_object_unref (shm->funnelpad);
  }
  shm->funnelpad = NULL;

  if (shm->src)
  {
    gst_element_set_locked_state (shm->src, TRUE);
    gst_element_set_state (shm->src, GST_STATE_NULL);
    gst_bin_remove (GST_BIN (self->priv->gst_src), shm->src);
  }
  shm->src = NULL;

  g_free (shm->path);
  g_slice_free (ShmSrc, shm);

  return FALSE;
}



struct _ShmSink {
  guint component;
  gchar *path;
  GstElement *sink;
  GstElement *recvonly_filter;
  GstPad *teepad;

  ready ready_func;
  connection connected_func;
  gpointer cb_data;
};


static void
ready_cb (GstBin *bin, GstElement *elem, ShmSink *shm)
{
  gchar *path = NULL;

  if (elem != shm->sink)
    return;

  g_object_get (elem, "socket-path", &path, NULL);
  shm->ready_func (shm->component, path, shm->cb_data);
  g_free (path);
}


static void
connected_cb (GstBin *bin, gint id, ShmSink *shm)
{
  shm->connected_func (shm->component, id, shm->cb_data);
}

ShmSink *
fs_shm_transmitter_get_shm_sink (FsShmTransmitter *self,
    guint component,
    const gchar *path,
    ready ready_func,
    connection connected_func,
    gpointer cb_data,
    GError **error)
{
  ShmSink *shm = g_slice_new0 (ShmSink);
  GstElement *elem;
  GstPad *pad;

  GST_DEBUG ("Trying to add shm sink for c:%u path %s", component, path);

  shm->component = component;

  shm->path = g_strdup (path);

  shm->ready_func = ready_func;
  shm->connected_func = connected_func;
  shm->cb_data = cb_data;

  /* First add the sink */

  elem = gst_element_factory_make ("shmsink", NULL);
  if (!elem)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not make shmsink");
    goto error;
  }
  g_object_set (elem,
      "socket-path", path,
      "wait-for-connection", FALSE,
      "async", FALSE,
      "sync" , FALSE,
      NULL);

  if (ready_func)
    g_signal_connect (self->priv->gst_sink, "ready", G_CALLBACK (ready_cb),
        shm);

  if (connected_func)
    g_signal_connect (elem, "client-connected", G_CALLBACK (connected_cb), shm);

  if (!gst_bin_add (GST_BIN (self->priv->gst_sink), elem))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not add shmsink to bin");
    gst_object_unref (elem);
    goto error;
  }

  shm->sink = elem;

  /* Second add the recvonly filter */

  elem = gst_element_factory_make ("valve", NULL);
  if (!elem)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not make valve");
    goto error;
  }

  if (!gst_bin_add (GST_BIN (self->priv->gst_sink), elem))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not add recvonly filter to bin");
    gst_object_unref (elem);
    goto error;
  }

  shm->recvonly_filter = elem;

  /* Third connect these */

  if (!gst_element_link (shm->recvonly_filter, shm->sink))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not link recvonly filter and shmsink");
    goto error;
  }

  if (!gst_element_sync_state_with_parent (shm->sink))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not sync the state of the new shmsink with its parent");
    goto error;
  }

  if (!gst_element_sync_state_with_parent (shm->recvonly_filter))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not sync the state of the new recvonly filter  with its parent");
    goto error;
  }

  shm->teepad = gst_element_get_request_pad (self->priv->tees[component],
      "src_%u");

  if (!shm->teepad)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
        "Could not get teepad");
    goto error;
  }

  pad = gst_element_get_static_pad (shm->recvonly_filter, "sink");
  if (GST_PAD_LINK_FAILED (gst_pad_link (shm->teepad, pad)))
  {
    g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link tee"
        " and valve");
    gst_object_unref (pad);
    goto error;
  }
  gst_object_unref (pad);

  return shm;

 error:
  fs_shm_transmitter_check_shm_sink (self, shm, NULL);

  return NULL;
}

gboolean
fs_shm_transmitter_check_shm_sink (FsShmTransmitter *self, ShmSink *shm,
    const gchar *path)
{
  if (path && !strcmp (path, shm->path))
    return TRUE;

  if (path)
    GST_DEBUG ("Replacing shm socket %s with %s", shm->path, path);
  else
    GST_DEBUG ("Freeing shm socket %s", shm->path);

  if (shm->teepad)
  {
    gst_element_release_request_pad (self->priv->tees[shm->component],
        shm->teepad);
    gst_object_unref (shm->teepad);
  }
  shm->teepad = NULL;

  if (shm->sink)
  {
    gst_element_set_locked_state (shm->sink, TRUE);
    gst_element_set_state (shm->sink, GST_STATE_NULL);
    gst_bin_remove (GST_BIN (self->priv->gst_sink), shm->sink);
  }
  shm->sink = NULL;

  if (shm->recvonly_filter)
  {
    gst_element_set_locked_state (shm->recvonly_filter, TRUE);
    gst_element_set_state (shm->recvonly_filter, GST_STATE_NULL);
    gst_bin_remove (GST_BIN (self->priv->gst_sink), shm->recvonly_filter);
  }
  shm->recvonly_filter = NULL;

  g_free (shm->path);
  g_slice_free (ShmSink, shm);

  return FALSE;
}


void
fs_shm_transmitter_sink_set_sending (FsShmTransmitter *self, ShmSink *shm,
    gboolean sending)
{
  GObjectClass *klass = G_OBJECT_GET_CLASS (shm->recvonly_filter);

  if (g_object_class_find_property (klass, "drop"))
    g_object_set (shm->recvonly_filter, "drop", !sending, NULL);

  if (sending)
    gst_element_send_event (shm->sink,
        gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
            gst_structure_new ("GstForceKeyUnit",
              "all-headers", G_TYPE_BOOLEAN, TRUE,
              NULL)));
}
