bluez: support aac in avdtpsrc
authorBernhard Miller <bernhard.miller@streamunlimited.com>
Wed, 28 Aug 2013 12:26:04 +0000 (14:26 +0200)
committerSebastian Dröge <slomo@circular-chaos.org>
Thu, 29 Aug 2013 08:17:07 +0000 (10:17 +0200)
Signed-off-by: Bernhard Miller <bernhard.miller@streamunlimited.com>
sys/bluez/a2dp-codecs.h
sys/bluez/gstavdtpsrc.c
sys/bluez/gstavdtputil.c

index b836872..39e875d 100644 (file)
 #define MPEG_BIT_RATE_32000            0x0002
 #define MPEG_BIT_RATE_FREE             0x0001
 
+#define AAC_OBJECT_TYPE_MPEG2_AAC_LC           0x80
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LC           0x40
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LTP          0x20
+#define AAC_OBJECT_TYPE_MPEG4_AAC_SCALABLE     0x10
+
+#define AAC_SAMPLING_FREQ_8000         0x0800
+#define AAC_SAMPLING_FREQ_11025                0x0400
+#define AAC_SAMPLING_FREQ_12000                0x0200
+#define AAC_SAMPLING_FREQ_16000                0x0100
+#define AAC_SAMPLING_FREQ_22050                0x0080
+#define AAC_SAMPLING_FREQ_24000                0x0040
+#define AAC_SAMPLING_FREQ_32000                0x0020
+#define AAC_SAMPLING_FREQ_44100                0x0010
+#define AAC_SAMPLING_FREQ_48000                0x0008
+#define AAC_SAMPLING_FREQ_64000                0x0004
+#define AAC_SAMPLING_FREQ_88200                0x0002
+#define AAC_SAMPLING_FREQ_96000                0x0001
+
+#define AAC_CHANNELS_1         0x02
+#define AAC_CHANNELS_2         0x01
+
 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
 
 typedef struct {
@@ -112,6 +133,15 @@ typedef struct {
        uint16_t bitrate;
 } __attribute__ ((packed)) a2dp_mpeg_t;
 
+typedef struct {
+       uint8_t object_type;
+       uint16_t frequency;
+       uint8_t rfa;
+       uint8_t channels;
+       uint8_t vbr;
+       uint32_t bitrate;
+} __attribute__ ((packed)) a2dp_aac_t;
+
 #elif G_BYTE_ORDER == G_BIG_ENDIAN
 
 typedef struct {
@@ -134,6 +164,15 @@ typedef struct {
        uint16_t bitrate;
 } __attribute__ ((packed)) a2dp_mpeg_t;
 
+typedef struct {
+       uint8_t object_type;
+       uint16_t frequency:12;
+       uint8_t channels:2;
+       uint8_t rfa:2;
+       uint8_t vbr:1;
+       uint32_t bitrate:23;
+} __attribute__ ((packed)) a2dp_aac_t;
+
 #else
 #error "Unknown byte order"
 #endif
index f130151..311c407 100644 (file)
@@ -52,7 +52,14 @@ static GstStaticPadTemplate gst_avdtp_src_template =
         "payload = (int) "
         GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
         "clock-rate = (int) { 16000, 32000, "
-        "44100, 48000 }, " "encoding-name = (string) \"SBC\"; "));
+        "44100, 48000 }, " "encoding-name = (string) \"SBC\"; "
+        "application/x-rtp, "
+        "media = (string) \"audio\","
+        "payload = (int) "
+        GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+        "clock-rate = (int) { 8000, 11025, 12000, 16000, "
+        "22050, 2400, 32000, 44100, 48000, 64000, 88200, 96000 }, "
+        "encoding-name = (string) \"MP4A-LATM\"; "));
 
 static void gst_avdtp_src_finalize (GObject * object);
 static void gst_avdtp_src_get_property (GObject * object, guint prop_id,
@@ -184,7 +191,38 @@ gst_avdtp_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
           "payload", GST_TYPE_INT_RANGE, 96, 127,
           "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");
+      caps = gst_caps_new_simple ("application/x-rtp",
+          "media", G_TYPE_STRING, "audio",
+          "payload", GST_TYPE_INT_RANGE, 96, 127,
+          "encoding-name", G_TYPE_STRING, "MP4A-LATM", NULL);
+
+      value = gst_structure_get_value (structure, "mpegversion");
+      if (!value || !G_VALUE_HOLDS_INT (value)) {
+        GST_ERROR_OBJECT (avdtpsrc, "Failed to get mpegversion");
+        goto fail;
+      }
+      gst_caps_set_simple (caps, "mpegversion", G_TYPE_INT,
+          g_value_get_int (value), NULL);
+
+      value = gst_structure_get_value (structure, "channels");
+      if (!value || !G_VALUE_HOLDS_INT (value)) {
+        GST_ERROR_OBJECT (avdtpsrc, "Failed to get channels");
+        goto fail;
+      }
+      gst_caps_set_simple (caps, "channels", G_TYPE_INT,
+          g_value_get_int (value), NULL);
+
+      value = gst_structure_get_value (structure, "base-profile");
+      if (!value || !G_VALUE_HOLDS_STRING (value)) {
+        GST_ERROR_OBJECT (avdtpsrc, "Failed to get base-profile");
+        goto fail;
+      }
+      gst_caps_set_simple (caps, "base-profile", G_TYPE_STRING,
+          g_value_get_string (value), NULL);
+
+    } else {
+      GST_ERROR_OBJECT (avdtpsrc,
+          "Only SBC and MPEG-2/4 are supported at the moment");
     }
 
     value = gst_structure_get_value (structure, "rate");
