Hi, On Sat, Dec 29, 2018 at 12:49 PM Huang-Huang Bao <eh5@xxxxxxxx> wrote: > > Optional (build with --disable-bluez5-aac-codec) > > AAC User Configurations: > KEY VALUE DESC DEFAULT > aac_bitrate_mode [1, 5] Variable Bitrate (VBR) (encoder) 5 > 0 Constant Bitrate (CBR) (encoder) > > aac_fmt s16 16-bit signed LE (encoder) auto > s32 32-bit signed LE (encoder) > auto > > aac_afterburner <on/off> FDK-AAC afterburner feature (encoder) off > --- > configure.ac | 14 +- > src/Makefile.am | 8 + > src/modules/bluetooth/a2dp/a2dp-api.h | 10 +- > src/modules/bluetooth/a2dp/a2dp_aac.c | 762 +++++++++++++++++++++++++ > src/modules/bluetooth/a2dp/a2dp_util.c | 33 ++ > 5 files changed, 825 insertions(+), 2 deletions(-) > create mode 100644 src/modules/bluetooth/a2dp/a2dp_aac.c > > diff --git a/configure.ac b/configure.ac > index 2512d3c95..0f9d7fb6c 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -1061,7 +1061,7 @@ PA_MACHINE_ID_FALLBACK="${localstatedir}/lib/dbus/machine-id" > AX_DEFINE_DIR(PA_MACHINE_ID_FALLBACK, PA_MACHINE_ID_FALLBACK, > [Fallback machine-id file]) > > -#### BlueZ support (optional, dependent on D-Bus and SBC) #### > +#### BlueZ support (optional, dependent on D-Bus and SBC and FDK-AAC) #### > > AC_ARG_ENABLE([bluez5], > AS_HELP_STRING([--disable-bluez5],[Disable optional BlueZ 5 support])) > @@ -1083,6 +1083,16 @@ AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], HAVE_BLUEZ=1, HAVE_BLUEZ=0) > AC_SUBST(HAVE_BLUEZ) > AM_CONDITIONAL([HAVE_BLUEZ], [test "x$HAVE_BLUEZ" = x1]) > > +## FDK-AAC ## > +AC_ARG_ENABLE([bluez5-aac-codec], > + AS_HELP_STRING([--disable-bluez5-aac-codec],[Disable optional A2DP AAC codec support (Bluez 5)])) > +AS_IF([test "x$HAVE_BLUEZ_5" = "x1" && test "x$enable_bluez5_aac_codec" != "xno"], > + [PKG_CHECK_MODULES(FDK_AAC, [ fdk-aac >= 0.1.5 ], HAVE_FDK_AAC=1, HAVE_FDK_AAC=0)], > + HAVE_FDK_AAC=0) > +AC_SUBST(HAVE_FDK_AAC) > +AM_CONDITIONAL([HAVE_FDK_AAC], [test "x$HAVE_FDK_AAC" = x1]) > +AS_IF([test "x$HAVE_FDK_AAC" = "x1"], AC_DEFINE([HAVE_FDK_AAC], 1, [Bluez 5 A2DP AAC codec enabled])) > + > ## Bluetooth Headset profiles backend ## > > AC_ARG_ENABLE([bluez5-ofono-headset], > @@ -1587,6 +1597,7 @@ AS_IF([test "x$HAVE_SYSTEMD_DAEMON" = "x1"], ENABLE_SYSTEMD_DAEMON=yes, ENABLE_S > AS_IF([test "x$HAVE_SYSTEMD_LOGIN" = "x1"], ENABLE_SYSTEMD_LOGIN=yes, ENABLE_SYSTEMD_LOGIN=no) > AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE_SYSTEMD_JOURNAL=no) > AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], ENABLE_BLUEZ_5=yes, ENABLE_BLUEZ_5=no) > +AS_IF([test "x$HAVE_FDK_AAC" = "x1"], ENABLE_BLUEZ_5_AAC_CODEC=yes, ENABLE_BLUEZ_5_AAC_CODEC=no) > AS_IF([test "x$HAVE_BLUEZ_5_OFONO_HEADSET" = "x1"], ENABLE_BLUEZ_5_OFONO_HEADSET=yes, ENABLE_BLUEZ_5_OFONO_HEADSET=no) > AS_IF([test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = "x1"], ENABLE_BLUEZ_5_NATIVE_HEADSET=yes, ENABLE_BLUEZ_5_NATIVE_HEADSET=no) > AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], ENABLE_HAL_COMPAT=yes, ENABLE_HAL_COMPAT=no) > @@ -1645,6 +1656,7 @@ echo " > Enable LIRC: ${ENABLE_LIRC} > Enable D-Bus: ${ENABLE_DBUS} > Enable BlueZ 5: ${ENABLE_BLUEZ_5} > + Enable A2DP AAC codec: ${ENABLE_BLUEZ_5_AAC_CODEC} > Enable ofono headsets: ${ENABLE_BLUEZ_5_OFONO_HEADSET} > Enable native headsets: ${ENABLE_BLUEZ_5_NATIVE_HEADSET} > Enable udev: ${ENABLE_UDEV} > diff --git a/src/Makefile.am b/src/Makefile.am > index 521b9b684..c44a65f05 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -2128,6 +2128,10 @@ libbluez5_util_la_SOURCES = \ > modules/bluetooth/a2dp/a2dp-api.h \ > modules/bluetooth/a2dp/a2dp-codecs.h \ > modules/bluetooth/a2dp/rtp.h > +if HAVE_FDK_AAC > +libbluez5_util_la_SOURCES += \ > + modules/bluetooth/a2dp/a2dp_aac.c > +endif > if HAVE_BLUEZ_5_OFONO_HEADSET > libbluez5_util_la_SOURCES += \ > modules/bluetooth/backend-ofono.c > @@ -2140,6 +2144,10 @@ endif > libbluez5_util_la_LDFLAGS = -avoid-version > libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(SBC_LIBS) > libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) > +if HAVE_FDK_AAC > +libbluez5_util_la_LIBADD += $(FDK_AAC_LIBS) > +libbluez5_util_la_CFLAGS += $(FDK_AAC_CFLAGS) > +endif > > module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c > module_bluez5_discover_la_LDFLAGS = $(MODULE_LDFLAGS) > diff --git a/src/modules/bluetooth/a2dp/a2dp-api.h b/src/modules/bluetooth/a2dp/a2dp-api.h > index 1492d29fd..1e601a9c5 100644 > --- a/src/modules/bluetooth/a2dp/a2dp-api.h > +++ b/src/modules/bluetooth/a2dp/a2dp-api.h > @@ -37,7 +37,9 @@ typedef struct pa_a2dp_codec pa_a2dp_codec_t; > typedef struct pa_a2dp_config pa_a2dp_config_t; > > extern const pa_a2dp_codec_t pa_a2dp_sbc; > - > +#ifdef HAVE_FDK_AAC > +extern const pa_a2dp_codec_t pa_a2dp_aac; > +#endif > > /* Run from <pa_a2dp_sink_t>.encode */ > > @@ -50,9 +52,15 @@ typedef void (*pa_a2dp_source_read_buf_free_cb_t)(const void **read_buf, void *d > typedef enum pa_a2dp_codec_index { > PA_A2DP_SINK_MIN, > PA_A2DP_SINK_SBC, > +#ifdef HAVE_FDK_AAC > + PA_A2DP_SINK_AAC, > +#endif > PA_A2DP_SINK_MAX, > PA_A2DP_SOURCE_MIN = PA_A2DP_SINK_MAX, > PA_A2DP_SOURCE_SBC, > +#ifdef HAVE_FDK_AAC > + PA_A2DP_SOURCE_AAC, > +#endif > PA_A2DP_SOURCE_MAX, > PA_A2DP_CODEC_INDEX_UNAVAILABLE > } pa_a2dp_codec_index_t; > diff --git a/src/modules/bluetooth/a2dp/a2dp_aac.c b/src/modules/bluetooth/a2dp/a2dp_aac.c > new file mode 100644 > index 000000000..a1d7bf1d2 > --- /dev/null > +++ b/src/modules/bluetooth/a2dp/a2dp_aac.c > @@ -0,0 +1,762 @@ > +/*** > + This file is part of PulseAudio. > + > + Copyright 2018 Huang-Huang Bao > + > + PulseAudio 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. > + > + PulseAudio 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 > + General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. > +***/ > + > +#include <arpa/inet.h> > +#include <string.h> > + > +#include <fdk-aac/aacenc_lib.h> > +#include <fdk-aac/aacdecoder_lib.h> > + > +#include <pulse/xmalloc.h> > + > +#include "a2dp-api.h" > + > +#define streq(a, b) (!strcmp((a),(b))) > + > +#define AAC_DEFAULT_BITRATE 320000u > + > +typedef struct aac_info { > + pa_a2dp_source_read_cb_t read_pcm; > + pa_a2dp_source_read_buf_free_cb_t read_buf_free; > + > + bool is_a2dp_sink; > + > + uint16_t seq_num; > + > + HANDLE_AACDECODER aacdecoder_handle; > + bool aacdecoder_handle_opened; > + > + HANDLE_AACENCODER aacenc_handle; > + bool aacenc_handle_opened; > + AACENC_InfoStruct aacenc_info; > + > + uint32_t bitrate; > + size_t mtu; > + > + /* Constant Bitrate: 0 > + * Variable Bitrate: 1-5 (Only effects when both bluetooth devices have vbr support ) */ > + int aac_enc_bitrate_mode; > + uint32_t aac_afterburner; > + pa_sample_format_t force_pa_fmt; > + > + pa_sample_spec sample_spec; > + > + size_t read_block_size; > + size_t write_block_size; > + > +} aac_info_t; > + > +static bool pa_aac_decoder_load() { > + /* AAC libs dynamically linked */ > + return true; > +} > + > +static bool pa_aac_encoder_load() { > + /* AAC libs dynamically linked */ > + return true; > +} > + > +static bool > +pa_aac_decoder_init(void **codec_data) { > + aac_info_t *info = pa_xmalloc0(sizeof(aac_info_t)); > + *codec_data = info; > + info->is_a2dp_sink = true; > + return true; > +} > + > +static bool > +pa_aac_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data) { > + aac_info_t *info = pa_xmalloc0(sizeof(aac_info_t)); > + *codec_data = info; > + info->is_a2dp_sink = false; > + info->read_pcm = read_cb; > + info->read_buf_free = free_cb; > + info->aacenc_handle_opened = false; > + info->aac_enc_bitrate_mode = 5; > + info->aac_afterburner = false; > + info->force_pa_fmt = PA_SAMPLE_INVALID; > + return true; > +} > + > +/* KEY VALUE DESC DEFAULT > + * aac_bitrate_mode [1, 5] Variable Bitrate (VBR) (encoder) 5 > + * 0 Constant Bitrate (CBR) (encoder) > + * > + * aac_fmt s16 16-bit signed LE (encoder) auto > + * s32 32-bit signed LE (encoder) > + * auto > + * > + * aac_afterburner <on/off> FDK-AAC afterburner feature (encoder) off > + */ > +static int pa_aac_update_user_config(pa_proplist *user_config, void **codec_data) { > + aac_info_t *i = *codec_data; > + const char *aac_bitrate_mode_str, *aac_fmt_str, *aac_afterburner_str; > + int aac_bitrate_mode = 0, ret = 0; > + pa_assert(i); > + > + aac_bitrate_mode_str = pa_proplist_gets(user_config, "aac_bitrate_mode"); > + aac_fmt_str = pa_proplist_gets(user_config, "aac_fmt"); > + aac_afterburner_str = pa_proplist_gets(user_config, "aac_afterburner"); > + > + if (aac_bitrate_mode_str) { > + aac_bitrate_mode = atoi(aac_bitrate_mode_str); > + > + if (aac_bitrate_mode >= 0 && aac_bitrate_mode <= 5) { > + i->aac_enc_bitrate_mode = aac_bitrate_mode; > + ret++; > + } else > + pa_log ("aac_bitrate_mode parameter must in range [0, 5] (found %s)", aac_bitrate_mode_str); > + } > + > + if (aac_fmt_str) { > + if (streq(aac_fmt_str, "s16")) { > + i->force_pa_fmt = PA_SAMPLE_S16LE; > + ret++; > + } else if (streq(aac_fmt_str, "s32")) { > + i->force_pa_fmt = PA_SAMPLE_S32LE; > + ret++; > + } else if (streq(aac_fmt_str, "auto")) { > + i->force_pa_fmt = PA_SAMPLE_INVALID; > + ret++; > + } else > + pa_log ("aac_fmt parameter must be either s16, s32 or auto (found %s)", aac_fmt_str); > + } > + > + if (aac_afterburner_str) { > + if (streq("on", aac_afterburner_str)) { > + i->aac_afterburner = 1; > + ret++; > + } else if (streq("off", aac_afterburner_str)) { > + i->aac_afterburner = 0; > + ret++; > + } else > + pa_log ("aac_afterburner parameter must be either on or off (found %s)", aac_afterburner_str); > + } > + > + return ret; > +} > + > +static size_t > +pa_aac_decode(const void *read_buf, size_t read_buf_size, void *write_buf, size_t write_buf_size, size_t *_decoded, > + uint32_t *timestamp, void **codec_data) { > + const struct rtp_header *header; > + const UCHAR *p; > + INT_PCM *d; > + UINT to_decode, pkt_size; > + UINT total_written = 0; > + aac_info_t *aac_info = *codec_data; > + pa_assert(aac_info); > + > + header = read_buf; > + *timestamp = ntohl(header->timestamp); > + > + p = (UCHAR *) read_buf + sizeof(*header); > + pkt_size = to_decode = (UINT) (read_buf_size - sizeof(*header)); > + > + d = write_buf; > + > + *_decoded = 0; > + while (PA_LIKELY(to_decode > 0)) { > + CStreamInfo* info; > + > + AAC_DECODER_ERROR aac_err = aacDecoder_Fill(aac_info->aacdecoder_handle, > + (UCHAR **) &p, &pkt_size, &to_decode); > + > + if (PA_UNLIKELY(aac_err != AAC_DEC_OK)) { > + pa_log_error("aacDecoder_Fill() error 0x%x", aac_err); > + *_decoded = 0; > + return 0; > + } > + > + while (true) { > + INT written; > + aac_err = aacDecoder_DecodeFrame(aac_info->aacdecoder_handle, d, (INT) write_buf_size, 0); > + if (PA_UNLIKELY(aac_err == AAC_DEC_NOT_ENOUGH_BITS)) > + break; > + if (PA_UNLIKELY(aac_err != AAC_DEC_OK)){ > + pa_log_error("aacDecoder_DecodeFrame() error 0x%x", aac_err); > + break; > + } > + > + info = aacDecoder_GetStreamInfo(aac_info->aacdecoder_handle); > + if(PA_UNLIKELY(!info || info->sampleRate <= 0)) { > + pa_log_error("Invalid stream info"); > + break; > + } > + > + written = info->frameSize * info->numChannels * 2; > + d += written; > + total_written += (UINT) written; > + } > + } > + > + *_decoded = pkt_size; > + > + return total_written; > +} > + > +static size_t > +pa_aac_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *_encoded, void *read_cb_data, > + void **codec_data) { > + struct rtp_header *header; > + size_t nbytes; > + uint8_t *d; > + const uint8_t *p; > + int to_write; > + unsigned frame_count; > + aac_info_t *aac_info = *codec_data; > + const size_t sample_size = pa_sample_size(&aac_info->sample_spec), > + frame_size = pa_frame_size(&aac_info->sample_spec); > + void *in_bufs[1] = {NULL}; > + void *out_bufs[1] = {NULL}; > + int in_bufferIdentifiers[1] = {IN_AUDIO_DATA}; > + int out_bufferIdentifiers[1] = {OUT_BITSTREAM_DATA}; > + int in_bufSizes[1] = {(int) (aac_info->aacenc_info.frameLength * frame_size)}; > + int out_bufSizes[1]; > + int bufElSizes[1] = {(int) sample_size}; > + AACENC_BufDesc in_bufDesc = { > + .numBufs = 1, > + .bufs = in_bufs, > + .bufferIdentifiers = in_bufferIdentifiers, > + .bufSizes = in_bufSizes, > + .bufElSizes = bufElSizes > + }; > + AACENC_BufDesc out_bufDesc = { > + .numBufs = 1, > + .bufs = out_bufs, > + .bufferIdentifiers = out_bufferIdentifiers, > + .bufSizes = out_bufSizes, > + .bufElSizes = bufElSizes > + }; > + AACENC_InArgs in_args = { > + .numAncBytes = 0, > + .numInSamples = aac_info->aacenc_info.frameLength * aac_info->aacenc_info.inputChannels > + }; > + AACENC_OutArgs out_args; > + > + pa_assert(aac_info); > + > + header = write_buf; > + > + frame_count = 0; > + > + aac_info->read_pcm((const void **) &p, (size_t) in_bufSizes[0], read_cb_data); > + > + in_bufDesc.bufs[0] = (void *) p; > + > + d = (uint8_t *) write_buf + sizeof(*header); > + to_write = (int) (write_buf_size - sizeof(*header)); > + out_bufDesc.bufs[0] = d; > + out_bufSizes[0] = to_write; > + > + > + *_encoded = 0; > + > + while (PA_UNLIKELY(in_args.numInSamples && to_write > 0)) { > + size_t encoded; > + > + AACENC_ERROR aac_err = aacEncEncode(aac_info->aacenc_handle, &in_bufDesc, &out_bufDesc, &in_args, &out_args); > + > + if (PA_UNLIKELY(aac_err != AACENC_OK)) { > + pa_log_error("AAC encoding error, 0x%x", aac_err); > + aac_info->read_buf_free((const void **) &p, read_cb_data); > + *_encoded = 0; > + return 0; > + } > + > + encoded = out_args.numInSamples * sample_size; > + > + in_args.numInSamples -= out_args.numInSamples; > + p += encoded; > + *_encoded += encoded; > + > + to_write -= out_args.numOutBytes; > + d += out_args.numOutBytes; > + > + frame_count++; > + } > + > + aac_info->read_buf_free((const void **) &p, read_cb_data); > + > + memset(write_buf, 0, sizeof(*header)); > + header->v = 2; > + header->pt = 1; > + header->sequence_number = htons(aac_info->seq_num++); > + header->timestamp = htonl(timestamp); > + header->ssrc = htonl(1); > + > + nbytes = d - (uint8_t *) write_buf; > + > + return nbytes; > +} > + > +static void > +pa_aac_config_transport(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, > + pa_sample_spec *sample_spec, void **codec_data) { > + AACENC_ERROR aac_err; > + aac_info_t *aac_info = *codec_data; > + a2dp_aac_t *config = (a2dp_aac_t *) configuration; > + UINT aot, sample_rate, channels; > + pa_sample_format_t fmt; > + > + pa_assert(aac_info); > + pa_assert_se(configuration_size == sizeof(*config)); > + > + aac_info->bitrate = PA_MIN(AAC_DEFAULT_BITRATE, ((uint32_t) AAC_GET_BITRATE(*config))); > + > + > + if(aac_info->is_a2dp_sink) > + sample_spec->format = PA_SAMPLE_S16LE; > + else{ > + if (aac_info->force_pa_fmt == PA_SAMPLE_INVALID) > + fmt = default_sample_spec.format; > + else > + fmt = aac_info->force_pa_fmt; > + > + switch (fmt) { > + case PA_SAMPLE_S24LE: > + case PA_SAMPLE_S24BE: > + case PA_SAMPLE_S24_32LE: > + case PA_SAMPLE_S24_32BE: > + case PA_SAMPLE_S32LE: > + case PA_SAMPLE_S32BE: > + case PA_SAMPLE_FLOAT32LE: > + case PA_SAMPLE_FLOAT32BE: > + sample_spec->format = PA_SAMPLE_S32LE; > + break; > + default: > + sample_spec->format = PA_SAMPLE_S16LE; > + } > + } > + > + switch (config->object_type) { > + case AAC_OBJECT_TYPE_MPEG2_AAC_LC: > + aot = AOT_AAC_LC; > + break; > + case AAC_OBJECT_TYPE_MPEG4_AAC_LC: > + aot = AOT_AAC_LC; > + break; > + case AAC_OBJECT_TYPE_MPEG4_AAC_LTP: > + aot = AOT_AAC_LTP; > + break; > + case AAC_OBJECT_TYPE_MPEG4_AAC_SCA: > + aot = AOT_AAC_SCAL; > + break; > + default: > + pa_log_error("Invalid AAC object type"); > + pa_assert_not_reached(); > + } > + > + switch (AAC_GET_FREQUENCY(*config)) { > + case AAC_SAMPLING_FREQ_8000: > + sample_rate = 8000; > + sample_spec->rate = 8000; > + break; > + case AAC_SAMPLING_FREQ_11025: > + sample_rate = 11025; > + sample_spec->rate = 11025; > + break; > + case AAC_SAMPLING_FREQ_12000: > + sample_rate = 12000; > + sample_spec->rate = 12000; > + break; > + case AAC_SAMPLING_FREQ_16000: > + sample_rate = 16000; > + sample_spec->rate = 16000; > + break; > + case AAC_SAMPLING_FREQ_22050: > + sample_rate = 22050; > + sample_spec->rate = 22050; > + break; > + case AAC_SAMPLING_FREQ_24000: > + sample_rate = 24000; > + sample_spec->rate = 24000; > + break; > + case AAC_SAMPLING_FREQ_32000: > + sample_rate = 32000; > + sample_spec->rate = 32000; > + break; > + case AAC_SAMPLING_FREQ_44100: > + sample_rate = 44100; > + sample_spec->rate = 44100; > + break; > + case AAC_SAMPLING_FREQ_48000: > + sample_rate = 48000; > + sample_spec->rate = 48000; > + break; > + case AAC_SAMPLING_FREQ_64000: > + sample_rate = 64000; > + sample_spec->rate = 64000; > + break; > + case AAC_SAMPLING_FREQ_88200: > + sample_rate = 88200; > + sample_spec->rate = 88200; > + break; > + case AAC_SAMPLING_FREQ_96000: > + sample_rate = 96000; > + sample_spec->rate = 96000; > + break; > + default: > + pa_log_error("Invalid AAC frequency"); > + pa_assert_not_reached(); > + } > + > + switch (config->channels) { > + case AAC_CHANNELS_1: > + channels = MODE_1; > + sample_spec->channels = 1; > + break; > + case AAC_CHANNELS_2: > + channels = MODE_2; > + sample_spec->channels = 2; > + break; > + default: > + pa_log_error("Invalid AAC channel mode"); > + pa_assert_not_reached(); > + } > + > + aac_info->sample_spec = *sample_spec; > + > + /* AAC SINK */ > + if (aac_info->is_a2dp_sink) { > + if (!aac_info->aacdecoder_handle_opened) { > + aac_info->aacdecoder_handle = aacDecoder_Open(TT_MP4_LATM_MCP1, 1); > + aac_info->aacdecoder_handle_opened = true; > + } > + > + pa_assert_se(AAC_DEC_OK == aacDecoder_SetParam(aac_info->aacdecoder_handle, AAC_PCM_MIN_OUTPUT_CHANNELS, > + sample_spec->channels)); > + pa_assert_se(AAC_DEC_OK == aacDecoder_SetParam(aac_info->aacdecoder_handle, AAC_PCM_MAX_OUTPUT_CHANNELS, > + sample_spec->channels)); > + > + return; > + } > + > + > + /* AAC SOURCE */ > + > + if (!aac_info->aacenc_handle_opened) { > + aac_err = aacEncOpen(&aac_info->aacenc_handle, 0, 2); > + > + if (aac_err != AACENC_OK) { > + pa_log_error("Cannot open AAC encoder handle: AAC error 0x%x", aac_err); > + pa_assert_not_reached(); > + } > + aac_info->aacenc_handle_opened = true; > + } > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_AOT, aot); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_SAMPLERATE, sample_rate); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_CHANNELMODE, channels); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + if (config->vbr) { > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_BITRATEMODE, > + (UINT) aac_info->aac_enc_bitrate_mode); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + } > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_AUDIOMUXVER, 2); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_SIGNALING_MODE, 1); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_BITRATE, aac_info->bitrate); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_TRANSMUX, TT_MP4_LATM_MCP1); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_HEADER_PERIOD, 1); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_AFTERBURNER, aac_info->aac_afterburner); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncEncode(aac_info->aacenc_handle, NULL, NULL, NULL, NULL); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + pa_assert_se(AACENC_OK == aacEncInfo(aac_info->aacenc_handle, &aac_info->aacenc_info)); > + > + pa_assert(aac_info->aacenc_info.inputChannels == aac_info->sample_spec.channels); > + > +}; > + > +static void pa_aac_get_read_block_size(size_t read_link_mtu, size_t *read_block_size, void **codec_data) { > + aac_info_t *aac_info = *codec_data; > + pa_assert(aac_info); > + > + aac_info->mtu = read_link_mtu; > + > + /* aacEncoder.pdf Section 3.2.1 > + * AAC-LC audio frame contains 1024 PCM samples per channel */ > + *read_block_size = 1024 * pa_frame_size(&aac_info->sample_spec); > + aac_info->read_block_size = *read_block_size; > +}; > + > +static void pa_aac_get_write_block_size(size_t write_link_mtu, size_t *write_block_size, void **codec_data) { > + aac_info_t *aac_info = *codec_data; > + pa_assert(aac_info); > + > + aac_info->mtu = write_link_mtu; > + > + /* aacEncoder.pdf section 3.2.1 > + * AAC-LC audio frame contains 1024 PCM samples per channel */ > + *write_block_size = 1024 * pa_frame_size(&aac_info->sample_spec); > + aac_info->write_block_size = *write_block_size; > +}; > + > +static void pa_aac_setup_stream(void **codec_data) { > + AACENC_ERROR aac_err; > + aac_info_t *aac_info = *codec_data; > + uint32_t max_bitrate; > + pa_assert(aac_info); > + > + max_bitrate = (uint32_t) ((8 * (aac_info->mtu - sizeof(struct rtp_header)) * aac_info->sample_spec.rate) / 1024); > + > + aac_info->bitrate = PA_MIN(max_bitrate, aac_info->bitrate); > + > + pa_log_debug("Maximum AAC transmission bitrate: %d bps; Bitrate in use: %d bps", max_bitrate, aac_info->bitrate); > + > + /* AAC SINK */ > + if (aac_info->is_a2dp_sink) { > + return; > + } > + > + > + /* AAC SOURCE */ > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_BITRATE, aac_info->bitrate); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_PEAK_BITRATE, (UINT) max_bitrate); > + if (aac_err != AACENC_OK) > + pa_assert_not_reached(); > + > +}; > + > +static void pa_aac_free(void **codec_data) { > + aac_info_t *aac_info = *codec_data; > + if (!aac_info) > + return; > + > + if (aac_info->aacenc_handle_opened) > + aacEncClose(&aac_info->aacenc_handle); > + > + if (aac_info->aacdecoder_handle_opened) > + aacDecoder_Close(aac_info->aacdecoder_handle); > + > + pa_xfree(aac_info); > + *codec_data = NULL; > + > +}; > + > +static size_t pa_aac_get_capabilities(void **_capabilities) { > + a2dp_aac_t *capabilities = pa_xmalloc0(sizeof(a2dp_aac_t)); > + > + capabilities->object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC | AAC_OBJECT_TYPE_MPEG4_AAC_LC; > + capabilities->channels = AAC_CHANNELS_1 | AAC_CHANNELS_2; > + AAC_SET_BITRATE(*capabilities, AAC_DEFAULT_BITRATE); > + AAC_SET_FREQUENCY(*capabilities, (AAC_SAMPLING_FREQ_8000 | AAC_SAMPLING_FREQ_11025 | AAC_SAMPLING_FREQ_12000 | > + AAC_SAMPLING_FREQ_16000 | AAC_SAMPLING_FREQ_22050 | AAC_SAMPLING_FREQ_24000 | > + AAC_SAMPLING_FREQ_32000 | AAC_SAMPLING_FREQ_44100 | AAC_SAMPLING_FREQ_48000 | > + AAC_SAMPLING_FREQ_64000 | AAC_SAMPLING_FREQ_88200 | AAC_SAMPLING_FREQ_96000)); > + capabilities->vbr = 1; > + *_capabilities = capabilities; > + > + return sizeof(*capabilities); > +}; > + > +static size_t > +pa_aac_select_configuration(const pa_sample_spec default_sample_spec, const uint8_t *supported_capabilities, > + const size_t capabilities_size, void **configuration) { > + a2dp_aac_t *cap = (a2dp_aac_t *) supported_capabilities; > + a2dp_aac_t *config = pa_xmalloc0(sizeof(a2dp_aac_t)); > + pa_a2dp_freq_cap_t aac_freq_cap, aac_freq_table[] = { > + {8000U, AAC_SAMPLING_FREQ_8000}, > + {11025U, AAC_SAMPLING_FREQ_11025}, > + {12000U, AAC_SAMPLING_FREQ_12000}, > + {16000U, AAC_SAMPLING_FREQ_16000}, > + {22050U, AAC_SAMPLING_FREQ_22050}, > + {24000U, AAC_SAMPLING_FREQ_24000}, > + {32000U, AAC_SAMPLING_FREQ_32000}, > + {44100U, AAC_SAMPLING_FREQ_44100}, > + {48000U, AAC_SAMPLING_FREQ_48000}, > + {64000U, AAC_SAMPLING_FREQ_64000}, > + {88200U, AAC_SAMPLING_FREQ_88200}, > + {96000U, AAC_SAMPLING_FREQ_96000} > + }; > + > + if (capabilities_size != sizeof(a2dp_aac_t)) > + return 0; > + > + if (!pa_a2dp_select_cap_frequency(AAC_GET_FREQUENCY(*cap), default_sample_spec, aac_freq_table, > + PA_ELEMENTSOF(aac_freq_table), &aac_freq_cap)) > + return 0; > + > + AAC_SET_FREQUENCY(*config, aac_freq_cap.cap); > + > + AAC_SET_BITRATE(*config, AAC_GET_BITRATE(*cap)); > + > + if (default_sample_spec.channels <= 1) { > + if (cap->channels & AAC_CHANNELS_1) > + config->channels = AAC_CHANNELS_1; > + else if (cap->channels & AAC_CHANNELS_2) > + config->channels = AAC_CHANNELS_2; > + else { > + pa_log_error("No supported channel modes"); > + return 0; > + } > + } > + > + if (default_sample_spec.channels >= 2) { > + if (cap->channels & AAC_CHANNELS_2) > + config->channels = AAC_CHANNELS_2; > + else if (cap->channels & AAC_CHANNELS_1) > + config->channels = AAC_CHANNELS_1; > + else { > + pa_log_error("No supported channel modes"); > + return 0; > + } > + } > + > + if (cap->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) > + config->object_type = AAC_OBJECT_TYPE_MPEG4_AAC_LC; > + else if (cap->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) > + config->object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC; > + else { > + pa_log_error("No supported aac object type"); > + return 0; > + } > + > + config->vbr = cap->vbr; > + > + *configuration = config; > + return sizeof(*config); > +}; > + > +static void pa_aac_free_capabilities(void **capabilities) { > + if (!capabilities || !*capabilities) > + return; > + pa_xfree(*capabilities); > + *capabilities = NULL; > +} > + > +static bool pa_aac_validate_configuration(const uint8_t *selected_configuration, const size_t configuration_size) { > + a2dp_aac_t *c = (a2dp_aac_t *) selected_configuration; > + > + if (configuration_size != sizeof(a2dp_aac_t)) { > + pa_log_error("AAC configuration array of invalid size"); > + return false; > + } > + > + switch (c->object_type) { > + case AAC_OBJECT_TYPE_MPEG2_AAC_LC: > + case AAC_OBJECT_TYPE_MPEG4_AAC_LC: > + break; > + default: > + pa_log_error("Invalid object type in AAC configuration"); > + return false; > + } > + > + switch (AAC_GET_FREQUENCY(*c)) { > + case AAC_SAMPLING_FREQ_8000: > + case AAC_SAMPLING_FREQ_11025: > + case AAC_SAMPLING_FREQ_12000: > + case AAC_SAMPLING_FREQ_16000: > + case AAC_SAMPLING_FREQ_22050: > + case AAC_SAMPLING_FREQ_24000: > + case AAC_SAMPLING_FREQ_32000: > + case AAC_SAMPLING_FREQ_44100: > + case AAC_SAMPLING_FREQ_48000: > + case AAC_SAMPLING_FREQ_64000: > + case AAC_SAMPLING_FREQ_88200: > + case AAC_SAMPLING_FREQ_96000: > + break; > + default: > + pa_log_error("Invalid sampling frequency in AAC configuration"); > + return false; > + } > + > + switch (c->channels) { > + case AAC_CHANNELS_1: > + case AAC_CHANNELS_2: > + break; > + default: > + pa_log_error("Invalid channel mode in AAC Configuration"); > + return false; > + } > + > + return true; > +}; > + > + > +static pa_a2dp_source_t pa_aac_source = { > + .encoder_load = pa_aac_encoder_load, > + .init = pa_aac_encoder_init, > + .update_user_config = pa_aac_update_user_config, > + .encode = pa_aac_encode, > + .config_transport=pa_aac_config_transport, > + .get_block_size=pa_aac_get_write_block_size, > + .setup_stream = pa_aac_setup_stream, > + .set_tx_length = NULL, > + .decrease_quality = NULL, > + .free = pa_aac_free > +}; > + > +static pa_a2dp_sink_t pa_aac_sink = { > + .decoder_load = pa_aac_decoder_load, > + .init = pa_aac_decoder_init, > + .update_user_config = NULL, > + .config_transport=pa_aac_config_transport, > + .get_block_size=pa_aac_get_read_block_size, > + .setup_stream = pa_aac_setup_stream, > + .decode = pa_aac_decode, > + .free = pa_aac_free > +}; > + > +const pa_a2dp_codec_t pa_a2dp_aac = { > + .name = "AAC", > + .codec = A2DP_CODEC_MPEG24, > + .vendor_codec = NULL, > + .a2dp_sink = &pa_aac_sink, > + .a2dp_source = &pa_aac_source, > + .get_capabilities = pa_aac_get_capabilities, > + .select_configuration = pa_aac_select_configuration, > + .free_capabilities = pa_aac_free_capabilities, > + .free_configuration = pa_aac_free_capabilities, > + .validate_configuration = pa_aac_validate_configuration > +}; > diff --git a/src/modules/bluetooth/a2dp/a2dp_util.c b/src/modules/bluetooth/a2dp/a2dp_util.c > index cc9382f2e..edd28b5cc 100644 > --- a/src/modules/bluetooth/a2dp/a2dp_util.c > +++ b/src/modules/bluetooth/a2dp/a2dp_util.c > @@ -30,6 +30,9 @@ > #define A2DP_SBC_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/SBC" > #define A2DP_SBC_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/SBC" > > +#define A2DP_AAC_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/AAC" > +#define A2DP_AAC_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/AAC" > + > #define A2DP_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR" > #define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR" > > @@ -209,6 +212,14 @@ void pa_a2dp_codec_index_to_endpoint(pa_a2dp_codec_index_t codec_index, const ch > case PA_A2DP_SOURCE_SBC: > *endpoint = A2DP_SBC_SRC_ENDPOINT; > break; > +#ifdef HAVE_FDK_AAC > + case PA_A2DP_SINK_AAC: > + *endpoint = A2DP_AAC_SNK_ENDPOINT; > + break; > + case PA_A2DP_SOURCE_AAC: > + *endpoint = A2DP_AAC_SRC_ENDPOINT; > + break; > +#endif > default: > *endpoint = NULL; > } > @@ -219,6 +230,12 @@ void pa_a2dp_endpoint_to_codec_index(const char *endpoint, pa_a2dp_codec_index_t > *codec_index = PA_A2DP_SINK_SBC; > else if (streq(endpoint, A2DP_SBC_SRC_ENDPOINT)) > *codec_index = PA_A2DP_SOURCE_SBC; > +#ifdef HAVE_FDK_AAC > + else if (streq(endpoint, A2DP_AAC_SNK_ENDPOINT)) > + *codec_index = PA_A2DP_SINK_AAC; > + else if (streq(endpoint, A2DP_AAC_SRC_ENDPOINT)) > + *codec_index = PA_A2DP_SOURCE_AAC; > +#endif > else > *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; > }; > @@ -229,6 +246,12 @@ void pa_a2dp_codec_index_to_a2dp_codec(pa_a2dp_codec_index_t codec_index, const > case PA_A2DP_SOURCE_SBC: > *a2dp_codec = &pa_a2dp_sbc; > break; > +#ifdef HAVE_FDK_AAC > + case PA_A2DP_SINK_AAC: > + case PA_A2DP_SOURCE_AAC: > + *a2dp_codec = &pa_a2dp_aac; > + break; > +#endif > default: > *a2dp_codec = NULL; > } > @@ -244,6 +267,11 @@ void pa_a2dp_a2dp_codec_to_codec_index(const pa_a2dp_codec_t *a2dp_codec, bool i > case A2DP_CODEC_SBC: > *codec_index = is_a2dp_sink ? PA_A2DP_SINK_SBC : PA_A2DP_SOURCE_SBC; > return; > +#ifdef HAVE_FDK_AAC > + case A2DP_CODEC_MPEG24: > + *codec_index = is_a2dp_sink ? PA_A2DP_SINK_AAC : PA_A2DP_SOURCE_AAC; > + return; > +#endif > case A2DP_CODEC_VENDOR: > if (!a2dp_codec->vendor_codec) { > *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; > @@ -262,6 +290,11 @@ pa_a2dp_get_a2dp_codec(uint8_t codec, const a2dp_vendor_codec_t *vendor_codec, c > case A2DP_CODEC_SBC: > *a2dp_codec = &pa_a2dp_sbc; > return; > +#ifdef HAVE_FDK_AAC > + case A2DP_CODEC_MPEG24: > + *a2dp_codec = &pa_a2dp_aac; > + return; > +#endif > case A2DP_CODEC_VENDOR: > if (!vendor_codec) { > *a2dp_codec = NULL; > -- Im little worried about the maintainance of a2dp_util.c if we keedp the practice of #ifsef every codec, imo it would be much cleaner if we just have a mechanism to register the codecs, maybe we load them as separate plugins which then call a2dp_util.c to register their 'drivers', the plugin can check if the bluetooth plugin is loaded in order to register itself, doing that way we can also pass the parameter via default.pa. -- Luiz Augusto von Dentz _______________________________________________ pulseaudio-discuss mailing list pulseaudio-discuss@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss