Skip to content

Commit

Permalink
Adopt AAC encoder/decoder to A2DP spec v1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
arkq committed Jan 20, 2024
1 parent 70249cf commit 4495c7c
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 19 deletions.
1 change: 1 addition & 0 deletions .github/spellcheck-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ dbus
disp
dlopen
dmix
dmx
docutils
dpsnk
dpsrc
Expand Down
100 changes: 86 additions & 14 deletions src/a2dp-aac.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@
#include "shared/rt.h"

static unsigned int a2dp_aac_get_fdk_vbr_mode(
unsigned int channelmode, unsigned int bitrate) {
unsigned int channels, unsigned int bitrate) {
static const unsigned int modes[][5] = {
/* bitrate upper bounds for mono channel mode */
{ 32000, 40000, 56000, 72000, 112000 },
/* bitrate upper bounds for stereo channel mode */
{ 40000, 64000, 96000, 128000, 192000 },
};
const size_t ch = channelmode == MODE_1 ? 0 : 1;
const size_t ch = channels == 1 ? 0 : 1;
for (size_t i = 0; i < ARRAYSIZE(modes[ch]); i++)
if (bitrate <= modes[ch][i])
return i + 1;
Expand Down Expand Up @@ -80,8 +80,6 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) {
pthread_cleanup_push(PTHREAD_CLEANUP(aacEncClose), &handle);

unsigned int aot = AOT_NONE;
unsigned int channelmode = channels == 1 ? MODE_1 : MODE_2;

switch (configuration->object_type) {
case AAC_OBJECT_TYPE_MPEG2_LC:
#if AACENCODER_LIB_VERSION <= 0x03040C00 /* 3.4.12 */ || \
Expand All @@ -98,6 +96,31 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) {
case AAC_OBJECT_TYPE_MPEG4_SCA:
aot = AOT_AAC_SCAL;
break;
case AAC_OBJECT_TYPE_MPEG4_HE:
aot = AOT_SBR;
break;
case AAC_OBJECT_TYPE_MPEG4_HE2:
aot = AOT_PS;
break;
case AAC_OBJECT_TYPE_MPEG4_ELD2:
aot = AOT_ER_AAC_ELD;
break;
}

unsigned int channel_mode = MODE_1;
switch (configuration->channels) {
case AAC_CHANNELS_1:
channel_mode = MODE_1;
break;
case AAC_CHANNELS_2:
channel_mode = MODE_2;
break;
case AAC_CHANNELS_6:
channel_mode = MODE_1_2_2_1;
break;
case AAC_CHANNELS_8:
channel_mode = MODE_1_2_2_2_1;
break;
}

if ((err = aacEncoder_SetParam(handle, AACENC_AOT, aot)) != AACENC_OK) {
Expand All @@ -120,12 +143,12 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) {
error("Couldn't set sampling rate: %s", aacenc_strerror(err));
goto fail_init;
}
if ((err = aacEncoder_SetParam(handle, AACENC_CHANNELMODE, channelmode)) != AACENC_OK) {
if ((err = aacEncoder_SetParam(handle, AACENC_CHANNELMODE, channel_mode)) != AACENC_OK) {
error("Couldn't set channel mode: %s", aacenc_strerror(err));
goto fail_init;
}
if (configuration->vbr) {
const unsigned int mode = a2dp_aac_get_fdk_vbr_mode(channelmode, bitrate);
const unsigned int mode = a2dp_aac_get_fdk_vbr_mode(channels, bitrate);
if ((err = aacEncoder_SetParam(handle, AACENC_BITRATEMODE, mode)) != AACENC_OK) {
error("Couldn't set VBR bitrate mode %u: %s", mode, aacenc_strerror(err));
goto fail_init;
Expand Down Expand Up @@ -459,6 +482,8 @@ void *a2dp_aac_dec_thread(struct ba_transport_pcm *t_pcm) {
static const struct a2dp_channels a2dp_aac_channels[] = {
{ 1, AAC_CHANNELS_1 },
{ 2, AAC_CHANNELS_2 },
{ 6, AAC_CHANNELS_6 },
{ 8, AAC_CHANNELS_8 },
{ 0 },
};

Expand Down Expand Up @@ -509,7 +534,13 @@ static int a2dp_aac_configuration_select(
caps, sizeof(*caps)) != 0)
return -1;

if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_SCA)
if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_HE2)
caps->object_type = AAC_OBJECT_TYPE_MPEG4_HE2;
else if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_HE)
caps->object_type = AAC_OBJECT_TYPE_MPEG4_HE;
else if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_ELD2)
caps->object_type = AAC_OBJECT_TYPE_MPEG4_ELD2;
else if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_SCA)
caps->object_type = AAC_OBJECT_TYPE_MPEG4_SCA;
else if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_LTP)
caps->object_type = AAC_OBJECT_TYPE_MPEG4_LTP;
Expand Down Expand Up @@ -569,6 +600,9 @@ static int a2dp_aac_configuration_check(
case AAC_OBJECT_TYPE_MPEG4_LC:
case AAC_OBJECT_TYPE_MPEG4_LTP:
case AAC_OBJECT_TYPE_MPEG4_SCA:
case AAC_OBJECT_TYPE_MPEG4_HE:
case AAC_OBJECT_TYPE_MPEG4_HE2:
case AAC_OBJECT_TYPE_MPEG4_ELD2:
break;
default:
debug("AAC: Invalid object type: %#x", conf->object_type);
Expand Down Expand Up @@ -614,11 +648,26 @@ static int a2dp_aac_source_init(struct a2dp_codec *codec) {
[15] = { .module_id = ~FDK_NONE } };
aacEncGetLibInfo(info);

unsigned int caps = FDKlibInfo_getCapabilities(info, FDK_AACENC);
debug("FDK-AAC encoder capabilities: %#x", caps);
unsigned int caps_aac = FDKlibInfo_getCapabilities(info, FDK_AACENC);
unsigned int caps_sbr = FDKlibInfo_getCapabilities(info, FDK_SBRENC);
debug("FDK-AAC encoder capabilities: aac=%#x sbr=%#x", caps_aac, caps_sbr);

/* Check whether mandatory AAC profile is supported. */
if ((caps_aac & CAPF_AAC_LC) == 0) {
error("AAC: Low Complexity (AAC-LC) is not supported");
return errno = ENOTSUP, -1;
}

if (caps & CAPF_ER_AAC_SCAL)
if (caps_aac & CAPF_ER_AAC_SCAL)
codec->capabilities.aac.object_type |= AAC_OBJECT_TYPE_MPEG4_SCA;
if (caps_sbr & CAPF_SBR_HQ)
codec->capabilities.aac.object_type |= AAC_OBJECT_TYPE_MPEG4_HE;
if (caps_sbr & CAPF_SBR_PS_MPEG)
codec->capabilities.aac.object_type |= AAC_OBJECT_TYPE_MPEG4_HE2;
if (caps_aac & CAPF_ER_AAC_ELDV2)
codec->capabilities.aac.object_type |= AAC_OBJECT_TYPE_MPEG4_ELD2;
if (caps_aac & CAPF_AAC_UNIDRC)
codec->capabilities.aac.drc = 1;

if (config.a2dp.force_mono)
codec->capabilities.aac.channels = AAC_CHANNELS_1;
Expand Down Expand Up @@ -662,7 +711,9 @@ struct a2dp_codec a2dp_aac_source = {
AAC_SAMPLING_FREQ_96000)
.channels =
AAC_CHANNELS_1 |
AAC_CHANNELS_2,
AAC_CHANNELS_2 |
AAC_CHANNELS_6 |
AAC_CHANNELS_8,
.vbr = 1,
A2DP_AAC_INIT_BITRATE(320000)
},
Expand All @@ -682,11 +733,32 @@ static int a2dp_aac_sink_init(struct a2dp_codec *codec) {
[15] = { .module_id = ~FDK_NONE } };
aacDecoder_GetLibInfo(info);

