| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> |
| * 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 <config.h> |
| #endif |
| |
| #include <string.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| |
| #include <bluetooth/bluetooth.h> |
| #include "a2dp-codecs.h" |
| |
| #include <gio/gunixfdlist.h> |
| #include <gst/gst.h> |
| #include "gstavdtputil.h" |
| #include "bluez.h" |
| |
| #define TEMPLATE_MAX_BITPOOL 64 |
| |
| GST_DEBUG_CATEGORY_EXTERN (avdtp_debug); |
| #define GST_CAT_DEFAULT avdtp_debug |
| |
| static void gst_avdtp_connection_transport_release (GstAvdtpConnection * conn); |
| |
| static gboolean |
| on_state_change (BluezMediaTransport1 * proxy, GParamSpec * pspec, |
| GstAvdtpConnection * conn) |
| { |
| const gchar *newstate; |
| gboolean is_idle; |
| |
| newstate = bluez_media_transport1_get_state (proxy); |
| is_idle = g_str_equal (newstate, "idle"); |
| |
| if (!conn->data.is_acquired && !is_idle) { |
| GST_DEBUG ("Re-acquiring connection"); |
| gst_avdtp_connection_acquire (conn, TRUE); |
| |
| } else if (is_idle) { |
| /* We don't know if we need to release the transport -- that may have been |
| * done for us by bluez already! Or not ... so release it just in case, but |
| * mark its stale beforehand to suppress any errors. */ |
| GST_DEBUG ("Marking connection stale"); |
| conn->data.is_acquired = FALSE; |
| gst_avdtp_connection_transport_release (conn); |
| |
| } else |
| GST_DEBUG ("State is %s, acquired is %s", newstate, |
| conn->data.is_acquired ? "true" : "false"); |
| |
| return TRUE; |
| } |
| |
| gboolean |
| gst_avdtp_connection_acquire (GstAvdtpConnection * conn, gboolean use_try) |
| { |
| GVariant *handle = NULL; |
| GUnixFDList *fd_list = NULL; |
| GError *err = NULL; |
| int fd; |
| uint16_t imtu, omtu; |
| |
| if (conn->transport == NULL) { |
| GST_ERROR ("No transport specified"); |
| return FALSE; |
| } |
| |
| if (conn->data.conn == NULL) { |
| conn->data.conn = |
| bluez_media_transport1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, |
| G_DBUS_PROXY_FLAGS_NONE, "org.bluez", conn->transport, NULL, &err); |
| |
| if (conn->data.conn == NULL) { |
| GST_ERROR ("Failed to create proxy for media transport: %s", |
| err && err->message ? err->message : "Unknown error"); |
| g_clear_error (&err); |
| return FALSE; |
| } |
| |
| g_signal_connect (conn->data.conn, "notify::state", |
| G_CALLBACK (on_state_change), conn); |
| } |
| |
| if (conn->data.is_acquired) { |
| GST_INFO ("Transport is already acquired"); |
| return TRUE; |
| } |
| |
| if (use_try) { |
| if (!bluez_media_transport1_call_try_acquire_sync (conn->data.conn, |
| NULL, &handle, &imtu, &omtu, &fd_list, NULL, &err)) |
| goto fail; |
| } else { |
| if (!bluez_media_transport1_call_acquire_sync (conn->data.conn, |
| NULL, &handle, &imtu, &omtu, &fd_list, NULL, &err)) |
| goto fail; |
| } |
| |
| fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (handle), &err); |
| if (fd < 0) |
| goto fail; |
| |
| g_variant_unref (handle); |
| g_object_unref (fd_list); |
| 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; |
| conn->data.is_acquired = TRUE; |
| |
| return TRUE; |
| |
| fail: |
| GST_ERROR ("Failed to %s transport stream: %s", use_try ? "try_acquire" : |
| "acquire", err && err->message ? err->message : "unknown error"); |
| |
| g_clear_error (&err); |
| if (handle) |
| g_variant_unref (handle); |
| |
| conn->data.is_acquired = FALSE; |
| return FALSE; |
| } |
| |
| static void |
| gst_avdtp_connection_transport_release (GstAvdtpConnection * conn) |
| { |
| GError *err = NULL; |
| |
| if (!bluez_media_transport1_call_release_sync (conn->data.conn, NULL, &err)) { |
| /* We don't care about errors if the transport was already marked stale */ |
| if (!conn->data.is_acquired) { |
| g_clear_error (&err); |
| return; |
| } |
| |
| GST_ERROR ("Failed to release transport stream: %s", err->message ? |
| err->message : "unknown error"); |
| g_clear_error (&err); |
| } |
| conn->data.is_acquired = FALSE; |
| } |
| |
| 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); |
| |
| g_clear_object (&conn->data.conn); |
| } |
| } |
| |
| 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); |
| } |
| |
| gboolean |
| gst_avdtp_connection_get_properties (GstAvdtpConnection * conn) |
| { |
| GVariant *var; |
| |
| conn->data.codec = bluez_media_transport1_get_codec (conn->data.conn); |
| |
| conn->data.uuid = bluez_media_transport1_dup_uuid (conn->data.conn); |
| |
| var = bluez_media_transport1_dup_configuration (conn->data.conn); |
| conn->data.config_size = g_variant_get_size (var); |
| conn->data.config = g_new0 (guint8, conn->data.config_size); |
| g_variant_store (var, conn->data.config); |
| g_variant_unref (var); |
| |
| return TRUE; |
| } |
| |
| 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_new_empty ("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, "channel-mode", value); |
| else |
| gst_structure_take_value (structure, "channel-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-method", value); |
| else |
| gst_structure_take_value (structure, "allocation-method", 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_new_empty ("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; |
| } |
| |
| static GstStructure * |
| gst_avdtp_util_parse_aac_raw (void *config) |
| { |
| GstStructure *structure; |
| GValue value = G_VALUE_INIT; |
| GValue value_str = G_VALUE_INIT; |
| GValue list = G_VALUE_INIT; |
| a2dp_aac_t aac_local = { 0 }; |
| a2dp_aac_t *aac = &aac_local; |
| |
| #if G_BYTE_ORDER == G_LITTLE_ENDIAN |
| uint8_t *raw = (uint8_t *) config; |
| aac->object_type = raw[0]; |
| aac->frequency = (raw[1] << 4) | ((raw[2] & 0xFF) >> 4); |
| aac->channels = (raw[2] >> 2) & 0x3; |
| aac->rfa = raw[2] & 0x3; |
| aac->vbr = (raw[3] >> 7) & 0x1; |
| aac->bitrate = (raw[4] << 16) | (raw[3] << 8) | raw[4]; |
| aac->bitrate &= ~0x800000; |
| #elif G_BYTE_ORDER == G_BIG_ENDIAN |
| *aac = *((a2dp_aac_t *) config); |
| #else |
| #error "Unknown byte order" |
| #endif |
| |
| GST_LOG ("aac objtype=%x freq=%x rfa=%x channels=%x vbr=%x bitrate=%x", |
| aac->object_type, aac->frequency, aac->rfa, aac->channels, aac->vbr, |
| aac->bitrate); |
| |
| structure = gst_structure_new_empty ("audio/mpeg"); |
| g_value_init (&value, G_TYPE_INT); |
| g_value_init (&value_str, G_TYPE_STRING); |
| |
| /* mpegversion */ |
| g_value_init (&list, GST_TYPE_LIST); |
| if (aac->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) { |
| g_value_set_int (&value, 2); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if ((aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) |
| || (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP) |
| || (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCALABLE)) { |
| 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, "mpegversion", &value); |
| else |
| gst_structure_set_value (structure, "mpegversion", &list); |
| |
| g_value_reset (&list); |
| |
| /* base-profile */ |
| if (aac->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC |
| || aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) { |
| g_value_set_string (&value_str, "lc"); |
| gst_value_list_prepend_value (&list, &value_str); |
| } |
| if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP) { |
| g_value_set_string (&value_str, "ltp"); |
| gst_value_list_prepend_value (&list, &value_str); |
| } |
| if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCALABLE) { |
| g_value_set_string (&value_str, "ssr"); |
| gst_value_list_prepend_value (&list, &value_str); |
| } |
| if (gst_value_list_get_size (&list) == 1) |
| gst_structure_set_value (structure, "base-profile", &value_str); |
| else |
| gst_structure_set_value (structure, "base-profile", &list); |
| |
| g_value_reset (&list); |
| |
| /* rate */ |
| g_value_init (&list, GST_TYPE_LIST); |
| if (aac->frequency & AAC_SAMPLING_FREQ_8000) { |
| g_value_set_int (&value, 8000); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_11025) { |
| g_value_set_int (&value, 11025); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_12000) { |
| g_value_set_int (&value, 12000); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_16000) { |
| g_value_set_int (&value, 16000); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_22050) { |
| g_value_set_int (&value, 22050); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_24000) { |
| g_value_set_int (&value, 24000); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_32000) { |
| g_value_set_int (&value, 32000); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_44100) { |
| g_value_set_int (&value, 44100); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_48000) { |
| g_value_set_int (&value, 48000); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_64000) { |
| g_value_set_int (&value, 64000); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_88200) { |
| g_value_set_int (&value, 88200); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->frequency & AAC_SAMPLING_FREQ_96000) { |
| g_value_set_int (&value, 96000); |
| 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_set_value (structure, "rate", &list); |
| |
| g_value_reset (&list); |
| |
| /* channels */ |
| g_value_init (&list, GST_TYPE_LIST); |
| if (aac->channels & AAC_CHANNELS_1) { |
| g_value_set_int (&value, 1); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (aac->channels & AAC_CHANNELS_2) { |
| g_value_set_int (&value, 2); |
| gst_value_list_prepend_value (&list, &value); |
| } |
| if (gst_value_list_get_size (&list) == 1) |
| gst_structure_set_value (structure, "channels", &value); |
| else |
| gst_structure_set_value (structure, "channels", &list); |
| |
| GST_LOG ("AAC caps: %" GST_PTR_FORMAT, structure); |
| |
| g_value_unset (&list); |
| g_value_unset (&value); |
| g_value_unset (&value_str); |
| |
| 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; |
| case A2DP_CODEC_MPEG24: |
| structure = gst_avdtp_util_parse_aac_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; |
| GIOStatus status; |
| GIOFlags flags; |
| int fd; |
| int priority; |
| |
| /* 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, NULL); |
| 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, NULL); |
| if (status != G_IO_STATUS_NORMAL) |
| GST_WARNING ("Error while setting server socket to block"); |
| |
| priority = 6; |
| if (setsockopt (fd, SOL_SOCKET, SO_PRIORITY, (const void *) &priority, |
| sizeof (priority)) < 0) |
| GST_WARNING ("Unable to set socket to low delay"); |
| |
| memset (data->buffer, 0, sizeof (data->buffer)); |
| |
| return TRUE; |
| } |