bluez: Make a2dpsink to act like a bin and split the payloader.
authorLuiz Augusto von Dentz <luiz.dentz@openbossa.org>
Wed, 23 Jan 2008 13:14:02 +0000 (13:14 +0000)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Wed, 9 Jan 2013 14:30:10 +0000 (14:30 +0000)
sys/bluez/gsta2dpsink.c
sys/bluez/gsta2dpsink.h

index b22feaa..f5fcddf 100644 (file)
 #endif
 
 #include <unistd.h>
-#include <sys/un.h>
-#include <sys/socket.h>
-#include <fcntl.h>
 #include <pthread.h>
 
-#include <netinet/in.h>
-
-#include <bluetooth/bluetooth.h>
-
-#include "ipc.h"
-#include "rtp.h"
 #include "gstsbcutil.h"
-
 #include "gsta2dpsink.h"
 
-GST_DEBUG_CATEGORY_STATIC (a2dp_sink_debug);
-#define GST_CAT_DEFAULT a2dp_sink_debug
-
-#define BUFFER_SIZE 2048
-#define TEMPLATE_MAX_BITPOOL_VALUE 64
-
-#define GST_A2DP_SINK_MUTEX_LOCK(s) G_STMT_START {     \
-               g_mutex_lock (s->sink_lock);            \
-       } G_STMT_END
-
-#define GST_A2DP_SINK_MUTEX_UNLOCK(s) G_STMT_START {   \
-               g_mutex_unlock (s->sink_lock);          \
-       } G_STMT_END
-
-
-struct bluetooth_data
-{
-  struct bt_getcapabilities_rsp caps;   /* Bluetooth device capabilities */
-  struct bt_setconfiguration_rsp cfg;   /* Bluetooth device configuration */
-  int samples;                  /* Number of encoded samples */
-  gchar buffer[BUFFER_SIZE];    /* Codec transfer buffer */
-  gsize count;                  /* Codec transfer buffer counter */
-
-  int nsamples;                 /* Cumulative number of codec samples */
-  uint16_t seq_num;             /* Cumulative packet sequence */
-  int frame_count;              /* Current frames in buffer */
-};
+GST_DEBUG_CATEGORY_STATIC (gst_a2dp_sink_debug);
+#define GST_CAT_DEFAULT gst_a2dp_sink_debug
 
-
-#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0)
-#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0)
+#define A2DP_SBC_RTP_PAYLOAD_TYPE 1
+#define TEMPLATE_MAX_BITPOOL_STR "64"
 
 enum
 {
   PROP_0,
-  PROP_DEVICE,
+  PROP_DEVICE
 };
 
-GST_BOILERPLATE (GstA2dpSink, gst_a2dp_sink, GstBaseSink, GST_TYPE_BASE_SINK);
+GST_BOILERPLATE (GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
 
-static const GstElementDetails a2dp_sink_details =
+static const GstElementDetails gst_a2dp_sink_details =
 GST_ELEMENT_DETAILS ("Bluetooth A2DP sink",
     "Sink/Audio",
     "Plays audio to an A2DP device",
     "Marcel Holtmann <marcel@holtmann.org>");
 
-static GstStaticPadTemplate a2dp_sink_factory =
+static GstStaticPadTemplate gst_a2dp_sink_factory =
     GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("audio/x-sbc, "
         "rate = (int) { 16000, 32000, 44100, 48000 }, "
         "channels = (int) [ 1, 2 ], "
         "mode = (string) { mono, dual, stereo, joint }, "
         "blocks = (int) { 4, 8, 12, 16 }, "
-        "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness },"
-        /* FIXME use constant here */
-        "bitpool = (int) [ 2, 64 ]; "
-        "audio/mpeg, "
-        "mpegversion = (int) 1, "
-        "layer = (int) [ 1, 3 ], "
-        "rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, "
-        "channels = (int) [ 1, 2 ]"));
-
-static GIOError gst_a2dp_sink_audioservice_send (GstA2dpSink * self,
-    const bt_audio_msg_header_t * msg);
-static GIOError gst_a2dp_sink_audioservice_expect (GstA2dpSink * self,
-    bt_audio_msg_header_t * outmsg, int expected_type);
-
+        "subbands = (int) { 4, 8 }, "
+        "allocation = (string) { snr, loudness }, "
+        "bitpool = (int) [ 2, " TEMPLATE_MAX_BITPOOL_STR " ]; "));
 
 static void
 gst_a2dp_sink_base_init (gpointer g_class)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
+  gst_element_class_set_details (element_class, &gst_a2dp_sink_details);
   gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&a2dp_sink_factory));