unsigned int caps = FDKlibInfo_getCapabilities(info, FDK_AACDEC);
debug("FDK-AAC decoder capabilities: %#x", caps);
unsigned int caps_aac = FDKlibInfo_getCapabilities(info, FDK_AACDEC);
unsigned int caps_sbr = FDKlibInfo_getCapabilities(info, FDK_SBRDEC);
unsigned int caps_dmx = FDKlibInfo_getCapabilities(info, FDK_PCMDMX);
debug("FDK-AAC decoder capabilities: aac=%#x sbr=%#x dmx=%#x",
caps_aac, caps_sbr, caps_dmx);

/* Check whether mandatory AAC profile is supported. */
if ((caps_aac & CAPF_AAC_LC) == 0) {
error("AAC: Low Complexity (AAC-LC) is not supported");
return errno = ENOTSUP, -1;
}

if (caps & CAPF_ER_AAC_SCAL)
if (caps_aac & CAPF_ER_AAC_SCAL)
codec->capabilities.aac.object_type |= AAC_OBJECT_TYPE_MPEG4_SCA;
if (caps_sbr & CAPF_SBR_HQ)
codec->capabilities.aac.object_type |= AAC_OBJECT_TYPE_MPEG4_HE;
if (caps_sbr & CAPF_SBR_PS_MPEG)
codec->capabilities.aac.object_type |= AAC_OBJECT_TYPE_MPEG4_HE2;
if (caps_aac & CAPF_ER_AAC_ELDV2)
codec->capabilities.aac.object_type |= AAC_OBJECT_TYPE_MPEG4_ELD2;
if (caps_aac & CAPF_AAC_UNIDRC)
codec->capabilities.aac.drc = 1;
if (caps_dmx & CAPF_DMX_6_CH)
codec->capabilities.aac.channels |= AAC_CHANNELS_6;
if (caps_dmx & CAPF_DMX_8_CH)
codec->capabilities.aac.channels |= AAC_CHANNELS_8;

