diff --git a/sys/bluez/gsta2dpsink.h b/sys/bluez/gsta2dpsink.h index ab20241e20..20c40e8d8b 100644 --- a/sys/bluez/gsta2dpsink.h +++ b/sys/bluez/gsta2dpsink.h @@ -28,7 +28,6 @@ #include "gstavdtpsink.h" G_BEGIN_DECLS - #define GST_TYPE_A2DP_SINK \ (gst_a2dp_sink_get_type()) #define GST_A2DP_SINK(obj) \ @@ -39,45 +38,45 @@ G_BEGIN_DECLS (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SINK)) #define GST_IS_A2DP_SINK_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SINK)) - typedef struct _GstA2dpSink GstA2dpSink; typedef struct _GstA2dpSinkClass GstA2dpSinkClass; -struct _GstA2dpSink { - GstBin bin; +struct _GstA2dpSink +{ + GstBin bin; - GstElement *rtp; - GstAvdtpSink *sink; - GstElement *capsfilter; - GstElement *fakesink; + GstElement *rtp; + GstAvdtpSink *sink; + GstElement *capsfilter; + GstElement *fakesink; - gchar *device; - gchar *transport; - gboolean autoconnect; - gboolean sink_is_in_bin; + gchar *device; + gchar *transport; + gboolean autoconnect; + gboolean sink_is_in_bin; - GstPad *ghostpad; - GstPadQueryFunction ghostpad_queryfunc; - GstPadEventFunction ghostpad_eventfunc; + GstPad *ghostpad; + GstPadQueryFunction ghostpad_queryfunc; + GstPadEventFunction ghostpad_eventfunc; - GstEvent *segment_event; - /* Store the tags received before the a2dpsender sink is created - * when it is created we forward this to it */ - GstTagList *taglist; + GstEvent *segment_event; + /* Store the tags received before the a2dpsender sink is created + * when it is created we forward this to it */ + GstTagList *taglist; - GMutex cb_mutex; + GMutex cb_mutex; }; -struct _GstA2dpSinkClass { - GstBinClass parent_class; +struct _GstA2dpSinkClass +{ + GstBinClass parent_class; }; -GType gst_a2dp_sink_get_type(void); +GType gst_a2dp_sink_get_type (void); gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin); -GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self); +GstCaps *gst_a2dp_sink_get_device_caps (GstA2dpSink * self); G_END_DECLS - #endif diff --git a/sys/bluez/gstavdtpsink.c b/sys/bluez/gstavdtpsink.c index f7cfc681f3..d8ac2bb686 100644 --- a/sys/bluez/gstavdtpsink.c +++ b/sys/bluez/gstavdtpsink.c @@ -43,8 +43,6 @@ GST_DEBUG_CATEGORY_STATIC (avdtp_sink_debug); #define GST_CAT_DEFAULT avdtp_sink_debug -#define BUFFER_SIZE 2048 -#define TEMPLATE_MAX_BITPOOL 64 #define CRC_PROTECTED 1 #define CRC_UNPROTECTED 0 @@ -58,19 +56,6 @@ GST_DEBUG_CATEGORY_STATIC (avdtp_sink_debug); g_mutex_unlock(&s->sink_lock); \ } G_STMT_END -struct bluetooth_data -{ - guint link_mtu; - - DBusConnection *conn; - guint8 codec; /* Bluetooth transport configuration */ - gchar *uuid; - guint8 *config; - gint config_size; - - gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ -}; - #define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) #define IS_MPEG_AUDIO(n) (strcmp((n), "audio/mpeg") == 0) @@ -105,23 +90,6 @@ static GstStaticPadTemplate avdtp_sink_factory = GST_RTP_PAYLOAD_DYNAMIC_STRING ", " "clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\"")); -static void -gst_avdtp_sink_transport_release (GstAvdtpSink * self) -{ - DBusMessage *msg; - const char *access_type = "w"; - - msg = dbus_message_new_method_call ("org.bluez", self->transport, - "org.bluez.MediaTransport", "Release"); - - dbus_message_append_args (msg, DBUS_TYPE_STRING, &access_type, - DBUS_TYPE_INVALID); - - dbus_connection_send (self->data->conn, msg, NULL); - - dbus_message_unref (msg); -} - static gboolean gst_avdtp_sink_stop (GstBaseSink * basesink) { @@ -134,20 +102,7 @@ gst_avdtp_sink_stop (GstBaseSink * basesink) self->watch_id = 0; } - if (self->stream) { - g_io_channel_shutdown (self->stream, TRUE, NULL); - g_io_channel_unref (self->stream); - self->stream = NULL; - } - - if (self->data) { - if (self->transport) - gst_avdtp_sink_transport_release (self); - if (self->data->conn) - dbus_connection_unref (self->data->conn); - g_free (self->data); - self->data = NULL; - } + gst_avdtp_connection_release (&self->conn); if (self->stream_caps) { gst_caps_unref (self->stream_caps); @@ -167,14 +122,9 @@ gst_avdtp_sink_finalize (GObject * object) { GstAvdtpSink *self = GST_AVDTP_SINK (object); - if (self->data) - gst_avdtp_sink_stop (GST_BASE_SINK (self)); + gst_avdtp_sink_stop (GST_BASE_SINK (self)); - if (self->device) - g_free (self->device); - - if (self->transport) - g_free (self->transport); + gst_avdtp_connection_reset (&self->conn); g_mutex_clear (&self->sink_lock); @@ -191,7 +141,7 @@ gst_avdtp_sink_set_property (GObject * object, guint prop_id, case PROP_DEVICE: if (sink->device) g_free (sink->device); - sink->device = g_value_dup_string (value); + gst_avdtp_connection_set_device (&sink->conn, g_value_get_string (value)); break; case PROP_AUTOCONNECT: @@ -201,7 +151,8 @@ gst_avdtp_sink_set_property (GObject * object, guint prop_id, case PROP_TRANSPORT: if (sink->transport) g_free (sink->transport); - sink->transport = g_value_dup_string (value); + gst_avdtp_connection_set_transport (&sink->conn, + g_value_get_string (value)); break; default: @@ -218,7 +169,7 @@ gst_avdtp_sink_get_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_DEVICE: - g_value_set_string (value, sink->device); + g_value_set_string (value, sink->conn.device); break; case PROP_AUTOCONNECT: @@ -226,7 +177,7 @@ gst_avdtp_sink_get_property (GObject * object, guint prop_id, break; case PROP_TRANSPORT: - g_value_set_string (value, sink->transport); + g_value_set_string (value, sink->conn.transport); break; default: @@ -235,300 +186,6 @@ gst_avdtp_sink_get_property (GObject * object, guint prop_id, } } -static GstStructure * -gst_avdtp_sink_parse_sbc_raw (GstAvdtpSink * self) -{ - a2dp_sbc_t *sbc = (a2dp_sbc_t *) self->data->config; - GstStructure *structure; - GValue value = G_VALUE_INIT; - GValue list = G_VALUE_INIT; - gboolean mono, stereo; - - structure = gst_structure_new_empty ("audio/x-sbc"); - - /* mode */ - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&value, G_TYPE_STRING); - if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO) { - g_value_set_static_string (&value, "mono"); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO) { - g_value_set_static_string (&value, "stereo"); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) { - g_value_set_static_string (&value, "dual"); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) { - g_value_set_static_string (&value, "joint"); - gst_value_list_prepend_value (&list, &value); - } - g_value_unset (&value); - gst_structure_take_value (structure, "channel-mode", &list); - - /* subbands */ - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&value, G_TYPE_INT); - if (sbc->subbands & SBC_SUBBANDS_4) { - g_value_set_int (&value, 4); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->subbands & SBC_SUBBANDS_8) { - g_value_set_int (&value, 8); - gst_value_list_prepend_value (&list, &value); - } - g_value_unset (&value); - gst_structure_take_value (structure, "subbands", &list); - - /* blocks */ - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&value, G_TYPE_INT); - if (sbc->block_length & SBC_BLOCK_LENGTH_16) { - g_value_set_int (&value, 16); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->block_length & SBC_BLOCK_LENGTH_12) { - g_value_set_int (&value, 12); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->block_length & SBC_BLOCK_LENGTH_8) { - g_value_set_int (&value, 8); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->block_length & SBC_BLOCK_LENGTH_4) { - g_value_set_int (&value, 4); - gst_value_list_prepend_value (&list, &value); - } - g_value_unset (&value); - gst_structure_take_value (structure, "blocks", &list); - - /* allocation */ - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&value, G_TYPE_STRING); - if (sbc->allocation_method & SBC_ALLOCATION_LOUDNESS) { - g_value_set_static_string (&value, "loudness"); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->allocation_method & SBC_ALLOCATION_SNR) { - g_value_set_static_string (&value, "snr"); - gst_value_list_prepend_value (&list, &value); - } - g_value_unset (&value); - gst_structure_take_value (structure, "allocation-method", &list); - - /* rate */ - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&value, G_TYPE_INT); - if (sbc->frequency & SBC_SAMPLING_FREQ_48000) { - g_value_set_int (&value, 48000); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->frequency & SBC_SAMPLING_FREQ_44100) { - g_value_set_int (&value, 44100); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->frequency & SBC_SAMPLING_FREQ_32000) { - g_value_set_int (&value, 32000); - gst_value_list_prepend_value (&list, &value); - } - if (sbc->frequency & SBC_SAMPLING_FREQ_16000) { - g_value_set_int (&value, 16000); - gst_value_list_prepend_value (&list, &value); - } - g_value_unset (&value); - gst_structure_take_value (structure, "rate", &list); - - /* bitpool */ - g_value_init (&value, GST_TYPE_INT_RANGE); - gst_value_set_int_range (&value, - MIN (sbc->min_bitpool, TEMPLATE_MAX_BITPOOL), - MIN (sbc->max_bitpool, TEMPLATE_MAX_BITPOOL)); - gst_structure_take_value (structure, "bitpool", &value); - - /* channels */ - mono = FALSE; - stereo = FALSE; - if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO) - mono = TRUE; - if ((sbc->channel_mode & SBC_CHANNEL_MODE_STEREO) || - (sbc->channel_mode & - SBC_CHANNEL_MODE_DUAL_CHANNEL) || - (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init (&value, GST_TYPE_INT_RANGE); - gst_value_set_int_range (&value, 1, 2); - } else { - g_value_init (&value, G_TYPE_INT); - if (mono) - g_value_set_int (&value, 1); - else if (stereo) - g_value_set_int (&value, 2); - else { - GST_ERROR_OBJECT (self, "Unexpected number of channels"); - g_value_set_int (&value, 0); - } - } - gst_structure_take_value (structure, "channels", &value); - - return structure; -} - -static GstStructure * -gst_avdtp_sink_parse_mpeg_raw (GstAvdtpSink * self) -{ - a2dp_mpeg_t *mpeg = (a2dp_mpeg_t *) self->data->config; - GstStructure *structure; - GValue value = G_VALUE_INIT; - GValue list = G_VALUE_INIT; - gboolean mono, stereo; - - GST_LOG_OBJECT (self, "parsing mpeg caps"); - - if ((mpeg->layer & (MPEG_LAYER_MP1 | MPEG_LAYER_MP2 | MPEG_LAYER_MP3)) == 0) { - GST_WARNING_OBJECT (self, "no valid mpeg layer"); - return NULL; - } - - structure = gst_structure_new_empty ("audio/mpeg"); - - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&value, G_TYPE_INT); - g_value_set_int (&value, 1); - gst_value_list_prepend_value (&list, &value); - g_value_set_int (&value, 2); - gst_value_list_prepend_value (&list, &value); - g_value_unset (&value); - gst_structure_take_value (structure, "mpegversion", &list); - - /* layer */ - GST_LOG_OBJECT (self, "setting mpeg layer"); - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&value, G_TYPE_INT); - if (mpeg->layer & MPEG_LAYER_MP1) { - g_value_set_int (&value, 1); - gst_value_list_prepend_value (&list, &value); - } - if (mpeg->layer & MPEG_LAYER_MP2) { - g_value_set_int (&value, 2); - gst_value_list_prepend_value (&list, &value); - } - if (mpeg->layer & MPEG_LAYER_MP3) { - g_value_set_int (&value, 3); - gst_value_list_prepend_value (&list, &value); - } - g_value_unset (&value); - gst_structure_take_value (structure, "layer", &list); - - /* rate */ - GST_LOG_OBJECT (self, "setting mpeg rate"); - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&value, G_TYPE_INT); - if (mpeg->frequency & MPEG_SAMPLING_FREQ_48000) { - g_value_set_int (&value, 48000); - gst_value_list_prepend_value (&list, &value); - } - if (mpeg->frequency & MPEG_SAMPLING_FREQ_44100) { - g_value_set_int (&value, 44100); - gst_value_list_prepend_value (&list, &value); - } - if (mpeg->frequency & MPEG_SAMPLING_FREQ_32000) { - g_value_set_int (&value, 32000); - gst_value_list_prepend_value (&list, &value); - } - if (mpeg->frequency & MPEG_SAMPLING_FREQ_24000) { - g_value_set_int (&value, 24000); - gst_value_list_prepend_value (&list, &value); - } - if (mpeg->frequency & MPEG_SAMPLING_FREQ_22050) { - g_value_set_int (&value, 22050); - gst_value_list_prepend_value (&list, &value); - } - if (mpeg->frequency & MPEG_SAMPLING_FREQ_16000) { - g_value_set_int (&value, 16000); - gst_value_list_prepend_value (&list, &value); - } - g_value_unset (&value); - gst_structure_take_value (structure, "rate", &list); - - /* channels */ - GST_LOG_OBJECT (self, "setting mpeg channels"); - mono = FALSE; - stereo = FALSE; - if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO) - mono = TRUE; - if ((mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO) || - (mpeg->channel_mode & - MPEG_CHANNEL_MODE_DUAL_CHANNEL) || - (mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init (&value, GST_TYPE_INT_RANGE); - gst_value_set_int_range (&value, 1, 2); - } else { - g_value_init (&value, G_TYPE_INT); - if (mono) - g_value_set_int (&value, 1); - else if (stereo) - g_value_set_int (&value, 2); - else { - GST_ERROR_OBJECT (self, "Unexpected number of channels"); - g_value_set_int (&value, 0); - } - } - gst_structure_take_value (structure, "channels", &value); - - return structure; -} - -static gboolean -gst_avdtp_sink_update_config (GstAvdtpSink * self) -{ - GstStructure *structure; - gchar *tmp; - - switch (self->data->codec) { - case A2DP_CODEC_SBC: - structure = gst_avdtp_sink_parse_sbc_raw (self); - break; - case A2DP_CODEC_MPEG12: - structure = gst_avdtp_sink_parse_mpeg_raw (self); - break; - default: - GST_ERROR_OBJECT (self, "Unsupported configuration"); - return FALSE; - } - - if (structure == NULL) - return FALSE; - - if (self->dev_caps != NULL) - gst_caps_unref (self->dev_caps); - - self->dev_caps = gst_caps_new_full (structure, NULL); - - tmp = gst_caps_to_string (self->dev_caps); - GST_DEBUG_OBJECT (self, "Transport configuration: %s", tmp); - g_free (tmp); - - return TRUE; -} - -static gboolean -gst_avdtp_sink_update_caps (GstAvdtpSink * self) -{ - GST_LOG_OBJECT (self, "updating device caps"); - - if (self->data->config_size == 0 || self->data->config == NULL) - return FALSE; - - return gst_avdtp_sink_update_config (self); -} - static gint gst_avdtp_sink_get_channel_mode (const gchar * mode) { @@ -594,194 +251,6 @@ gst_avdtp_sink_event (GstBaseSink * basesink, GstEvent * event) return TRUE; } -static gboolean -gst_avdtp_sink_transport_parse_property (GstAvdtpSink * self, - DBusMessageIter * i) -{ - const char *key; - DBusMessageIter variant_i; - - if (dbus_message_iter_get_arg_type (i) != DBUS_TYPE_STRING) { - GST_ERROR_OBJECT (self, "Property name not a string."); - return FALSE; - } - - dbus_message_iter_get_basic (i, &key); - - if (!dbus_message_iter_next (i)) { - GST_ERROR_OBJECT (self, "Property value missing"); - return FALSE; - } - - if (dbus_message_iter_get_arg_type (i) != DBUS_TYPE_VARIANT) { - GST_ERROR_OBJECT (self, "Property value not a variant."); - return FALSE; - } - - dbus_message_iter_recurse (i, &variant_i); - - switch (dbus_message_iter_get_arg_type (&variant_i)) { - case DBUS_TYPE_BYTE:{ - uint8_t value; - dbus_message_iter_get_basic (&variant_i, &value); - - if (g_str_equal (key, "Codec") == TRUE) - self->data->codec = value; - - break; - } - case DBUS_TYPE_STRING:{ - const char *value; - dbus_message_iter_get_basic (&variant_i, &value); - - if (g_str_equal (key, "UUID") == TRUE) { - g_free (self->data->uuid); - self->data->uuid = g_strdup (value); - } - - break; - } - case DBUS_TYPE_ARRAY:{ - DBusMessageIter array_i; - char *value; - int size; - - dbus_message_iter_recurse (&variant_i, &array_i); - dbus_message_iter_get_fixed_array (&array_i, &value, &size); - - if (g_str_equal (key, "Configuration")) { - g_free (self->data->config); - self->data->config = g_new0 (guint8, size); - self->data->config_size = size; - memcpy (self->data->config, value, size); - } - - break; - } - } - - return TRUE; -} - -static gboolean -gst_avdtp_sink_transport_acquire (GstAvdtpSink * self) -{ - DBusMessage *msg, *reply; - DBusError err; - const char *access_type = "w"; - int fd; - uint16_t imtu, omtu; - - dbus_error_init (&err); - - if (self->data->conn == NULL) - self->data->conn = dbus_bus_get (DBUS_BUS_SYSTEM, &err); - - msg = dbus_message_new_method_call ("org.bluez", self->transport, - "org.bluez.MediaTransport", "Acquire"); - - dbus_message_append_args (msg, DBUS_TYPE_STRING, &access_type, - DBUS_TYPE_INVALID); - - reply = dbus_connection_send_with_reply_and_block (self->data->conn, - msg, -1, &err); - - dbus_message_unref (msg); - - if (dbus_error_is_set (&err)) - goto fail; - - if (dbus_message_get_args (reply, &err, DBUS_TYPE_UNIX_FD, &fd, - DBUS_TYPE_UINT16, &imtu, - DBUS_TYPE_UINT16, &omtu, DBUS_TYPE_INVALID) == FALSE) - goto fail; - - dbus_message_unref (reply); - - self->stream = g_io_channel_unix_new (fd); - g_io_channel_set_encoding (self->stream, NULL, NULL); - g_io_channel_set_close_on_unref (self->stream, TRUE); - self->data->link_mtu = omtu; - GST_DEBUG_OBJECT (self, "stream_fd=%d mtu=%d", fd, omtu); - - return TRUE; - -fail: - GST_ERROR_OBJECT (self, "Failed to acquire transport stream: %s", - err.message); - - dbus_error_free (&err); - - if (reply) - dbus_message_unref (reply); - - return FALSE; -} - -static gboolean -gst_avdtp_sink_transport_get_properties (GstAvdtpSink * self) -{ - DBusMessage *msg, *reply; - DBusMessageIter arg_i, ele_i; - DBusError err; - - dbus_error_init (&err); - - /* Transport need to be acquire first to make sure the MTUs are - available */ - if (gst_avdtp_sink_transport_acquire (self) == FALSE) - return FALSE; - - msg = dbus_message_new_method_call ("org.bluez", self->transport, - "org.bluez.MediaTransport", "GetProperties"); - if (!msg) { - GST_ERROR_OBJECT (self, "D-Bus Memory allocation failed"); - return FALSE; - } - - reply = dbus_connection_send_with_reply_and_block (self->data->conn, - msg, -1, &err); - dbus_message_unref (msg); - - if (dbus_error_is_set (&err)) { - GST_ERROR_OBJECT (self, "GetProperties failed: %s", err.message); - dbus_error_free (&err); - return FALSE; - } - - if (!dbus_message_iter_init (reply, &arg_i)) { - GST_ERROR_OBJECT (self, "GetProperties reply has no arguments."); - goto fail; - } - - if (dbus_message_iter_get_arg_type (&arg_i) != DBUS_TYPE_ARRAY) { - GST_ERROR_OBJECT (self, "GetProperties argument is not an array."); - goto fail; - } - - dbus_message_iter_recurse (&arg_i, &ele_i); - while (dbus_message_iter_get_arg_type (&ele_i) != DBUS_TYPE_INVALID) { - - if (dbus_message_iter_get_arg_type (&ele_i) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter dict_i; - - dbus_message_iter_recurse (&ele_i, &dict_i); - - gst_avdtp_sink_transport_parse_property (self, &dict_i); - } - - if (!dbus_message_iter_next (&ele_i)) - break; - } - - return gst_avdtp_sink_update_caps (self); - -fail: - dbus_message_unref (reply); - return FALSE; - -} - static gboolean gst_avdtp_sink_start (GstBaseSink * basesink) { @@ -789,75 +258,35 @@ gst_avdtp_sink_start (GstBaseSink * basesink) GST_INFO_OBJECT (self, "start"); - self->data = g_new0 (struct bluetooth_data, 1); - - self->stream = NULL; self->stream_caps = NULL; self->mp3_using_crc = -1; self->channel_mode = -1; - if (self->transport == NULL) + if (self->conn.transport == NULL) return FALSE; - return gst_avdtp_sink_transport_get_properties (self); -} - -static gboolean -gst_avdtp_sink_conf_recv_stream_fd (GstAvdtpSink * self) -{ - struct bluetooth_data *data = self->data; - GError *gerr = NULL; - GIOStatus status; - GIOFlags flags; - int fd; - - /* Proceed if stream was already acquired */ - if (self->stream == NULL) { - GST_ERROR_OBJECT (self, "Error while configuring device: " - "could not acquire audio socket"); + if (!gst_avdtp_connection_acquire (&self->conn)) { + GST_ERROR_OBJECT (self, "Failed to acquire connection"); 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"); + if (!gst_avdtp_connection_get_properties (&self->conn)) { + GST_ERROR_OBJECT (self, "Failed to get transport properties"); + return FALSE; } - fd = g_io_channel_unix_get_fd (self->stream); + if (self->dev_caps) + gst_caps_unref (self->dev_caps); - /* 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) { - ssize_t bread = read (fd, data->buffer, data->link_mtu); - if (bread <= 0) - break; + self->dev_caps = gst_avdtp_connection_get_caps (&self->conn); + + if (!self->dev_caps) { + GST_ERROR_OBJECT (self, "Failed to get device caps"); + return FALSE; } - /* 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)); + GST_DEBUG_OBJECT (self, "Got connection caps: " GST_PTR_FORMAT, + self->dev_caps); return TRUE; } @@ -870,7 +299,7 @@ gst_avdtp_sink_preroll (GstBaseSink * basesink, GstBuffer * buffer) GST_AVDTP_SINK_MUTEX_LOCK (sink); - ret = gst_avdtp_sink_conf_recv_stream_fd (sink); + ret = gst_avdtp_connection_conf_recv_stream_fd (&sink->conn); GST_AVDTP_SINK_MUTEX_UNLOCK (sink); @@ -896,7 +325,7 @@ gst_avdtp_sink_render (GstBaseSink * basesink, GstBuffer * buffer) g_assert (!(g_io_channel_get_flags (self->stream) & G_IO_FLAG_NONBLOCK)); /* FIXME: why not use g_io_channel_write_chars() instead? */ - fd = g_io_channel_unix_get_fd (self->stream); + fd = g_io_channel_unix_get_fd (self->conn.stream); ret = write (fd, map.data, map.size); if (ret < 0) { /* FIXME: since this is probably fatal, shouldn't we post an error here? */ @@ -913,8 +342,8 @@ gst_avdtp_sink_unlock (GstBaseSink * basesink) { GstAvdtpSink *self = GST_AVDTP_SINK (basesink); - if (self->stream != NULL) - g_io_channel_flush (self->stream, NULL); + if (self->conn.stream != NULL) + g_io_channel_flush (self->conn.stream, NULL); return TRUE; } @@ -967,11 +396,10 @@ gst_avdtp_sink_class_init (GstAvdtpSinkClass * klass) static void gst_avdtp_sink_init (GstAvdtpSink * self) { - self->device = NULL; - self->transport = NULL; - self->data = NULL; + self->conn.device = NULL; + self->conn.transport = NULL; - self->stream = NULL; + self->conn.stream = NULL; self->dev_caps = NULL; @@ -1021,39 +449,39 @@ gst_avdtp_sink_set_device_caps (GstAvdtpSink * self, GstCaps * caps) guint gst_avdtp_sink_get_link_mtu (GstAvdtpSink * sink) { - return sink->data->link_mtu; + return sink->conn.data.link_mtu; } void gst_avdtp_sink_set_device (GstAvdtpSink * self, const gchar * dev) { - if (self->device != NULL) - g_free (self->device); + if (self->conn.device != NULL) + g_free (self->conn.device); GST_LOG_OBJECT (self, "Setting device: %s", dev); - self->device = g_strdup (dev); + self->conn.device = g_strdup (dev); } void gst_avdtp_sink_set_transport (GstAvdtpSink * self, const gchar * trans) { - if (self->transport != NULL) - g_free (self->transport); + if (self->conn.transport != NULL) + g_free (self->conn.transport); GST_LOG_OBJECT (self, "Setting transport: %s", trans); - self->transport = g_strdup (trans); + self->conn.transport = g_strdup (trans); } gchar * gst_avdtp_sink_get_device (GstAvdtpSink * self) { - return g_strdup (self->device); + return g_strdup (self->conn.device); } gchar * gst_avdtp_sink_get_transport (GstAvdtpSink * self) { - return g_strdup (self->transport); + return g_strdup (self->conn.transport); } void diff --git a/sys/bluez/gstavdtpsink.h b/sys/bluez/gstavdtpsink.h index d4b6bd1693..91eba40936 100644 --- a/sys/bluez/gstavdtpsink.h +++ b/sys/bluez/gstavdtpsink.h @@ -26,9 +26,9 @@ #include #include +#include "gstavdtputil.h" G_BEGIN_DECLS - #define GST_TYPE_AVDTP_SINK \ (gst_avdtp_sink_get_type()) #define GST_AVDTP_SINK(obj) \ @@ -41,66 +41,58 @@ G_BEGIN_DECLS (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVDTP_SINK)) #define GST_IS_AVDTP_SINK_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVDTP_SINK)) - typedef struct _GstAvdtpSink GstAvdtpSink; typedef struct _GstAvdtpSinkClass GstAvdtpSinkClass; -struct bluetooth_data; +struct _GstAvdtpSink +{ + GstBaseSink sink; -struct _GstAvdtpSink { - GstBaseSink sink; + GstAvdtpConnection conn; - gchar *device; - gchar *transport; - GIOChannel *stream; + gboolean autoconnect; - struct bluetooth_data *data; - gboolean autoconnect; + /* mp3 stream data (outside caps data) */ + gint mp3_using_crc; + gint channel_mode; - /* mp3 stream data (outside caps data)*/ - gint mp3_using_crc; - gint channel_mode; + /* stream connection data */ + GstCaps *stream_caps; - /* stream connection data */ - GstCaps *stream_caps; + GstCaps *dev_caps; - GstCaps *dev_caps; + GMutex sink_lock; - GMutex sink_lock; - - guint watch_id; + guint watch_id; }; -struct _GstAvdtpSinkClass { - GstBaseSinkClass parent_class; +struct _GstAvdtpSinkClass +{ + GstBaseSinkClass parent_class; }; -GType gst_avdtp_sink_get_type(void); +GType gst_avdtp_sink_get_type (void); -GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink); -gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *sink, - GstCaps *caps); +GstCaps *gst_avdtp_sink_get_device_caps (GstAvdtpSink * sink); +gboolean gst_avdtp_sink_set_device_caps (GstAvdtpSink * sink, GstCaps * caps); -guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink); +guint gst_avdtp_sink_get_link_mtu (GstAvdtpSink * sink); -void gst_avdtp_sink_set_device(GstAvdtpSink *sink, - const gchar *device); +void gst_avdtp_sink_set_device (GstAvdtpSink * sink, const gchar * device); -void gst_avdtp_sink_set_transport(GstAvdtpSink *sink, - const gchar *transport); +void gst_avdtp_sink_set_transport (GstAvdtpSink * sink, + const gchar * transport); -gchar *gst_avdtp_sink_get_device(GstAvdtpSink *sink); +gchar *gst_avdtp_sink_get_device (GstAvdtpSink * sink); -gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *sink); +gchar *gst_avdtp_sink_get_transport (GstAvdtpSink * sink); -gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin); +gboolean gst_avdtp_sink_plugin_init (GstPlugin * plugin); -void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc); +void gst_avdtp_sink_set_crc (GstAvdtpSink * self, gboolean crc); -void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self, - const gchar *mode); +void gst_avdtp_sink_set_channel_mode (GstAvdtpSink * self, const gchar * mode); G_END_DECLS - #endif /* __GST_AVDTP_SINK_H */ diff --git a/sys/bluez/gstavdtpsrc.c b/sys/bluez/gstavdtpsrc.c new file mode 100644 index 0000000000..fd2ccca5a3 --- /dev/null +++ b/sys/bluez/gstavdtpsrc.c @@ -0,0 +1,360 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Collabora Ltd. + * + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include "gstavdtpsrc.h" + +GST_DEBUG_CATEGORY_STATIC (avdtpsrc_debug); +#define GST_CAT_DEFAULT (avdtpsrc_debug) + +enum +{ + PROP_0, + PROP_TRANSPORT +}; + +GST_BOILERPLATE (GstAvdtpSrc, gst_avdtp_src, GstBaseSrc, GST_TYPE_BASE_SRC); + +static GstStaticPadTemplate gst_avdtp_src_template = + GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\"," + "payload = (int) " + GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 16000, 32000, " + "44100, 48000 }, " "encoding-name = (string) \"SBC\"; ")); + +static void gst_avdtp_src_finalize (GObject * object); +static void gst_avdtp_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_avdtp_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static GstCaps *gst_avdtp_src_getcaps (GstPad * pad); +static gboolean gst_avdtp_src_start (GstBaseSrc * bsrc); +static gboolean gst_avdtp_src_stop (GstBaseSrc * bsrc); +static GstFlowReturn gst_avdtp_src_create (GstBaseSrc * bsrc, guint64 offset, + guint length, GstBuffer ** outbuf); +static gboolean gst_avdtp_src_unlock (GstBaseSrc * bsrc); +static gboolean gst_avdtp_src_unlock_stop (GstBaseSrc * bsrc); + +static void +gst_avdtp_src_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_avdtp_src_template)); + + gst_element_class_set_details_simple (element_class, + "Bluetooth AVDTP Source", + "Codec/Depayloader/Network/RTP", + "Receives audio from an A2DP device", + "Arun Raghavan "); +} + +static void +gst_avdtp_src_class_init (GstAvdtpSrcClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSrcClass *basesrc_class = GST_BASE_SRC_CLASS (klass); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_avdtp_src_finalize); + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_avdtp_src_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_avdtp_src_get_property); + + basesrc_class->start = GST_DEBUG_FUNCPTR (gst_avdtp_src_start); + basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_avdtp_src_stop); + basesrc_class->create = GST_DEBUG_FUNCPTR (gst_avdtp_src_create); + basesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_avdtp_src_unlock); + basesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_avdtp_src_unlock_stop); + + g_object_class_install_property (gobject_class, PROP_TRANSPORT, + g_param_spec_string ("transport", + "Transport", "Use configured transport", NULL, G_PARAM_READWRITE)); + + GST_DEBUG_CATEGORY_INIT (avdtpsrc_debug, "avdtpsrc", 0, + "Bluetooth AVDTP Source"); +} + +static void +gst_avdtp_src_init (GstAvdtpSrc * avdtpsrc, GstAvdtpSrcClass * klass) +{ + avdtpsrc->poll = gst_poll_new (TRUE); + + gst_base_src_set_format (GST_BASE_SRC (avdtpsrc), GST_FORMAT_DEFAULT); + gst_base_src_set_live (GST_BASE_SRC (avdtpsrc), TRUE); + gst_base_src_set_do_timestamp (GST_BASE_SRC (avdtpsrc), TRUE); + + gst_pad_set_getcaps_function (GST_BASE_SRC_PAD (avdtpsrc), + GST_DEBUG_FUNCPTR (gst_avdtp_src_getcaps)); +} + +static void +gst_avdtp_src_finalize (GObject * object) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (object); + + gst_poll_free (avdtpsrc->poll); + + gst_avdtp_connection_reset (&avdtpsrc->conn); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_avdtp_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (object); + + switch (prop_id) { + case PROP_TRANSPORT: + g_value_set_string (value, avdtpsrc->conn.transport); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_avdtp_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (object); + + switch (prop_id) { + case PROP_TRANSPORT: + gst_avdtp_connection_set_transport (&avdtpsrc->conn, + g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstCaps * +gst_avdtp_src_getcaps (GstPad * pad) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (gst_pad_get_parent_element (pad)); + GstCaps *ret; + + if (avdtpsrc->dev_caps) { + const GValue *value; + const char *format; + int rate; + GstStructure *structure = gst_caps_get_structure (avdtpsrc->dev_caps, 0); + + format = gst_structure_get_name (structure); + + if (g_str_equal (format, "audio/x-sbc")) { + ret = gst_caps_new_simple ("application/x-rtp", + "encoding-name", G_TYPE_STRING, "SBC", NULL); + } else if (g_str_equal (format, "audio/mpeg")) { + GST_ERROR_OBJECT (avdtpsrc, "Only SBC is supported at " "the moment"); + } + + value = gst_structure_get_value (structure, "rate"); + if (!value || !G_VALUE_HOLDS_INT (value)) { + GST_ERROR_OBJECT (avdtpsrc, "Failed to get sample rate"); + goto fail; + } + rate = g_value_get_int (value); + + gst_caps_set_simple (ret, "clock-rate", G_TYPE_INT, rate, NULL); + } else { + ret = gst_caps_ref (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad))); + } + + return ret; + +fail: + if (ret) + gst_caps_unref (ret); + + return NULL; +} + +static gboolean +gst_avdtp_src_start (GstBaseSrc * bsrc) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (bsrc); + + /* None of this can go into prepare() since we need to set up the + * connection to figure out what format the device is going to send us. + */ + + if (!gst_avdtp_connection_acquire (&avdtpsrc->conn)) { + GST_ERROR_OBJECT (avdtpsrc, "Failed to acquire connection"); + return FALSE; + } + + if (!gst_avdtp_connection_get_properties (&avdtpsrc->conn)) { + GST_ERROR_OBJECT (avdtpsrc, "Failed to get transport properties"); + goto fail; + } + + if (!gst_avdtp_connection_conf_recv_stream_fd (&avdtpsrc->conn)) { + GST_ERROR_OBJECT (avdtpsrc, "Failed to configure stream fd"); + goto fail; + } + + GST_DEBUG_OBJECT (avdtpsrc, "Setting block size to link MTU (%d)", + avdtpsrc->conn.data.link_mtu); + gst_base_src_set_blocksize (GST_BASE_SRC (avdtpsrc), + avdtpsrc->conn.data.link_mtu); + + avdtpsrc->dev_caps = gst_avdtp_connection_get_caps (&avdtpsrc->conn); + if (!avdtpsrc->dev_caps) { + GST_ERROR_OBJECT (avdtpsrc, "Failed to get device caps"); + goto fail; + } + + gst_poll_fd_init (&avdtpsrc->pfd); + avdtpsrc->pfd.fd = g_io_channel_unix_get_fd (avdtpsrc->conn.stream); + + gst_poll_add_fd (avdtpsrc->poll, &avdtpsrc->pfd); + gst_poll_fd_ctl_read (avdtpsrc->poll, &avdtpsrc->pfd, TRUE); + gst_poll_set_flushing (avdtpsrc->poll, FALSE); + + g_atomic_int_set (&avdtpsrc->unlocked, FALSE); + + return TRUE; + +fail: + gst_avdtp_connection_release (&avdtpsrc->conn); + return FALSE; +} + +static gboolean +gst_avdtp_src_stop (GstBaseSrc * bsrc) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (bsrc); + + gst_poll_remove_fd (avdtpsrc->poll, &avdtpsrc->pfd); + gst_poll_set_flushing (avdtpsrc->poll, TRUE); + + gst_avdtp_connection_release (&avdtpsrc->conn); + + if (avdtpsrc->dev_caps) { + gst_caps_unref (avdtpsrc->dev_caps); + avdtpsrc->dev_caps = NULL; + } + + return TRUE; +} + +static GstFlowReturn +gst_avdtp_src_create (GstBaseSrc * bsrc, guint64 offset, + guint length, GstBuffer ** outbuf) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (bsrc); + GstBuffer *buf = NULL; + int ret; + + if (g_atomic_int_get (&avdtpsrc->unlocked)) + return GST_FLOW_WRONG_STATE; + + /* We don't operate in GST_FORMAT_BYTES, so offset is ignored */ + + while ((ret = gst_poll_wait (avdtpsrc->poll, GST_CLOCK_TIME_NONE))) { + if (g_atomic_int_get (&avdtpsrc->unlocked)) + /* We're unlocked, time to gtfo */ + return GST_FLOW_WRONG_STATE; + + if (ret < 0) + /* Something went wrong */ + goto read_error; + + if (ret > 0) + /* Got some data */ + break; + } + + buf = gst_buffer_new_and_alloc (length); + ret = read (avdtpsrc->pfd.fd, GST_BUFFER_DATA (buf), length); + + if (ret < 0) + goto read_error; + + GST_LOG_OBJECT (avdtpsrc, "Read %d bytes", ret); + + if (ret < length) { + /* Create a subbuffer for as much as we've actually read */ + *outbuf = gst_buffer_create_sub (buf, 0, ret); + gst_buffer_unref (buf); + } else + *outbuf = buf; + + return GST_FLOW_OK; + +read_error: + gst_buffer_unref (buf); + + GST_ERROR_OBJECT (avdtpsrc, "Error while reading audio data: %s", + strerror (errno)); + + return GST_FLOW_ERROR; +} + +static gboolean +gst_avdtp_src_unlock (GstBaseSrc * bsrc) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (bsrc); + + g_atomic_int_set (&avdtpsrc->unlocked, TRUE); + + gst_poll_set_flushing (avdtpsrc->poll, TRUE); +} + +static gboolean +gst_avdtp_src_unlock_stop (GstBaseSrc * bsrc) +{ + GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (bsrc); + + g_atomic_int_set (&avdtpsrc->unlocked, FALSE); + + gst_poll_set_flushing (avdtpsrc->poll, FALSE); + + /* Flush out any stale data that might be buffered */ + gst_avdtp_connection_conf_recv_stream_fd (&avdtpsrc->conn); +} + +gboolean +gst_avdtp_src_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "avdtpsrc", GST_RANK_NONE, + GST_TYPE_AVDTP_SRC); +} diff --git a/sys/bluez/gstavdtpsrc.h b/sys/bluez/gstavdtpsrc.h new file mode 100644 index 0000000000..9a3cb70602 --- /dev/null +++ b/sys/bluez/gstavdtpsrc.h @@ -0,0 +1,65 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Collabora Ltd. + * + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GST_AVDTP_SRC_H +#define __GST_AVDTP_SRC_H + +#include +#include +#include "gstavdtputil.h" + +G_BEGIN_DECLS +#define GST_TYPE_AVDTP_SRC \ + (gst_avdtp_src_get_type()) +#define GST_AVDTP_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVDTP_SRC, GstAvdtpSrc)) +#define GST_AVDTP_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVDTP_SRC, GstAvdtpSrc)) +#define GST_IS_AVDTP_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVDTP_SRC)) +#define GST_IS_AVDTP_SRC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVDTP_SRC)) +typedef struct _GstAvdtpSrc GstAvdtpSrc; +typedef struct _GstAvdtpSrcClass GstAvdtpSrcClass; + +struct _GstAvdtpSrcClass +{ + GstBaseSrcClass parentclass; +}; + +struct _GstAvdtpSrc +{ + GstBaseSrc basesrc; + + GstAvdtpConnection conn; + GstCaps *dev_caps; + + GstPoll *poll; + GstPollFD pfd; + volatile gint unlocked; +}; + +gboolean gst_avdtp_src_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif diff --git a/sys/bluez/gstavdtputil.c b/sys/bluez/gstavdtputil.c new file mode 100644 index 0000000000..fa94572482 --- /dev/null +++ b/sys/bluez/gstavdtputil.c @@ -0,0 +1,672 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2012 Collabora Ltd. + * + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include "a2dp-codecs.h" + +#include +#include "gstavdtputil.h" + +#define TEMPLATE_MAX_BITPOOL 64 + +gboolean +gst_avdtp_connection_acquire (GstAvdtpConnection * conn) +{ + DBusMessage *msg, *reply; + DBusError err; + const char *access_type = "rw"; + int fd; + uint16_t imtu, omtu; + + dbus_error_init (&err); + + if (conn->transport == NULL) { + GST_ERROR ("No transport specified"); + return FALSE; + } + + if (conn->data.conn == NULL) + conn->data.conn = dbus_bus_get (DBUS_BUS_SYSTEM, &err); + + msg = dbus_message_new_method_call ("org.bluez", conn->transport, + "org.bluez.MediaTransport", "Acquire"); + + dbus_message_append_args (msg, DBUS_TYPE_STRING, &access_type, + DBUS_TYPE_INVALID); + + reply = dbus_connection_send_with_reply_and_block (conn->data.conn, + msg, -1, &err); + + dbus_message_unref (msg); + + if (dbus_error_is_set (&err)) + goto fail; + + if (dbus_message_get_args (reply, &err, DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_UINT16, &imtu, + DBUS_TYPE_UINT16, &omtu, DBUS_TYPE_INVALID) == FALSE) + goto fail; + + dbus_message_unref (reply); + + conn->stream = g_io_channel_unix_new (fd); + g_io_channel_set_encoding (conn->stream, NULL, NULL); + g_io_channel_set_close_on_unref (conn->stream, TRUE); + conn->data.link_mtu = omtu; + + return TRUE; + +fail: + GST_ERROR ("Failed to acquire transport stream: %s", err.message); + + dbus_error_free (&err); + + if (reply) + dbus_message_unref (reply); + + return FALSE; +} + +static void +gst_avdtp_connection_transport_release (GstAvdtpConnection * conn) +{ + DBusMessage *msg; + const char *access_type = "rw"; + + msg = dbus_message_new_method_call ("org.bluez", conn->transport, + "org.bluez.MediaTransport", "Release"); + + dbus_message_append_args (msg, DBUS_TYPE_STRING, &access_type, + DBUS_TYPE_INVALID); + + dbus_connection_send (conn->data.conn, msg, NULL); + + dbus_message_unref (msg); +} + +void +gst_avdtp_connection_release (GstAvdtpConnection * conn) +{ + if (conn->stream) { + g_io_channel_shutdown (conn->stream, TRUE, NULL); + g_io_channel_unref (conn->stream); + conn->stream = NULL; + } + + if (conn->data.uuid) { + g_free (conn->data.uuid); + conn->data.uuid = NULL; + } + + if (conn->data.config) { + g_free (conn->data.config); + conn->data.config = NULL; + } + + if (conn->data.conn) { + if (conn->transport) + gst_avdtp_connection_transport_release (conn); + + dbus_connection_unref (conn->data.conn); + + conn->data.conn = FALSE; + } +} + +void +gst_avdtp_connection_reset (GstAvdtpConnection * conn) +{ + gst_avdtp_connection_release (conn); + + if (conn->device) { + g_free (conn->device); + conn->device = NULL; + } + + if (conn->transport) { + g_free (conn->transport); + conn->transport = NULL; + } +} + +void +gst_avdtp_connection_set_device (GstAvdtpConnection * conn, const char *device) +{ + if (conn->device) + g_free (conn->device); + + conn->device = g_strdup (device); +} + +void +gst_avdtp_connection_set_transport (GstAvdtpConnection * conn, + const char *transport) +{ + if (conn->transport) + g_free (conn->transport); + + conn->transport = g_strdup (transport); +} + +static gboolean +gst_avdtp_connection_parse_property (GstAvdtpConnection * conn, + DBusMessageIter * i) +{ + const char *key; + DBusMessageIter variant_i; + + if (dbus_message_iter_get_arg_type (i) != DBUS_TYPE_STRING) { + GST_ERROR ("Property name not a string."); + return FALSE; + } + + dbus_message_iter_get_basic (i, &key); + + if (!dbus_message_iter_next (i)) { + GST_ERROR ("Property value missing"); + return FALSE; + } + + if (dbus_message_iter_get_arg_type (i) != DBUS_TYPE_VARIANT) { + GST_ERROR ("Property value not a variant."); + return FALSE; + } + + dbus_message_iter_recurse (i, &variant_i); + + switch (dbus_message_iter_get_arg_type (&variant_i)) { + case DBUS_TYPE_BYTE:{ + uint8_t value; + dbus_message_iter_get_basic (&variant_i, &value); + + if (g_str_equal (key, "Codec") == TRUE) + conn->data.codec = value; + + break; + } + case DBUS_TYPE_STRING:{ + const char *value; + dbus_message_iter_get_basic (&variant_i, &value); + + if (g_str_equal (key, "UUID") == TRUE) { + g_free (conn->data.uuid); + conn->data.uuid = g_strdup (value); + } + + break; + } + case DBUS_TYPE_ARRAY:{ + DBusMessageIter array_i; + char *value; + int size; + + dbus_message_iter_recurse (&variant_i, &array_i); + dbus_message_iter_get_fixed_array (&array_i, &value, &size); + + if (g_str_equal (key, "Configuration")) { + g_free (conn->data.config); + conn->data.config = g_new0 (guint8, size); + conn->data.config_size = size; + memcpy (conn->data.config, value, size); + } + + break; + } + } + + return TRUE; +} + +gboolean +gst_avdtp_connection_get_properties (GstAvdtpConnection * conn) +{ + DBusMessage *msg, *reply; + DBusMessageIter arg_i, ele_i; + DBusError err; + + dbus_error_init (&err); + + msg = dbus_message_new_method_call ("org.bluez", conn->transport, + "org.bluez.MediaTransport", "GetProperties"); + + if (!msg) { + GST_ERROR ("D-Bus Memory allocation failed"); + return FALSE; + } + + reply = dbus_connection_send_with_reply_and_block (conn->data.conn, + msg, -1, &err); + + dbus_message_unref (msg); + + if (dbus_error_is_set (&err)) { + GST_ERROR ("GetProperties failed: %s", err.message); + dbus_error_free (&err); + return FALSE; + } + + if (!dbus_message_iter_init (reply, &arg_i)) { + GST_ERROR ("GetProperties reply has no arguments."); + goto fail; + } + + if (dbus_message_iter_get_arg_type (&arg_i) != DBUS_TYPE_ARRAY) { + GST_ERROR ("GetProperties argument is not an array."); + goto fail; + } + + dbus_message_iter_recurse (&arg_i, &ele_i); + while (dbus_message_iter_get_arg_type (&ele_i) != DBUS_TYPE_INVALID) { + + if (dbus_message_iter_get_arg_type (&ele_i) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter dict_i; + + dbus_message_iter_recurse (&ele_i, &dict_i); + + gst_avdtp_connection_parse_property (conn, &dict_i); + } + + if (!dbus_message_iter_next (&ele_i)) + break; + } + + return TRUE; + +fail: + dbus_message_unref (reply); + return FALSE; + +} + +static GstStructure * +gst_avdtp_util_parse_sbc_raw (void *config) +{ + a2dp_sbc_t *sbc = (a2dp_sbc_t *) config; + GstStructure *structure; + GValue *value; + GValue *list; + gboolean mono, stereo; + + structure = gst_structure_empty_new ("audio/x-sbc"); + value = g_value_init (g_new0 (GValue, 1), G_TYPE_STRING); + list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST); + + /* mode */ + if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO) { + g_value_set_static_string (value, "mono"); + gst_value_list_prepend_value (list, value); + } + if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO) { + g_value_set_static_string (value, "stereo"); + gst_value_list_prepend_value (list, value); + } + if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) { + g_value_set_static_string (value, "dual"); + gst_value_list_prepend_value (list, value); + } + if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) { + g_value_set_static_string (value, "joint"); + gst_value_list_prepend_value (list, value); + } + if (gst_value_list_get_size (list) == 1) + gst_structure_set_value (structure, "mode", value); + else + gst_structure_take_value (structure, "mode", list); + + g_value_unset (value); + g_value_reset (list); + + /* subbands */ + value = g_value_init (value, G_TYPE_INT); + if (sbc->subbands & SBC_SUBBANDS_4) { + g_value_set_int (value, 4); + gst_value_list_prepend_value (list, value); + } + if (sbc->subbands & SBC_SUBBANDS_8) { + g_value_set_int (value, 8); + gst_value_list_prepend_value (list, value); + } + if (gst_value_list_get_size (list) == 1) + gst_structure_set_value (structure, "subbands", value); + else + gst_structure_take_value (structure, "subbands", list); + + g_value_unset (value); + g_value_reset (list); + + /* blocks */ + value = g_value_init (value, G_TYPE_INT); + if (sbc->block_length & SBC_BLOCK_LENGTH_16) { + g_value_set_int (value, 16); + gst_value_list_prepend_value (list, value); + } + if (sbc->block_length & SBC_BLOCK_LENGTH_12) { + g_value_set_int (value, 12); + gst_value_list_prepend_value (list, value); + } + if (sbc->block_length & SBC_BLOCK_LENGTH_8) { + g_value_set_int (value, 8); + gst_value_list_prepend_value (list, value); + } + if (sbc->block_length & SBC_BLOCK_LENGTH_4) { + g_value_set_int (value, 4); + gst_value_list_prepend_value (list, value); + } + if (gst_value_list_get_size (list) == 1) + gst_structure_set_value (structure, "blocks", value); + else + gst_structure_take_value (structure, "blocks", list); + + g_value_unset (value); + g_value_reset (list); + + /* allocation */ + g_value_init (value, G_TYPE_STRING); + if (sbc->allocation_method & SBC_ALLOCATION_LOUDNESS) { + g_value_set_static_string (value, "loudness"); + gst_value_list_prepend_value (list, value); + } + if (sbc->allocation_method & SBC_ALLOCATION_SNR) { + g_value_set_static_string (value, "snr"); + gst_value_list_prepend_value (list, value); + } + if (gst_value_list_get_size (list) == 1) + gst_structure_set_value (structure, "allocation", value); + else + gst_structure_take_value (structure, "allocation", list); + + g_value_unset (value); + g_value_reset (list); + + /* rate */ + g_value_init (value, G_TYPE_INT); + if (sbc->frequency & SBC_SAMPLING_FREQ_48000) { + g_value_set_int (value, 48000); + gst_value_list_prepend_value (list, value); + } + if (sbc->frequency & SBC_SAMPLING_FREQ_44100) { + g_value_set_int (value, 44100); + gst_value_list_prepend_value (list, value); + } + if (sbc->frequency & SBC_SAMPLING_FREQ_32000) { + g_value_set_int (value, 32000); + gst_value_list_prepend_value (list, value); + } + if (sbc->frequency & SBC_SAMPLING_FREQ_16000) { + g_value_set_int (value, 16000); + gst_value_list_prepend_value (list, value); + } + if (gst_value_list_get_size (list) == 1) + gst_structure_set_value (structure, "rate", value); + else + gst_structure_take_value (structure, "rate", list); + + g_value_unset (value); + g_value_reset (list); + + /* bitpool */ + value = g_value_init (value, GST_TYPE_INT_RANGE); + gst_value_set_int_range (value, + MIN (sbc->min_bitpool, TEMPLATE_MAX_BITPOOL), + MIN (sbc->max_bitpool, TEMPLATE_MAX_BITPOOL)); + gst_structure_set_value (structure, "bitpool", value); + g_value_unset (value); + + /* channels */ + mono = FALSE; + stereo = FALSE; + if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO) + mono = TRUE; + if ((sbc->channel_mode & SBC_CHANNEL_MODE_STEREO) || + (sbc->channel_mode & + SBC_CHANNEL_MODE_DUAL_CHANNEL) || + (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { + g_value_init (value, GST_TYPE_INT_RANGE); + gst_value_set_int_range (value, 1, 2); + } else { + g_value_init (value, G_TYPE_INT); + if (mono) + g_value_set_int (value, 1); + else if (stereo) + g_value_set_int (value, 2); + else { + GST_ERROR ("Unexpected number of channels"); + g_value_set_int (value, 0); + } + } + + gst_structure_set_value (structure, "channels", value); + + g_value_unset (value); + g_free (value); + g_value_unset (list); + g_free (list); + + return structure; +} + +static GstStructure * +gst_avdtp_util_parse_mpeg_raw (void *config) +{ + a2dp_mpeg_t *mpeg = (a2dp_mpeg_t *) config; + GstStructure *structure; + GValue *value; + GValue *list; + gboolean valid_layer = FALSE; + gboolean mono, stereo; + + structure = gst_structure_empty_new ("audio/mpeg"); + value = g_new0 (GValue, 1); + g_value_init (value, G_TYPE_INT); + + list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST); + g_value_set_int (value, 1); + gst_value_list_prepend_value (list, value); + g_value_set_int (value, 2); + gst_value_list_prepend_value (list, value); + gst_structure_set_value (structure, "mpegversion", list); + g_free (list); + + /* layer */ + list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST); + if (mpeg->layer & MPEG_LAYER_MP1) { + g_value_set_int (value, 1); + gst_value_list_prepend_value (list, value); + valid_layer = TRUE; + } + if (mpeg->layer & MPEG_LAYER_MP2) { + g_value_set_int (value, 2); + gst_value_list_prepend_value (list, value); + valid_layer = TRUE; + } + if (mpeg->layer & MPEG_LAYER_MP3) { + g_value_set_int (value, 3); + gst_value_list_prepend_value (list, value); + valid_layer = TRUE; + } + if (list) { + if (gst_value_list_get_size (list) == 1) + gst_structure_set_value (structure, "layer", value); + else + gst_structure_set_value (structure, "layer", list); + g_free (list); + list = NULL; + } + + if (!valid_layer) { + gst_structure_free (structure); + g_free (value); + return NULL; + } + + /* rate */ + list = g_value_init (g_new0 (GValue, 1), GST_TYPE_LIST); + if (mpeg->frequency & MPEG_SAMPLING_FREQ_48000) { + g_value_set_int (value, 48000); + gst_value_list_prepend_value (list, value); + } + if (mpeg->frequency & MPEG_SAMPLING_FREQ_44100) { + g_value_set_int (value, 44100); + gst_value_list_prepend_value (list, value); + } + if (mpeg->frequency & MPEG_SAMPLING_FREQ_32000) { + g_value_set_int (value, 32000); + gst_value_list_prepend_value (list, value); + } + if (mpeg->frequency & MPEG_SAMPLING_FREQ_24000) { + g_value_set_int (value, 24000); + gst_value_list_prepend_value (list, value); + } + if (mpeg->frequency & MPEG_SAMPLING_FREQ_22050) { + g_value_set_int (value, 22050); + gst_value_list_prepend_value (list, value); + } + if (mpeg->frequency & MPEG_SAMPLING_FREQ_16000) { + g_value_set_int (value, 16000); + gst_value_list_prepend_value (list, value); + } + g_value_unset (value); + if (list) { + if (gst_value_list_get_size (list) == 1) + gst_structure_set_value (structure, "rate", value); + else + gst_structure_set_value (structure, "rate", list); + g_free (list); + list = NULL; + } + + /* channels */ + mono = FALSE; + stereo = FALSE; + if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO) + mono = TRUE; + if ((mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO) || + (mpeg->channel_mode & + MPEG_CHANNEL_MODE_DUAL_CHANNEL) || + (mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { + g_value_init (value, GST_TYPE_INT_RANGE); + gst_value_set_int_range (value, 1, 2); + } else { + g_value_init (value, G_TYPE_INT); + if (mono) + g_value_set_int (value, 1); + else if (stereo) + g_value_set_int (value, 2); + else { + GST_ERROR ("Unexpected number of channels"); + g_value_set_int (value, 0); + } + } + gst_structure_set_value (structure, "channels", value); + g_free (value); + + return structure; +} + +GstCaps * +gst_avdtp_connection_get_caps (GstAvdtpConnection * conn) +{ + GstCaps *caps; + GstStructure *structure; + + if (conn->data.config_size == 0 || conn->data.config == NULL) + return NULL; + + switch (conn->data.codec) { + case A2DP_CODEC_SBC: + structure = gst_avdtp_util_parse_sbc_raw (conn->data.config); + break; + case A2DP_CODEC_MPEG12: + structure = gst_avdtp_util_parse_mpeg_raw (conn->data.config); + break; + default: + GST_ERROR ("Unsupported configuration"); + return NULL; + } + + if (structure == NULL) + return FALSE; + + caps = gst_caps_new_full (structure, NULL); + + return caps; +} + +gboolean +gst_avdtp_connection_conf_recv_stream_fd (GstAvdtpConnection * conn) +{ + struct bluetooth_data *data = &conn->data; + GError *gerr = NULL; + GIOStatus status; + GIOFlags flags; + int fd; + + /* Proceed if stream was already acquired */ + if (conn->stream == NULL) { + GST_ERROR ("Error while configuring device: " + "could not acquire audio socket"); + return FALSE; + } + + /* set stream socket to nonblock */ + flags = g_io_channel_get_flags (conn->stream); + flags |= G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags (conn->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) + GST_WARNING ("Error while setting server socket to nonblock"); + + fd = g_io_channel_unix_get_fd (conn->stream); + + /* It is possible there is some outstanding + data in the pipe - we have to empty it */ + while (1) { + ssize_t bread = read (fd, data->buffer, data->link_mtu); + if (bread <= 0) + break; + } + + /* set stream socket to block */ + flags = g_io_channel_get_flags (conn->stream); + flags &= ~G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags (conn->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) + GST_WARNING ("Error while setting server socket to block"); + + memset (data->buffer, 0, sizeof (data->buffer)); + + return TRUE; +} diff --git a/sys/bluez/gstavdtputil.h b/sys/bluez/gstavdtputil.h new file mode 100644 index 0000000000..e75bdea333 --- /dev/null +++ b/sys/bluez/gstavdtputil.h @@ -0,0 +1,69 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2012 Collabora Ltd. + * + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GST_AVDTP_UTIL_H +#define __GST_AVDTP_UTIL_H + +#include + +#include + +G_BEGIN_DECLS +#define DEFAULT_CODEC_BUFFER_SIZE 2048 + struct bluetooth_data +{ + guint link_mtu; + + DBusConnection *conn; + guint8 codec; /* Bluetooth transport configuration */ + gchar *uuid; + guint8 *config; + gint config_size; + + gchar buffer[DEFAULT_CODEC_BUFFER_SIZE]; /* Codec transfer buffer */ +}; + +typedef struct _GstAvdtpConnection GstAvdtpConnection; + +struct _GstAvdtpConnection +{ + gchar *device; + gchar *transport; + GIOChannel *stream; + + struct bluetooth_data data; +}; + +gboolean gst_avdtp_connection_acquire (GstAvdtpConnection * conn); +void gst_avdtp_connection_release (GstAvdtpConnection * conn); +gboolean gst_avdtp_connection_get_properties (GstAvdtpConnection * conn); +GstCaps *gst_avdtp_connection_get_caps (GstAvdtpConnection * conn); +void gst_avdtp_connection_set_device (GstAvdtpConnection * conn, + const char *device); +void gst_avdtp_connection_set_transport (GstAvdtpConnection * conn, + const char *transport); +gboolean gst_avdtp_connection_conf_recv_stream_fd (GstAvdtpConnection * conn); + +G_END_DECLS +#endif