index 61b5b17..07afd45 100644 (file)
@@ -604,6 +604,156 @@ gst_avdtp_util_parse_mpeg_raw (void *config)
   return structure;
 }
 
+static GstStructure *
+gst_avdtp_util_parse_aac_raw (void *config)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+  uint8_t *raw = (uint8_t *) config;
+  a2dp_aac_t aac_local = { 0 };
+  a2dp_aac_t *aac = &aac_local;
+  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);
+
+  GstStructure *structure;
+  GValue value = G_VALUE_INIT;
+  GValue value_str = G_VALUE_INIT;
+  GValue listVersion = G_VALUE_INIT;
+  GValue listProfile = G_VALUE_INIT;
+  GValue listRate = G_VALUE_INIT;
+  GValue listChannels = G_VALUE_INIT;
+
+  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 (&listVersion, GST_TYPE_LIST);
+  if (aac->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) {
+    g_value_set_int (&value, 2);
+    gst_value_list_prepend_value (&listVersion, &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 (&listVersion, &value);
+  }
+  if (gst_value_list_get_size (&listVersion) == 1)
+    gst_structure_set_value (structure, "mpegversion", &value);
+  else
+    gst_structure_set_value (structure, "mpegversion", &listVersion);
+
+
+  /* base-profile */
+  g_value_init (&listProfile, GST_TYPE_LIST);
+  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 (&listProfile, &value_str);
+  }
+  if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP) {
+    g_value_set_string (&value_str, "ltp");
+    gst_value_list_prepend_value (&listProfile, &value_str);
+  }
+  if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCALABLE) {
+    g_value_set_string (&value_str, "ssr");
+    gst_value_list_prepend_value (&listProfile, &value_str);
+  }
+  if (gst_value_list_get_size (&listProfile) == 1)
+    gst_structure_set_value (structure, "base-profile", &value_str);
+  else
+    gst_structure_set_value (structure, "base-profile", &listProfile);
+
+
+  /* rate */
+  g_value_init (&listRate, GST_TYPE_LIST);
+  if (aac->frequency & AAC_SAMPLING_FREQ_8000) {
+    g_value_set_int (&value, 8000);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_11025) {
+    g_value_set_int (&value, 11025);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_12000) {
+    g_value_set_int (&value, 12000);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_16000) {
+    g_value_set_int (&value, 16000);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_22050) {
+    g_value_set_int (&value, 22050);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_24000) {
+    g_value_set_int (&value, 24000);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_32000) {
+    g_value_set_int (&value, 32000);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_44100) {
+    g_value_set_int (&value, 44100);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_48000) {
+    g_value_set_int (&value, 48000);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_64000) {
+    g_value_set_int (&value, 64000);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_88200) {
+    g_value_set_int (&value, 88200);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (aac->frequency & AAC_SAMPLING_FREQ_96000) {
+    g_value_set_int (&value, 96000);
+    gst_value_list_prepend_value (&listRate, &value);
+  }
+  if (gst_value_list_get_size (&listRate) == 1)
+    gst_structure_set_value (structure, "rate", &value);
+  else
+    gst_structure_set_value (structure, "rate", &listRate);
+
+  /* channels */
+  g_value_init (&listChannels, GST_TYPE_LIST);
+  if (aac->channels & AAC_CHANNELS_1) {
+    g_value_set_int (&value, 1);
+    gst_value_list_prepend_value (&listChannels, &value);
+  }
+  if (aac->channels & AAC_CHANNELS_2) {
+    g_value_set_int (&value, 2);
+    gst_value_list_prepend_value (&listChannels, &value);
+  }
+  if (gst_value_list_get_size (&listChannels) == 1)
+    gst_structure_set_value (structure, "channels", &value);
+  else
+    gst_structure_set_value (structure, "channels", &listChannels);
+
+  GST_LOG ("AAC caps: %" GST_PTR_FORMAT, structure);
+
+  return structure;
+}
+
 GstCaps *
 gst_avdtp_connection_get_caps (GstAvdtpConnection * conn)
 {
@@ -620,6 +770,9 @@ gst_avdtp_connection_get_caps (GstAvdtpConnection * conn)
     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;