-
-  gst_element_class_set_details (element_class, &a2dp_sink_details);
-}
-
-static gboolean
-gst_a2dp_sink_stop (GstBaseSink * basesink)
-{
-  GstA2dpSink *self = GST_A2DP_SINK (basesink);
-
-  GST_INFO_OBJECT (self, "stop");
-
-  if (self->watch_id != 0) {
-    g_source_remove (self->watch_id);
-    self->watch_id = 0;
-  }
-
-  if (self->stream) {
-    g_io_channel_flush (self->stream, NULL);
-    g_io_channel_close (self->stream);
-    g_io_channel_unref (self->stream);
-    self->stream = NULL;
-  }
-
-  if (self->server) {
-    bt_audio_service_close (g_io_channel_unix_get_fd (self->server));
-    g_io_channel_unref (self->server);
-    self->server = NULL;
-  }
-
-  if (self->data) {
-    g_free (self->data);
-    self->data = NULL;
-  }
-
-  if (self->stream_caps) {
-    gst_caps_unref (self->stream_caps);
-    self->stream_caps = NULL;
-  }
-
-  if (self->dev_caps) {
-    gst_caps_unref (self->dev_caps);
-    self->dev_caps = NULL;
-  }
-
-  return TRUE;
-}
-
-static void
-gst_a2dp_sink_finalize (GObject * object)
-{
-  GstA2dpSink *self = GST_A2DP_SINK (object);
-
-  if (self->data)
-    gst_a2dp_sink_stop (GST_BASE_SINK (self));
-
-  if (self->device)
-    g_free (self->device);
-
-  g_mutex_free (self->sink_lock);
-
-  G_OBJECT_CLASS (parent_class)->finalize (object);
+      gst_static_pad_template_get (&gst_a2dp_sink_factory));
 }
 
 static void
 gst_a2dp_sink_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
 {
-  GstA2dpSink *sink = GST_A2DP_SINK (object);
+  GstA2dpSink *self = GST_A2DP_SINK (object);
 
   switch (prop_id) {
     case PROP_DEVICE:
-      if (sink->device)
-        g_free (sink->device);
-      sink->device = g_value_dup_string (value);
+      if (self->sink != NULL)
+        gst_a2dp_sender_sink_set_device (self->sink,
+            g_value_get_string (value));
+
+      if (self->device != NULL)
+        g_free (self->device);
+      self->device = g_value_dup_string (value);
       break;
 
     default:
@@ -202,11 +99,16 @@ static void
 gst_a2dp_sink_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec)
 {
-  GstA2dpSink *sink = GST_A2DP_SINK (object);
+  GstA2dpSink *self = GST_A2DP_SINK (object);
+  gchar *device;
 
   switch (prop_id) {
     case PROP_DEVICE:
-      g_value_set_string (value, sink->device);
+      if (self->sink != NULL) {
+        device = gst_a2dp_sender_sink_get_device (self->sink);
+        if (device != NULL)
+          g_value_take_string (value, device);
+      }
       break;
 
     default:
@@ -215,773 +117,300 @@ gst_a2dp_sink_get_property (GObject * object, guint prop_id,
   }
 }
 
-static gint
-gst_a2dp_sink_bluetooth_recvmsg_fd (GstA2dpSink * sink)
+static GstStateChangeReturn
+gst_a2dp_sink_change_state (GstElement * element, GstStateChange transition)
 {
-  int err, ret;
+  GstA2dpSink *self = GST_A2DP_SINK (element);
 
-  ret = bt_audio_service_get_data_fd (g_io_channel_unix_get_fd (sink->server));
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      gst_element_set_state (GST_ELEMENT (self->sink), GST_STATE_READY);
+      break;
 
-  if (ret < 0) {
-    err = errno;
-    GST_ERROR_OBJECT (sink, "Unable to receive fd: %s (%d)",
-        strerror (err), err);
-    return -err;
-  }
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      if (self->newseg_event != NULL) {
+        gst_event_unref (self->newseg_event);
+        self->newseg_event = NULL;
+      }
+      break;
 
-  sink->stream = g_io_channel_unix_new (ret);
-  GST_DEBUG_OBJECT (sink, "stream_fd=%d", ret);
+    default:
+      break;
+  }
 
-  return 0;
+  return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
 }
 
-static gboolean
-gst_a2dp_sink_init_pkt_conf (GstA2dpSink * sink,
-    GstCaps * caps, sbc_capabilities_t * pkt)
+static void
+gst_a2dp_sink_class_init (GstA2dpSinkClass * klass)
 {
-  sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities;
-  const GValue *value = NULL;
-  const char *pref, *name;
-  gint rate, blocks, subbands;
-  GstStructure *structure = gst_caps_get_structure (caps, 0);
-
-  name = gst_structure_get_name (structure);
-  /* FIXME only sbc supported here, should suport mp3 */
-  if (!(IS_SBC (name))) {
-    GST_ERROR_OBJECT (sink, "Unsupported format %s", name);
-    return FALSE;
-  }
-
-  value = gst_structure_get_value (structure, "rate");
-  rate = g_value_get_int (value);
-  if (rate == 44100)
-    cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100;
-  else if (rate == 48000)
-    cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000;
-  else if (rate == 32000)
-    cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000;
-  else if (rate == 16000)
-    cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000;
-  else {
-    GST_ERROR_OBJECT (sink, "Invalid rate while setting caps");
-    return FALSE;
-  }
-
-  value = gst_structure_get_value (structure, "mode");
-  pref = g_value_get_string (value);
-  if (strcmp (pref, "auto") == 0)
-    cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO;
-  else if (strcmp (pref, "mono") == 0)
-    cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
-  else if (strcmp (pref, "dual") == 0)
-    cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
-  else if (strcmp (pref, "stereo") == 0)
-    cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
-  else if (strcmp (pref, "joint") == 0)
-    cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
-  else {
-    GST_ERROR_OBJECT (sink, "Invalid mode %s", pref);
-    return FALSE;
-  }
-
-  value = gst_structure_get_value (structure, "allocation");
-  pref = g_value_get_string (value);
-  if (strcmp (pref, "auto") == 0)
-    cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO;
-  else if (strcmp (pref, "loudness") == 0)
-    cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
-  else if (strcmp (pref, "snr") == 0)
-    cfg->allocation_method = BT_A2DP_ALLOCATION_SNR;
-  else {
-    GST_ERROR_OBJECT (sink, "Invalid allocation: %s", pref);
-    return FALSE;
-  }
-
-  value = gst_structure_get_value (structure, "subbands");
-  subbands = g_value_get_int (value);
-  if (subbands == 8)
-    cfg->subbands = BT_A2DP_SUBBANDS_8;
-  else if (subbands == 4)
-    cfg->subbands = BT_A2DP_SUBBANDS_4;
-  else {
-    GST_ERROR_OBJECT (sink, "Invalid subbands %d", subbands);
-    return FALSE;
-  }
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
 
-  value = gst_structure_get_value (structure, "blocks");
-  blocks = g_value_get_int (value);
-  if (blocks == 16)
-    cfg->block_length = BT_A2DP_BLOCK_LENGTH_16;
-  else if (blocks == 12)
-    cfg->block_length = BT_A2DP_BLOCK_LENGTH_12;
-  else if (blocks == 8)
-    cfg->block_length = BT_A2DP_BLOCK_LENGTH_8;
-  else if (blocks == 4)
-    cfg->block_length = BT_A2DP_BLOCK_LENGTH_4;
-  else {
-    GST_ERROR_OBJECT (sink, "Invalid blocks %d", blocks);
-    return FALSE;
-  }
+  parent_class = g_type_class_peek_parent (klass);
 
-  /* FIXME min and max ??? */
-  value = gst_structure_get_value (structure, "bitpool");
+  object_class->set_property = GST_DEBUG_FUNCPTR (gst_a2dp_sink_set_property);
+  object_class->get_property = GST_DEBUG_FUNCPTR (gst_a2dp_sink_get_property);
 
-  cfg->max_bitpool = cfg->min_bitpool = g_value_get_int (value);
+  element_class->change_state = GST_DEBUG_FUNCPTR (gst_a2dp_sink_change_state);
 
-  memcpy (pkt, cfg, sizeof (*pkt));
+  g_object_class_install_property (object_class, PROP_DEVICE,
+      g_param_spec_string ("device", "Device",
+          "Bluetooth remote device address", NULL, G_PARAM_READWRITE));
 
-  return TRUE;
+  GST_DEBUG_CATEGORY_INIT (gst_a2dp_sink_debug, "a2dpsink", 0,
+      "A2DP sink element");
 }
 
-static gboolean
-gst_a2dp_sink_conf_recv_stream_fd (GstA2dpSink * self)
+static GstCaps *
+gst_a2dp_sink_get_device_caps (GstA2dpSink * self)
 {
-  struct bluetooth_data *data = self->data;
-  gint ret;
-  GIOError err;
-  GError *gerr = NULL;
-  GIOStatus status;
-  GIOFlags flags;
-  gsize read;
-
-  ret = gst_a2dp_sink_bluetooth_recvmsg_fd (self);
-  if (ret < 0)
-    return FALSE;
-
-  if (!self->stream) {
-    GST_ERROR_OBJECT (self, "Error while configuring device: "
-        "could not acquire audio socket");
-    return FALSE;
-  }
-
-  /* set stream socket to nonblock */
-  GST_LOG_OBJECT (self, "setting stream socket to nonblock");
-  flags = g_io_channel_get_flags (self->stream);
-  flags |= G_IO_FLAG_NONBLOCK;
-  status = g_io_channel_set_flags (self->stream, flags, &gerr);
-  if (status != G_IO_STATUS_NORMAL) {
-    if (gerr)
-      GST_WARNING_OBJECT (self, "Error while "
-          "setting server socket to nonblock: " "%s", gerr->message);
-    else
-      GST_WARNING_OBJECT (self, "Error while "
-          "setting server " "socket to nonblock");
-  }
-
-  /* It is possible there is some outstanding
-     data in the pipe - we have to empty it */
-  GST_LOG_OBJECT (self, "emptying stream pipe");
-  while (1) {
-    err = g_io_channel_read (self->stream, data->buffer,
-        (gsize) data->cfg.link_mtu, &read);
-    if (err != G_IO_ERROR_NONE || read <= 0)
-      break;
-  }
-
-  /* set stream socket to block */
-  GST_LOG_OBJECT (self, "setting stream socket to block");
-  flags = g_io_channel_get_flags (self->stream);
-  flags &= ~G_IO_FLAG_NONBLOCK;
-  status = g_io_channel_set_flags (self->stream, flags, &gerr);
-  if (status != G_IO_STATUS_NORMAL) {
-    if (gerr)
-      GST_WARNING_OBJECT (self, "Error while "
-          "setting server socket to block:" "%s", gerr->message);
-    else
-      GST_WARNING_OBJECT (self, "Error while "
-          "setting server " "socket to block");
-  }
-
-  memset (data->buffer, 0, sizeof (data->buffer));
-
-  return TRUE;
+  return gst_a2dp_sender_sink_get_device_caps (self->sink);
 }
 
-static gboolean
-server_callback (GIOChannel * chan, GIOCondition cond, gpointer data)
+static GstCaps *
+gst_a2dp_sink_get_caps (GstPad * pad)
 {
-  GstA2dpSink *sink;
-
-  if (cond & G_IO_HUP || cond & G_IO_NVAL)
-    return FALSE;
-  else if (cond & G_IO_ERR) {
-    sink = GST_A2DP_SINK (data);
-    GST_WARNING_OBJECT (sink, "Untreated callback G_IO_ERR");
-  }
-
-  return TRUE;
+  GstCaps *caps;
+  GstCaps *caps_aux;
+  GstA2dpSink *self = GST_A2DP_SINK (GST_PAD_PARENT (pad));
+
+  if (self->sink == NULL) {
+    GST_DEBUG_OBJECT (self, "a2dpsink isn't initialized "
+        "returning template caps");
+    caps = gst_static_pad_template_get_caps (&gst_a2dp_sink_factory);
+  } else {
+    GST_LOG_OBJECT (self, "Getting device caps");
+    caps = gst_a2dp_sink_get_device_caps (self);
+    if (caps == NULL)
+      caps = gst_static_pad_template_get_caps (&gst_a2dp_sink_factory);
+  }
+  caps_aux = gst_caps_copy (caps);
+  g_object_set (self->capsfilter, "caps", caps_aux, NULL);
+  gst_caps_unref (caps_aux);
+  return caps;
 }
 
 static gboolean
-gst_a2dp_sink_update_caps (GstA2dpSink * self)
+gst_a2dp_sink_init_sender_sink (GstA2dpSink * self)
 {
-  sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities;
-  GstStructure *structure;
-  GValue *value;
-  GValue *list;
-  gchar *tmp;
-
-  GST_LOG_OBJECT (self, "updating device caps");
-
-  structure = gst_structure_empty_new ("audio/x-sbc");
-  value = g_value_init (g_new0 (GValue, 1), G_TYPE_STRING);
-
-  /* mode */
-  list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST);
-  if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) {
-    g_value_set_static_string (value, "joint");
-    gst_value_list_prepend_value (list, value);
-    g_value_set_static_string (value, "stereo");
-    gst_value_list_prepend_value (list, value);
-    g_value_set_static_string (value, "mono");
-    gst_value_list_prepend_value (list, value);
-    g_value_set_static_string (value, "dual");
-    gst_value_list_prepend_value (list, value);
-  } else {
-    if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
-      g_value_set_static_string (value, "mono");
-      gst_value_list_prepend_value (list, value);
-    }
-    if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) {
-      g_value_set_static_string (value, "stereo");
-      gst_value_list_prepend_value (list, value);
-    }
-    if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) {
-      g_value_set_static_string (value, "dual");
-      gst_value_list_prepend_value (list, value);
-    }
-    if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) {
-      g_value_set_static_string (value, "joint");
-      gst_value_list_prepend_value (list, value);
-    }
-  }
-  g_value_unset (value);
-  if (list) {
-    gst_structure_set_value (structure, "mode", list);
-    g_free (list);
-    list = NULL;
-  }
+  GstElement *sink;
 