A2DP_AAC_SET_BITRATE(codec->capabilities.aac, config.aac_bitrate);

Expand Down
8 changes: 7 additions & 1 deletion src/a2dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,15 @@ const struct a2dp_channels *a2dp_channels_select(
const struct a2dp_channels *selected = NULL;

/* favor higher number of channels */
for (size_t i = 0; channels[i].value != 0; i++)
for (size_t i = 0; channels[i].value != 0; i++) {
if (channels[i].count > 2)
/* When auto-selecting channel mode, skip multi-channel modes. If
* desired, multi-channel mode can be selected manually by the user
* using the SelectCodec() D-Bus method. */
continue;
if (capabilities & channels[i].value)
selected = &channels[i];
}

return selected;
}
Expand Down
9 changes: 5 additions & 4 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* BlueALSA - main.c
* Copyright (c) 2016-2023 Arkadiusz Bokowy
* Copyright (c) 2016-2024 Arkadiusz Bokowy
*
* This file is a part of bluez-alsa.
*
Expand Down Expand Up @@ -232,8 +232,8 @@ int main(int argc, char **argv) {
#endif
" --xapl-resp-name=NAME\t\tset product name used by XAPL\n"
"\nAvailable BT profiles:\n"
" - a2dp-source\tAdvanced Audio Source (v1.3)\n"
" - a2dp-sink\tAdvanced Audio Sink (v1.3)\n"
" - a2dp-source\tAdvanced Audio Source (v1.4)\n"
" - a2dp-sink\tAdvanced Audio Sink (v1.4)\n"
#if ENABLE_OFONO
" - hfp-ofono\tHands-Free AG/HF handled by oFono\n"
#endif
Expand Down Expand Up @@ -584,7 +584,8 @@ int main(int argc, char **argv) {
a2dp_sbc_sink.enabled = true;
config.hfp.codecs.cvsd = true;

a2dp_codecs_init();
if (a2dp_codecs_init() == -1)
return EXIT_FAILURE;

const char *storage_base_dir = BLUEALSA_STORAGE_DIR;
#if ENABLE_SYSTEMD
Expand Down

0 comments on commit 4495c7c

Please sign in to comment.