diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index 7b8580a893..833735a59d 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -2332,7 +2332,7 @@ "long-name": "Bluetooth AVDTP sink", "pad-templates": { "sink": { - "caps": "application/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: { (int)16000, (int)32000, (int)44100, (int)48000 }\n encoding-name: SBC\napplication/x-rtp:\n media: audio\n payload: 14\n clock-rate: 90000\napplication/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: 90000\n encoding-name: MPA\n", + "caps": "application/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: { (int)16000, (int)32000, (int)44100, (int)48000 }\n encoding-name: SBC\napplication/x-rtp:\n media: audio\n payload: 14\n clock-rate: 90000\napplication/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: 90000\n encoding-name: MPA\napplication/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: { (int)44100, (int)48000, (int)88200, (int)96000 }\n encoding-name: X-GST-LDAC\n", "direction": "sink", "presence": "always" } diff --git a/sys/bluez/a2dp-codecs.h b/sys/bluez/a2dp-codecs.h index 39e875d67b..825b92d5ec 100644 --- a/sys/bluez/a2dp-codecs.h +++ b/sys/bluez/a2dp-codecs.h @@ -111,6 +111,26 @@ #define AAC_CHANNELS_1 0x02 #define AAC_CHANNELS_2 0x01 +#define SONY_VENDOR_ID 0x0000012d +#define LDAC_CODEC_ID 0x00aa + +#define LDAC_SAMPLING_FREQ_44100 0x20 +#define LDAC_SAMPLING_FREQ_48000 0x10 +#define LDAC_SAMPLING_FREQ_88200 0x08 +#define LDAC_SAMPLING_FREQ_96000 0x04 + +#define LDAC_CHANNEL_MODE_MONO 0x04 +#define LDAC_CHANNEL_MODE_DUAL 0x02 +#define LDAC_CHANNEL_MODE_STEREO 0x01 + +#define A2DP_GET_VENDOR_ID(a) ( \ + (((uint32_t)(a).vendor_id[0]) << 0) | \ + (((uint32_t)(a).vendor_id[1]) << 8) | \ + (((uint32_t)(a).vendor_id[2]) << 16) | \ + (((uint32_t)(a).vendor_id[3]) << 24) \ + ) +#define A2DP_GET_CODEC_ID(a) ((a).codec_id[0] | (((uint16_t)(a).codec_id[1]) << 8)) + #if G_BYTE_ORDER == G_LITTLE_ENDIAN typedef struct { @@ -182,4 +202,10 @@ typedef struct { uint8_t codec_id[2]; } __attribute__ ((packed)) a2dp_vendor_codec_t; +typedef struct { + a2dp_vendor_codec_t info; + uint8_t frequency; + uint8_t channel_mode; +} __attribute__ ((packed)) a2dp_ldac_t; + #endif /* #define __GST_BLUEZ_A2DP_CODECS_H_INCLUDED__ */ diff --git a/sys/bluez/gstavdtpsink.c b/sys/bluez/gstavdtpsink.c index c6463d59b6..dc909aa83a 100644 --- a/sys/bluez/gstavdtpsink.c +++ b/sys/bluez/gstavdtpsink.c @@ -74,21 +74,22 @@ static GstStaticPadTemplate avdtp_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, 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 }, " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 16000, 32000, 44100, 48000 }, " "encoding-name = (string) \"SBC\"; " "application/x-rtp, " "media = (string) \"audio\", " - "payload = (int) " - GST_RTP_PAYLOAD_MPA_STRING ", " + "payload = (int) " GST_RTP_PAYLOAD_MPA_STRING ", " "clock-rate = (int) 90000; " "application/x-rtp, " "media = (string) \"audio\", " - "payload = (int) " - GST_RTP_PAYLOAD_DYNAMIC_STRING ", " - "clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\"")); + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 44100, 48000, 88200, 96000 }, " + "encoding-name = (string) \"X-GST-LDAC\"")); static gboolean gst_avdtp_sink_stop (GstBaseSink * basesink) diff --git a/sys/bluez/gstavdtputil.c b/sys/bluez/gstavdtputil.c index 3a11bd275b..5bf135fb6b 100644 --- a/sys/bluez/gstavdtputil.c +++ b/sys/bluez/gstavdtputil.c @@ -690,6 +690,112 @@ gst_avdtp_util_parse_aac_raw (void *config) return structure; } +static GstStructure * +gst_avdtp_util_parse_ldac_raw (void *config) +{ + /* We assume the vendor/codec ID have been verified already */ + a2dp_ldac_t *ldac = (a2dp_ldac_t *) config; + GstStructure *structure; + GValue value = G_VALUE_INIT; + GValue list = G_VALUE_INIT; + gboolean mono, stereo; + + structure = gst_structure_new_empty ("audio/x-ldac"); + + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&value, G_TYPE_INT); + + /* rate */ + if (ldac->frequency & LDAC_SAMPLING_FREQ_44100) { + g_value_set_int (&value, 44100); + gst_value_list_prepend_value (&list, &value); + } + if (ldac->frequency & LDAC_SAMPLING_FREQ_48000) { + g_value_set_int (&value, 48000); + gst_value_list_prepend_value (&list, &value); + } + if (ldac->frequency & LDAC_SAMPLING_FREQ_88200) { + g_value_set_int (&value, 88200); + gst_value_list_prepend_value (&list, &value); + } + if (ldac->frequency & LDAC_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_unset (&value); + g_value_reset (&list); + + /* channels */ + mono = FALSE; + stereo = FALSE; + if (ldac->channel_mode & LDAC_CHANNEL_MODE_MONO) + mono = TRUE; + if ((ldac->channel_mode & LDAC_CHANNEL_MODE_STEREO) || + (ldac->channel_mode & LDAC_CHANNEL_MODE_DUAL)) + 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_value_init (&value, G_TYPE_STRING); + + /* channel mode */ + if (ldac->channel_mode & LDAC_CHANNEL_MODE_MONO) { + g_value_set_static_string (&value, "mono"); + gst_value_list_prepend_value (&list, &value); + } + if (ldac->channel_mode & LDAC_CHANNEL_MODE_STEREO) { + g_value_set_static_string (&value, "stereo"); + gst_value_list_prepend_value (&list, &value); + } + if (ldac->channel_mode & LDAC_CHANNEL_MODE_DUAL) { + g_value_set_static_string (&value, "dual"); + 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_unset (&list); + + return structure; +} + +static GstStructure * +gst_avdtp_util_parse_vendor_raw (void *config) +{ + a2dp_vendor_codec_t *vendor = (a2dp_vendor_codec_t *) config; + + if (A2DP_GET_VENDOR_ID (*vendor) == SONY_VENDOR_ID && + A2DP_GET_CODEC_ID (*vendor) == LDAC_CODEC_ID) + return gst_avdtp_util_parse_ldac_raw (config); + else + return NULL; +} + GstCaps * gst_avdtp_connection_get_caps (GstAvdtpConnection * conn) { @@ -709,6 +815,9 @@ gst_avdtp_connection_get_caps (GstAvdtpConnection * conn) case A2DP_CODEC_MPEG24: structure = gst_avdtp_util_parse_aac_raw (conn->data.config); break; + case A2DP_CODEC_VENDOR: + structure = gst_avdtp_util_parse_vendor_raw (conn->data.config); + break; default: GST_ERROR ("Unsupported configuration"); return NULL;