-  /* subbands */
-  list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST);
-  value = g_value_init (value, G_TYPE_INT);
-  if (sbc->subbands & BT_A2DP_SUBBANDS_4) {
-    g_value_set_int (value, 4);
-    gst_value_list_prepend_value (list, value);
-  }
-  if (sbc->subbands & BT_A2DP_SUBBANDS_8) {
-    g_value_set_int (value, 8);
-    gst_value_list_prepend_value (list, value);
-  }
-  g_value_unset (value);
-  if (list) {
-    gst_structure_set_value (structure, "subbands", list);
-    g_free (list);
-    list = NULL;
-  }
+  if (self->sink == NULL)
+    sink = gst_element_factory_make ("a2dpsendersink", "sendersink");
+  else
+    sink = GST_ELEMENT (self->sink);
 
-  /* blocks */
-  value = g_value_init (value, G_TYPE_INT);
-  list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST);
-  if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) {
-    g_value_set_int (value, 16);
-    gst_value_list_prepend_value (list, value);
-  }
-  if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) {
-    g_value_set_int (value, 12);
-    gst_value_list_prepend_value (list, value);
-  }
-  if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) {
-    g_value_set_int (value, 8);
-    gst_value_list_prepend_value (list, value);
-  }
-  if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) {
-    g_value_set_int (value, 4);
-    gst_value_list_prepend_value (list, value);
-  }
-  g_value_unset (value);
-  if (list) {
-    gst_structure_set_value (structure, "blocks", list);
-    g_free (list);
-    list = NULL;
+  if (sink == NULL) {
+    GST_ERROR_OBJECT (self, "Couldn't create a2dpsendersink");
+    return FALSE;
   }
 
-  /* allocation */
-  g_value_init (value, G_TYPE_STRING);
-  list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST);
-  if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) {
-    g_value_set_static_string (value, "loudness");
-    gst_value_list_prepend_value (list, value);
-    g_value_set_static_string (value, "snr");
-    gst_value_list_prepend_value (list, value);
-  } else {
-    if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) {
-      g_value_set_static_string (value, "loudness");
-      gst_value_list_prepend_value (list, value);
-    }
-    if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) {
-      g_value_set_static_string (value, "snr");
-      gst_value_list_prepend_value (list, value);
-    }
-  }
-  g_value_unset (value);
-  if (list) {
-    gst_structure_set_value (structure, "allocation", list);
-    g_free (list);
-    list = NULL;
+  if (!gst_bin_add (GST_BIN (self), sink)) {
+    GST_ERROR_OBJECT (self, "failed to add a2dpsendersink " "to the bin");
+    goto cleanup_and_fail;
   }
 
-  /* rate */
-  g_value_init (value, G_TYPE_INT);
-  list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST);
-  if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) {
-    g_value_set_int (value, 48000);
-    gst_value_list_prepend_value (list, value);
-  }
-  if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) {
-    g_value_set_int (value, 44100);
-    gst_value_list_prepend_value (list, value);
-  }
-  if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) {
-    g_value_set_int (value, 32000);
-    gst_value_list_prepend_value (list, value);
+  if (gst_element_set_state (sink, GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) {
+    GST_ERROR_OBJECT (self, "a2dpsendersink failed to go to ready");
+    goto remove_element_and_fail;
   }
-  if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) {
-    g_value_set_int (value, 16000);
-    gst_value_list_prepend_value (list, value);
-  }
-  g_value_unset (value);
-  if (list) {
-    gst_structure_set_value (structure, "rate", list);
-    g_free (list);
-    list = NULL;
+
+  if (!gst_element_link (GST_ELEMENT (self->rtp), sink)) {
+    GST_ERROR_OBJECT (self, "couldn't link rtpsbcpay " "to a2dpsendersink");
+    goto remove_element_and_fail;
   }
 
-  /* bitpool */
-  value = g_value_init (value, GST_TYPE_INT_RANGE);
-  gst_value_set_int_range (value,
-      MIN (sbc->min_bitpool, TEMPLATE_MAX_BITPOOL_VALUE),
-      MIN (sbc->max_bitpool, TEMPLATE_MAX_BITPOOL_VALUE));
-  gst_structure_set_value (structure, "bitpool", value);
+  self->sink = GST_A2DP_SENDER_SINK (sink);
+  g_object_set (G_OBJECT (self->sink), "device", self->device, NULL);
 
-  /* channels */
-  gst_value_set_int_range (value, 1, 2);
-  gst_structure_set_value (structure, "channels", value);
+  gst_element_set_state (sink, GST_STATE_PAUSED);
 
-  g_free (value);
+  return TRUE;
 
-  if (self->dev_caps != NULL)
-    gst_caps_unref (self->dev_caps);
-  self->dev_caps = gst_caps_new_full (structure, NULL);
+remove_element_and_fail:
+  gst_element_set_state (sink, GST_STATE_NULL);
+  gst_bin_remove (GST_BIN (self), sink);
+  return FALSE;
 
-  tmp = gst_caps_to_string (self->dev_caps);
-  GST_DEBUG_OBJECT (self, "Device capabilities: %s", tmp);
-  g_free (tmp);
+cleanup_and_fail:
+  if (sink != NULL)
+    g_object_unref (G_OBJECT (sink));
 
-  return TRUE;
+  return FALSE;
 }
 
 static gboolean
-gst_a2dp_sink_get_capabilities (GstA2dpSink * self)
+gst_a2dp_sink_init_rtp_sbc_element (GstA2dpSink * self)
 {
-  gchar *buf[BT_AUDIO_IPC_PACKET_SIZE];
-  struct bt_getcapabilities_req *req = (void *) buf;
-  bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf;
-  struct bt_getcapabilities_rsp *rsp = (void *) buf;
-  GIOError io_error;
-
-  memset (req, 0, BT_AUDIO_IPC_PACKET_SIZE);
-
-  req->h.msg_type = BT_GETCAPABILITIES_REQ;
-  strncpy (req->device, self->device, 18);
+  GstElement *rtppay;
 
-  io_error = gst_a2dp_sink_audioservice_send (self, &req->h);
-  if (io_error != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error while asking device caps");
-  }
-
-  io_error = gst_a2dp_sink_audioservice_expect (self, &rsp_hdr->msg_h,
-      BT_GETCAPABILITIES_RSP);
-  if (io_error != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error while getting device caps");
+  rtppay = gst_element_factory_make ("rtpsbcpay", "rtp");
+  if (rtppay == NULL) {
+    GST_ERROR_OBJECT (self, "Couldn't create rtpsbcpay");
     return FALSE;
   }
 
-  if (rsp_hdr->posix_errno != 0) {
-    GST_ERROR_OBJECT (self, "BT_GETCAPABILITIES failed : %s(%d)",
-        strerror (rsp_hdr->posix_errno), rsp_hdr->posix_errno);
-    return FALSE;
+  if (!gst_bin_add (GST_BIN (self), rtppay)) {
+    GST_ERROR_OBJECT (self, "failed to add rtp sbc pay to the bin");
+    goto cleanup_and_fail;
   }
 
-  memcpy (&self->data->caps, rsp, sizeof (*rsp));
-  if (!gst_a2dp_sink_update_caps (self)) {
-    GST_WARNING_OBJECT (self, "failed to update capabilities");
-    return FALSE;
+  if (gst_element_set_state (rtppay, GST_STATE_READY) ==
+      GST_STATE_CHANGE_FAILURE) {
+    GST_ERROR_OBJECT (self, "rtpsbcpay failed to go to ready");
+    goto remove_element_and_fail;
   }
 
-  return TRUE;
-}
-
-static gboolean
-gst_a2dp_sink_start (GstBaseSink * basesink)
-{
-  GstA2dpSink *self = GST_A2DP_SINK (basesink);
-  gint sk;
-  gint err;
-
-  GST_INFO_OBJECT (self, "start");
-
-  self->watch_id = 0;
-
-  sk = bt_audio_service_open ();
-  if (sk <= 0) {
-    err = errno;
-    GST_ERROR_OBJECT (self, "Cannot open connection to bt "
-        "audio service: %s %d", strerror (err), err);
-    goto failed;
+  if (!gst_element_link (self->capsfilter, rtppay)) {
+    GST_ERROR_OBJECT (self, "couldn't link capsfilter " "to rtpsbcpay");
+    goto remove_element_and_fail;
   }
 
-  self->server = g_io_channel_unix_new (sk);
-  self->watch_id = g_io_add_watch (self->server, G_IO_HUP | G_IO_ERR |
-      G_IO_NVAL, server_callback, self);
+  self->rtp = GST_BASE_RTP_PAYLOAD (rtppay);
+  g_object_set (G_OBJECT (self->rtp), "min-frames", -1, NULL);
 
-  self->data = g_new0 (struct bluetooth_data, 1);
-  memset (self->data, 0, sizeof (struct bluetooth_data));
+  gst_element_set_state (rtppay, GST_STATE_PAUSED);
 
-  self->stream = NULL;
-  self->stream_caps = NULL;
+  return TRUE;
 
-  if (!gst_a2dp_sink_get_capabilities (self))
-    goto failed;
+remove_element_and_fail:
+  gst_element_set_state (rtppay, GST_STATE_NULL);
+  gst_bin_remove (GST_BIN (self), rtppay);
+  return FALSE;
 
-  return TRUE;
+cleanup_and_fail:
+  if (rtppay != NULL)
+    g_object_unref (G_OBJECT (rtppay));
 
-failed:
-  bt_audio_service_close (sk);
   return FALSE;
 }
 
 static gboolean
-gst_a2dp_sink_stream_start (GstA2dpSink * self)
+gst_a2dp_sink_set_caps (GstPad * pad, GstCaps * caps)
 {
-  gchar buf[BT_AUDIO_IPC_PACKET_SIZE];
-  struct bt_streamstart_req *req = (void *) buf;
-  bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf;
-  struct bt_streamfd_ind *ind = (void *) buf;
-  GIOError io_error;
+  GstA2dpSink *self;
+  GstStructure *structure;
 
-  GST_DEBUG_OBJECT (self, "stream start");
+  self = GST_A2DP_SINK (GST_PAD_PARENT (pad));
+  GST_INFO_OBJECT (self, "setting caps");
 
-  memset (req, 0, sizeof (buf));
-  req->h.msg_type = BT_STREAMSTART_REQ;
+  structure = gst_caps_get_structure (caps, 0);
 
-  io_error = gst_a2dp_sink_audioservice_send (self, &req->h);
-  if (io_error != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error ocurred while sending " "start packet");
+  /* first, we need to create our rtp payloader */
+  if (gst_structure_has_name (structure, "audio/x-sbc"))
+    gst_a2dp_sink_init_rtp_sbc_element (self);
+  else {
+    GST_ERROR_OBJECT (self, "Unexpected media type");
     return FALSE;
   }
 
-  GST_DEBUG_OBJECT (self, "stream start packet sent");
-
-  io_error = gst_a2dp_sink_audioservice_expect (self, &rsp_hdr->msg_h,
-      BT_STREAMSTART_RSP);
-  if (io_error != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error while stream start confirmation");
+  if (!gst_a2dp_sink_init_sender_sink (self))
     return FALSE;
-  }
 
-  if (rsp_hdr->posix_errno != 0) {
-    GST_ERROR_OBJECT (self, "BT_STREAMSTART_RSP failed : %s(%d)",
-        strerror (rsp_hdr->posix_errno), rsp_hdr->posix_errno);
+  if (!gst_a2dp_sender_sink_set_device_caps (self->sink, caps))
     return FALSE;
-  }
 
-  GST_DEBUG_OBJECT (self, "stream started");
+  g_object_set (G_OBJECT (self->rtp), "mtu",
+      gst_a2dp_sender_sink_get_link_mtu (self->sink), NULL);
 
-  io_error = gst_a2dp_sink_audioservice_expect (self, &ind->h, BT_STREAMFD_IND);
-  if (io_error != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error while receiving stream fd");
-    return FALSE;
-  }
-
-  if (!gst_a2dp_sink_conf_recv_stream_fd (self))
-    return FALSE;
+  /* we forward our new segment here if we have one */
+  gst_pad_send_event (GST_BASE_RTP_PAYLOAD_SINKPAD (self->rtp),
+      self->newseg_event);
+  self->newseg_event = NULL;
 
-  return TRUE;
+  return self->ghostpad_setcapsfunc (GST_PAD (self->ghostpad), caps);
 }
 
+/* used for catching newsegment events while we don't have a sink, for
+ * later forwarding it to the sink */
 static gboolean
-gst_a2dp_sink_configure (GstA2dpSink * self, GstCaps * caps)
+gst_a2dp_sink_handle_event (GstPad * pad, GstEvent * event)
 {
-  gchar buf[BT_AUDIO_IPC_PACKET_SIZE];
-  struct bt_setconfiguration_req *req = (void *) buf;
-  bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf;
-  struct bt_setconfiguration_rsp *rsp = (void *) buf;
-  gboolean ret;
-  GIOError io_error;
-
-  GST_DEBUG_OBJECT (self, "configuring device");
-
-  memset (req, 0, sizeof (buf));
-  req->h.msg_type = BT_SETCONFIGURATION_REQ;
-  req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
-  strncpy (req->device, self->device, 18);
-  ret = gst_a2dp_sink_init_pkt_conf (self, caps, &req->sbc_capabilities);
-  if (!ret) {
-    GST_ERROR_OBJECT (self, "Couldn't parse caps " "to packet configuration");
-    return FALSE;
-  }
-
-  io_error = gst_a2dp_sink_audioservice_send (self, &req->h);
-  if (io_error != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error ocurred while sending "
-        "configurarion packet");
-    return FALSE;
-  }
+  GstA2dpSink *self;
 
-  GST_DEBUG_OBJECT (self, "configuration packet sent");
-
-  io_error = gst_a2dp_sink_audioservice_expect (self, &rsp_hdr->msg_h,
-      BT_SETCONFIGURATION_RSP);
-  if (io_error != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error while receiving device confirmation");
-    return FALSE;
-  }
+  self = GST_A2DP_SINK (GST_PAD_PARENT (pad));
 
-  if (rsp_hdr->posix_errno != 0) {
-    GST_ERROR_OBJECT (self, "BT_SETCONFIGURATION_RSP failed : %s(%d)",
-        strerror (rsp_hdr->posix_errno), rsp_hdr->posix_errno);
-    return FALSE;
+  if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT &&
+      gst_element_get_parent (GST_ELEMENT (self->sink)) !=
+      GST_OBJECT_CAST (self)) {
+    if (self->newseg_event != NULL)
+      gst_event_unref (self->newseg_event);
+    self->newseg_event = gst_event_ref (event);
   }
 
-  memcpy (&self->data->cfg, rsp, sizeof (*rsp));
-  GST_DEBUG_OBJECT (self, "configuration set");
-
-  return TRUE;
-}
-
-static GstFlowReturn
-gst_a2dp_sink_preroll (GstBaseSink * basesink, GstBuffer * buffer)
-{
-  GstA2dpSink *sink = GST_A2DP_SINK (basesink);
-  gboolean ret;
-
-  GST_A2DP_SINK_MUTEX_LOCK (sink);
-
-  ret = gst_a2dp_sink_stream_start (sink);
-
-  GST_A2DP_SINK_MUTEX_UNLOCK (sink);
-
-  if (!ret)
-    return GST_FLOW_ERROR;
-
-  return GST_FLOW_OK;
-}
-
-static int
-gst_a2dp_sink_avdtp_write (GstA2dpSink * self)
-{
-  gsize ret;
-  struct bluetooth_data *data = self->data;
-  struct rtp_header *header;
-  struct rtp_payload *payload;
-  GIOError err;
-
-  header = (void *) data->buffer;
-  payload = (void *) (data->buffer + sizeof (*header));
-
-  memset (data->buffer, 0, sizeof (*header) + sizeof (*payload));
-
-  payload->frame_count = data->frame_count;
-  header->v = 2;
-  header->pt = 1;
-  header->sequence_number = htons (data->seq_num);
-  header->timestamp = htonl (data->nsamples);
-  header->ssrc = htonl (1);
-
-  err = g_io_channel_write (self->stream, data->buffer, data->count, &ret);
-  if (err != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error while sending data");
-    ret = -1;
-  }
-
-  /* Reset buffer of data to send */
-  data->count = sizeof (struct rtp_header) + sizeof (struct rtp_payload);
-  data->frame_count = 0;
-  data->samples = 0;
-  data->seq_num++;
-
-  return ret;
-}
-
-static GstFlowReturn
-gst_a2dp_sink_render (GstBaseSink * basesink, GstBuffer * buffer)
-{
-  GstA2dpSink *self = GST_A2DP_SINK (basesink);
-  struct bluetooth_data *data = self->data;
-  gint encoded;
-  gint ret;
-
-  encoded = GST_BUFFER_SIZE (buffer);
-
-  if (data->count + encoded >= data->cfg.link_mtu) {
-    ret = gst_a2dp_sink_avdtp_write (self);
-    if (ret < 0)
-      return GST_FLOW_ERROR;
-  }
-
-  memcpy (data->buffer + data->count, GST_BUFFER_DATA (buffer), encoded);
-  data->count += encoded;
-  data->frame_count++;
-
-  return GST_FLOW_OK;
-}
-
-static GstCaps *
-gst_a2dp_sink_get_caps (GstBaseSink * basesink)
-{
-  GstA2dpSink *self = GST_A2DP_SINK (basesink);
-
-  if (self->dev_caps)
-    return gst_caps_ref (self->dev_caps);
-
-  return
-      gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (self)));
+  return self->ghostpad_eventfunc (GST_PAD (self->ghostpad), event);
 }
 
 static gboolean
-gst_a2dp_sink_set_caps (GstBaseSink * basesink, GstCaps * caps)
+gst_a2dp_sink_init_caps_filter (GstA2dpSink * self)
 {
-  GstA2dpSink *self = GST_A2DP_SINK (basesink);
-  gboolean ret;
-
-  GST_A2DP_SINK_MUTEX_LOCK (self);
-  ret = gst_a2dp_sink_configure (self, caps);
-
-  if (self->stream_caps)
-    gst_caps_unref (self->stream_caps);
-  self->stream_caps = gst_caps_ref (caps);
-
-  GST_A2DP_SINK_MUTEX_UNLOCK (self);
+  GstElement *element;
 
-  return ret;
-}
-
-static gboolean
-gst_a2dp_sink_unlock (GstBaseSink * basesink)
-{
-  GstA2dpSink *self = GST_A2DP_SINK (basesink);
+  element = gst_element_factory_make ("capsfilter", "filter");
+  if (element == NULL)
+    goto failed;
 
-  if (self->stream != NULL)
-    g_io_channel_flush (self->stream, NULL);
+  if (!gst_bin_add (GST_BIN (self), element))
+    goto failed;
 
+  self->capsfilter = element;
   return TRUE;
-}
 
-static void
-gst_a2dp_sink_class_init (GstA2dpSinkClass * klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  object_class->finalize = GST_DEBUG_FUNCPTR (gst_a2dp_sink_finalize);
-  object_class->set_property = GST_DEBUG_FUNCPTR (gst_a2dp_sink_set_property);
-  object_class->get_property = GST_DEBUG_FUNCPTR (gst_a2dp_sink_get_property);
-
-  basesink_class->start = GST_DEBUG_FUNCPTR (gst_a2dp_sink_start);
-  basesink_class->stop = GST_DEBUG_FUNCPTR (gst_a2dp_sink_stop);
-  basesink_class->render = GST_DEBUG_FUNCPTR (gst_a2dp_sink_render);
-  basesink_class->preroll = GST_DEBUG_FUNCPTR (gst_a2dp_sink_preroll);
-  basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_a2dp_sink_set_caps);
-  basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_a2dp_sink_get_caps);
-  basesink_class->unlock = GST_DEBUG_FUNCPTR (gst_a2dp_sink_unlock);
-
-  g_object_class_install_property (object_class, PROP_DEVICE,
-      g_param_spec_string ("device", "Device",
-          "Bluetooth remote device address", NULL, G_PARAM_READWRITE));
-
-  GST_DEBUG_CATEGORY_INIT (a2dp_sink_debug, "a2dpsink", 0, "A2DP sink element");
+failed:
+  GST_ERROR_OBJECT (self, "Failed to initialize caps filter");
+  return FALSE;
 }
 
 static void
 gst_a2dp_sink_init (GstA2dpSink * self, GstA2dpSinkClass * klass)
 {
-  self->device = NULL;
-  self->data = NULL;
-
-  self->stream = NULL;
-
-  self->dev_caps = NULL;
-
-  self->sink_lock = g_mutex_new ();
-}
-
-static GIOError
-gst_a2dp_sink_audioservice_send (GstA2dpSink * self,
-    const bt_audio_msg_header_t * msg)
-{
-  gint err;
-  GIOError error;
-  gsize written;
-
-  GST_DEBUG_OBJECT (self, "sending %s", bt_audio_strmsg (msg->msg_type));
-
-  error = g_io_channel_write (self->server, (const gchar *) msg,
-      BT_AUDIO_IPC_PACKET_SIZE, &written);
-  if (error != G_IO_ERROR_NONE) {
-    err = errno;
-    GST_ERROR_OBJECT (self, "Error sending data to audio service:"
-        " %s(%d)", strerror (err), err);
-  }
-
-  return error;
-}
-
-static GIOError
-gst_a2dp_sink_audioservice_recv (GstA2dpSink * self,
-    bt_audio_msg_header_t * inmsg)
-{
-  GIOError status;
-  gsize bytes_read;
-  const char *type;
-
-  status = g_io_channel_read (self->server, (gchar *) inmsg,
-      BT_AUDIO_IPC_PACKET_SIZE, &bytes_read);
-  if (status != G_IO_ERROR_NONE) {
-    GST_ERROR_OBJECT (self, "Error receiving data from service");
-    return status;
-  }
+  GstPad *capsfilter_pad;
 
-  type = bt_audio_strmsg (inmsg->msg_type);
-  if (!type) {
-    GST_ERROR_OBJECT (self, "Bogus message type %d "
-        "received from audio service", inmsg->msg_type);
-    return G_IO_ERROR_INVAL;
-  }
-
-  GST_DEBUG_OBJECT (self, "Received %s", type);
-
-  return status;
+  self->sink = NULL;
+  self->rtp = NULL;
+  self->device = NULL;
+  self->capsfilter = NULL;
+  self->newseg_event = NULL;
+
+  /* we initialize our capsfilter */
+  gst_a2dp_sink_init_caps_filter (self);
+  g_object_set (self->capsfilter, "caps",
+      gst_static_pad_template_get_caps (&gst_a2dp_sink_factory), NULL);
+
+  /* we search for the capsfilter sinkpad */
+  capsfilter_pad = gst_element_get_static_pad (self->capsfilter, "sink");
+
+  /* now we add a ghostpad */
+  self->ghostpad = GST_GHOST_PAD (gst_ghost_pad_new ("sink", capsfilter_pad));
+  g_object_unref (capsfilter_pad);
+
+  /* the getcaps of our ghostpad must reflect the device caps */
+  gst_pad_set_getcaps_function (GST_PAD (self->ghostpad),
+      gst_a2dp_sink_get_caps);
+  self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC (self->ghostpad);
+  gst_pad_set_setcaps_function (GST_PAD (self->ghostpad),
+      GST_DEBUG_FUNCPTR (gst_a2dp_sink_set_caps));
+
+  /* we need to handle events on our own and we also need the eventfunc
+   * of the ghostpad for forwarding calls */
+  self->ghostpad_eventfunc = GST_PAD_EVENTFUNC (GST_PAD (self->ghostpad));
+  gst_pad_set_event_function (GST_PAD (self->ghostpad),
+      gst_a2dp_sink_handle_event);
+
+  if (!gst_element_add_pad (GST_ELEMENT (self), GST_PAD (self->ghostpad)))
+    GST_ERROR_OBJECT (self, "failed to add ghostpad");
+
+  self->sink =
+      GST_A2DP_SENDER_SINK (gst_element_factory_make ("a2dpsendersink",
+          "sendersink"));
+  if (self->sink == NULL)
+    GST_WARNING_OBJECT (self, "failed to create a2dpsendersink");
 }
 
-static GIOError
-gst_a2dp_sink_audioservice_expect (GstA2dpSink * self,
-    bt_audio_msg_header_t * outmsg, int expected_type)
+gboolean
+gst_a2dp_sink_plugin_init (GstPlugin * plugin)
 {
-  GIOError status;
-
-  status = gst_a2dp_sink_audioservice_recv (self, outmsg);
-  if (status != G_IO_ERROR_NONE)
-    return status;
-
-  if (outmsg->msg_type != expected_type) {
-    GST_ERROR_OBJECT (self, "Bogus message %s "
-        "received while %s was expected",
-        bt_audio_strmsg (outmsg->msg_type), bt_audio_strmsg (expected_type));
-    return G_IO_ERROR_INVAL;
-  }
-
-  return status;
+  return gst_element_register (plugin, "a2dpsink",
+      GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK);
 }
index f5b9b69..4bf9d60 100644 (file)
@@ -22,7 +22,8 @@
  */
 
 #include <gst/gst.h>
-#include <gst/base/gstbasesink.h>
+#include <gst/rtp/gstbasertppayload.h>
+#include "gsta2dpsendersink.h"
 
 G_BEGIN_DECLS
 
@@ -40,31 +41,27 @@ G_BEGIN_DECLS
 typedef struct _GstA2dpSink GstA2dpSink;
 typedef struct _GstA2dpSinkClass GstA2dpSinkClass;
 
-struct bluetooth_data;
-
 struct _GstA2dpSink {
-       GstBaseSink sink;
-
-       gchar *device;
-       GIOChannel *stream;
+       GstBin bin;
 
-       struct bluetooth_data *data;
-       GIOChannel *server;
+       GstBaseRTPPayload *rtp;
+       GstA2dpSenderSink *sink;
+       GstElement *capsfilter;
 
-       /* stream connection data */
-       GstCaps *stream_caps;
-
-       GstCaps *dev_caps;
+       gchar *device;
 
-       GMutex *sink_lock;
+       GstGhostPad *ghostpad;
+       GstPadSetCapsFunction ghostpad_setcapsfunc;
+       GstPadEventFunction ghostpad_eventfunc;
 
-       guint watch_id;
+       GstEvent *newseg_event;
 };
 
 struct _GstA2dpSinkClass {
-       GstBaseSinkClass parent_class;
+       GstBinClass parent_class;
 };
 
 GType gst_a2dp_sink_get_type(void);
+gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin);
 
 G_END_DECLS