mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-26 17:18:15 +00:00
bluez: support aac in avdtpsrc
Signed-off-by: Bernhard Miller <bernhard.miller@streamunlimited.com>
This commit is contained in:
parent
e39239677e
commit
597e3cc98d
3 changed files with 232 additions and 2 deletions
|
@ -90,6 +90,27 @@
|
|||
#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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue