Integration of the AAC codec for A2DP source
Also:
- Implemented data fragmentation inside bta_av_data_path()
that is RTP compatible.
- Do not use the codec_type when composing the RTP payload type
per RFC 3016, Section 4.2. That value doesn't have actual meaning
in the context of the Bluetooth supported codecs, and is ambiguous:
all vendor codecs map to the same value 0xFF.
- Updated support function A2DP_BitsSet() so it works for
up to 64-bit integers.
- Updated a log message inside l2c_data_write() to print
packet length and peer MTU on error.
Test: A2DP streaming to AAC headsets
Bug: 30958229
Change-Id: I1b530f1c5c495b8231fd68bed788d4567096683d
diff --git a/stack/Android.bp b/stack/Android.bp
index 899407b..f0353cc 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -20,6 +20,8 @@
"srvc",
],
include_dirs: [
+ "external/aac/libAACenc/include",
+ "external/aac/libSYS/include",
"external/libldac/inc",
"system/bt",
"system/bt/btcore/include",
@@ -34,6 +36,8 @@
"system/bt/utils/include",
],
srcs: [
+ "a2dp/a2dp_aac.cc",
+ "a2dp/a2dp_aac_encoder.cc",
"a2dp/a2dp_api.cc",
"a2dp/a2dp_codec_config.cc",
"a2dp/a2dp_sbc.cc",
@@ -162,7 +166,10 @@
"srvc/srvc_dis.cc",
"srvc/srvc_eng.cc",
],
- static_libs: ["libbt-hci"],
+ static_libs: [
+ "libbt-hci",
+ "libFraunhoferAAC",
+ ],
shared_libs: [
"libcutils",
"liblog",
@@ -191,6 +198,7 @@
static_libs: [
"libbt-stack",
"libbt-sbc-encoder",
+ "libFraunhoferAAC",
"libosi",
],
}
diff --git a/stack/a2dp/a2dp_aac.cc b/stack/a2dp/a2dp_aac.cc
new file mode 100644
index 0000000..23207d0
--- /dev/null
+++ b/stack/a2dp/a2dp_aac.cc
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/******************************************************************************
+ *
+ * Utility functions to help build and parse the AAC Codec Information
+ * Element and Media Payload.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "a2dp_aac"
+
+#include "bt_target.h"
+
+#include "a2dp_aac.h"
+
+#include <string.h>
+
+#include <base/logging.h>
+#include "a2dp_aac_encoder.h"
+#include "bt_utils.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+#define A2DP_AAC_DEFAULT_BITRATE 320000 // 320 kbps
+#define A2DP_AAC_MIN_BITRATE 64000 // 64 kbps
+
+// data type for the AAC Codec Information Element */
+// NOTE: bits_per_sample is needed only for AAC encoder initialization.
+typedef struct {
+ uint8_t objectType; /* Object Type */
+ uint16_t sampleRate; /* Sampling Frequency */
+ uint8_t channelMode; /* STEREO/MONO */
+ uint8_t variableBitRateSupport; /* Variable Bit Rate Support*/
+ uint32_t bitRate; /* Bit rate */
+ btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+} tA2DP_AAC_CIE;
+
+/* AAC Source codec capabilities */
+static const tA2DP_AAC_CIE a2dp_aac_caps = {
+ // objectType
+ A2DP_AAC_OBJECT_TYPE_MPEG2_LC,
+ // sampleRate
+ (A2DP_AAC_SAMPLING_FREQ_44100 | A2DP_AAC_SAMPLING_FREQ_48000 |
+ A2DP_AAC_SAMPLING_FREQ_88200 | A2DP_AAC_SAMPLING_FREQ_96000),
+ // channelMode
+ A2DP_AAC_CHANNEL_MODE_STEREO,
+ // variableBitRateSupport
+ A2DP_AAC_VARIABLE_BIT_RATE_DISABLED,
+ // bitRate
+ A2DP_AAC_DEFAULT_BITRATE,
+ // bits_per_sample
+ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16};
+
+/* Default AAC codec configuration */
+static const tA2DP_AAC_CIE a2dp_aac_default_config = {
+ A2DP_AAC_OBJECT_TYPE_MPEG2_LC, // objectType
+ A2DP_AAC_SAMPLING_FREQ_44100, // sampleRate
+ A2DP_AAC_CHANNEL_MODE_STEREO, // channelMode
+ A2DP_AAC_VARIABLE_BIT_RATE_DISABLED, // variableBitRateSupport
+ A2DP_AAC_DEFAULT_BITRATE, // bitRate
+ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 // bits_per_sample
+};
+
+static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_aac = {
+ a2dp_aac_encoder_init,
+ a2dp_aac_encoder_cleanup,
+ a2dp_aac_feeding_reset,
+ a2dp_aac_feeding_flush,
+ a2dp_aac_get_encoder_interval_ms,
+ a2dp_aac_send_frames,
+ a2dp_aac_debug_codec_dump};
+
+UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAac(
+ const tA2DP_AAC_CIE* p_cap, const uint8_t* p_codec_info,
+ bool is_peer_codec_info);
+
+// Builds the AAC Media Codec Capabilities byte sequence beginning from the
+// LOSC octet. |media_type| is the media type |AVDT_MEDIA_TYPE_*|.
+// |p_ie| is a pointer to the AAC Codec Information Element information.
+// The result is stored in |p_result|. Returns A2DP_SUCCESS on success,
+// otherwise the corresponding A2DP error status code.
+static tA2DP_STATUS A2DP_BuildInfoAac(uint8_t media_type,
+ const tA2DP_AAC_CIE* p_ie,
+ uint8_t* p_result) {
+ if (p_ie == NULL || p_result == NULL) {
+ return A2DP_INVALID_PARAMS;
+ }
+
+ *p_result++ = A2DP_AAC_CODEC_LEN;
+ *p_result++ = (media_type << 4);
+ *p_result++ = A2DP_MEDIA_CT_AAC;
+
+ // Object Type
+ if (p_ie->objectType == 0) return A2DP_INVALID_PARAMS;
+ *p_result++ = p_ie->objectType;
+
+ // Sampling Frequency
+ if (p_ie->sampleRate == 0) return A2DP_INVALID_PARAMS;
+ *p_result++ = (uint8_t)(p_ie->sampleRate & A2DP_AAC_SAMPLING_FREQ_MASK0);
+ *p_result = (uint8_t)((p_ie->sampleRate & A2DP_AAC_SAMPLING_FREQ_MASK1) >> 8);
+
+ // Channel Mode
+ if (p_ie->channelMode == 0) return A2DP_INVALID_PARAMS;
+ *p_result++ |= (p_ie->channelMode & A2DP_AAC_CHANNEL_MODE_MASK);
+
+ // Variable Bit Rate Support
+ *p_result = (p_ie->variableBitRateSupport & A2DP_AAC_VARIABLE_BIT_RATE_MASK);
+
+ // Bit Rate
+ *p_result++ |= (uint8_t)((p_ie->bitRate & A2DP_AAC_BIT_RATE_MASK0) >> 16);
+ *p_result++ = (uint8_t)((p_ie->bitRate & A2DP_AAC_BIT_RATE_MASK1) >> 8);
+ *p_result++ = (uint8_t)(p_ie->bitRate & A2DP_AAC_BIT_RATE_MASK2);
+
+ return A2DP_SUCCESS;
+}
+
+// Parses the AAC Media Codec Capabilities byte sequence beginning from the
+// LOSC octet. The result is stored in |p_ie|. The byte sequence to parse is
+// |p_codec_info|. If |is_capability| is true, the byte sequence is
+// codec capabilities, otherwise is codec configuration.
+// Returns A2DP_SUCCESS on success, otherwise the corresponding A2DP error
+// status code.
+static tA2DP_STATUS A2DP_ParseInfoAac(tA2DP_AAC_CIE* p_ie,
+ const uint8_t* p_codec_info,
+ bool is_capability) {
+ uint8_t losc;
+ uint8_t media_type;
+ tA2DP_CODEC_TYPE codec_type;
+
+ if (p_ie == NULL || p_codec_info == NULL) return A2DP_INVALID_PARAMS;
+
+ // Check the codec capability length
+ losc = *p_codec_info++;
+ if (losc != A2DP_AAC_CODEC_LEN) return A2DP_WRONG_CODEC;
+
+ media_type = (*p_codec_info++) >> 4;
+ codec_type = *p_codec_info++;
+ /* Check the Media Type and Media Codec Type */
+ if (media_type != AVDT_MEDIA_TYPE_AUDIO || codec_type != A2DP_MEDIA_CT_AAC) {
+ return A2DP_WRONG_CODEC;
+ }
+
+ p_ie->objectType = *p_codec_info++;
+ p_ie->sampleRate = (*p_codec_info & A2DP_AAC_SAMPLING_FREQ_MASK0) |
+ (*(p_codec_info + 1) << 8 & A2DP_AAC_SAMPLING_FREQ_MASK1);
+ p_codec_info++;
+ p_ie->channelMode = *p_codec_info & A2DP_AAC_CHANNEL_MODE_MASK;
+ p_codec_info++;
+
+ p_ie->variableBitRateSupport =
+ *p_codec_info & A2DP_AAC_VARIABLE_BIT_RATE_MASK;
+
+ p_ie->bitRate = ((*p_codec_info) << 16 & A2DP_AAC_BIT_RATE_MASK0) |
+ (*(p_codec_info + 1) << 8 & A2DP_AAC_BIT_RATE_MASK1) |
+ (*(p_codec_info + 2) & A2DP_AAC_BIT_RATE_MASK2);
+ p_codec_info += 3;
+
+ if (is_capability) return A2DP_SUCCESS;
+
+ if (A2DP_BitsSet(p_ie->objectType) != A2DP_SET_ONE_BIT)
+ return A2DP_BAD_OBJ_TYPE;
+ if (A2DP_BitsSet(p_ie->sampleRate) != A2DP_SET_ONE_BIT)
+ return A2DP_BAD_SAMP_FREQ;
+ if (A2DP_BitsSet(p_ie->channelMode) != A2DP_SET_ONE_BIT)
+ return A2DP_BAD_CH_MODE;
+
+ return A2DP_SUCCESS;
+}
+
+bool A2DP_IsSourceCodecValidAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE cfg_cie;
+
+ /* Use a liberal check when parsing the codec info */
+ return (A2DP_ParseInfoAac(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+ (A2DP_ParseInfoAac(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsSinkCodecValidAac(UNUSED_ATTR const uint8_t* p_codec_info) {
+ return false;
+}
+
+bool A2DP_IsPeerSourceCodecValidAac(UNUSED_ATTR const uint8_t* p_codec_info) {
+ return false;
+}
+
+bool A2DP_IsPeerSinkCodecValidAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE cfg_cie;
+
+ /* Use a liberal check when parsing the codec info */
+ return (A2DP_ParseInfoAac(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+ (A2DP_ParseInfoAac(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsSinkCodecSupportedAac(UNUSED_ATTR const uint8_t* p_codec_info) {
+ return false;
+}
+
+bool A2DP_IsPeerSourceCodecSupportedAac(
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+ return false;
+}
+
+tA2DP_STATUS A2DP_BuildSrc2SinkConfigAac(UNUSED_ATTR const uint8_t* p_src_cap,
+ UNUSED_ATTR uint8_t* p_pref_cfg) {
+ return A2DP_NS_CODEC_TYPE;
+}
+
+// Checks whether A2DP AAC codec configuration matches with a device's codec
+// capabilities. |p_cap| is the AAC codec configuration. |p_codec_info| is
+// the device's codec capabilities.
+// If |is_capability| is true, the byte sequence is codec capabilities,
+// otherwise is codec configuration.
+// |p_codec_info| contains the codec capabilities for a peer device that
+// is acting as an A2DP source.
+// Returns A2DP_SUCCESS if the codec configuration matches with capabilities,
+// otherwise the corresponding A2DP error status code.
+static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAac(
+ const tA2DP_AAC_CIE* p_cap, const uint8_t* p_codec_info,
+ bool is_capability) {
+ tA2DP_STATUS status;
+ tA2DP_AAC_CIE cfg_cie;
+
+ /* parse configuration */
+ status = A2DP_ParseInfoAac(&cfg_cie, p_codec_info, is_capability);
+ if (status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
+ return status;
+ }
+
+ /* verify that each parameter is in range */
+
+ LOG_DEBUG(LOG_TAG, "%s: Object Type peer: 0x%x, capability 0x%x", __func__,
+ cfg_cie.objectType, p_cap->objectType);
+ LOG_DEBUG(LOG_TAG, "%s: Sample Rate peer: %u, capability %u", __func__,
+ cfg_cie.sampleRate, p_cap->sampleRate);
+ LOG_DEBUG(LOG_TAG, "%s: Channel Mode peer: 0x%x, capability 0x%x", __func__,
+ cfg_cie.channelMode, p_cap->channelMode);
+ LOG_DEBUG(
+ LOG_TAG, "%s: Variable Bit Rate Support peer: 0x%x, capability 0x%x",
+ __func__, cfg_cie.variableBitRateSupport, p_cap->variableBitRateSupport);
+ LOG_DEBUG(LOG_TAG, "%s: Bit Rate peer: %u, capability %u", __func__,
+ cfg_cie.bitRate, p_cap->bitRate);
+
+ /* Object Type */
+ if ((cfg_cie.objectType & p_cap->objectType) == 0) return A2DP_BAD_OBJ_TYPE;
+
+ /* Sample Rate */
+ if ((cfg_cie.sampleRate & p_cap->sampleRate) == 0) return A2DP_BAD_SAMP_FREQ;
+
+ /* Channel Mode */
+ if ((cfg_cie.channelMode & p_cap->channelMode) == 0) return A2DP_NS_CH_MODE;
+
+ return A2DP_SUCCESS;
+}
+
+bool A2DP_UsesRtpHeaderAac(UNUSED_ATTR bool content_protection_enabled,
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+ return true;
+}
+
+const char* A2DP_CodecNameAac(UNUSED_ATTR const uint8_t* p_codec_info) {
+ return "AAC";
+}
+
+bool A2DP_CodecTypeEqualsAac(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b) {
+ tA2DP_AAC_CIE aac_cie_a;
+ tA2DP_AAC_CIE aac_cie_b;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAac(&aac_cie_a, p_codec_info_a, true);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return false;
+ }
+ a2dp_status = A2DP_ParseInfoAac(&aac_cie_b, p_codec_info_b, true);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return false;
+ }
+
+ return true;
+}
+
+bool A2DP_CodecEqualsAac(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b) {
+ tA2DP_AAC_CIE aac_cie_a;
+ tA2DP_AAC_CIE aac_cie_b;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAac(&aac_cie_a, p_codec_info_a, true);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return false;
+ }
+ a2dp_status = A2DP_ParseInfoAac(&aac_cie_b, p_codec_info_b, true);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return false;
+ }
+
+ return (aac_cie_a.objectType == aac_cie_b.objectType) &&
+ (aac_cie_a.sampleRate == aac_cie_b.sampleRate) &&
+ (aac_cie_a.channelMode == aac_cie_b.channelMode) &&
+ (aac_cie_a.variableBitRateSupport ==
+ aac_cie_b.variableBitRateSupport) &&
+ (aac_cie_a.bitRate == aac_cie_b.bitRate);
+}
+
+int A2DP_GetTrackSampleRateAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE aac_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ switch (aac_cie.sampleRate) {
+ case A2DP_AAC_SAMPLING_FREQ_8000:
+ return 8000;
+ case A2DP_AAC_SAMPLING_FREQ_11025:
+ return 11025;
+ case A2DP_AAC_SAMPLING_FREQ_12000:
+ return 12000;
+ case A2DP_AAC_SAMPLING_FREQ_16000:
+ return 16000;
+ case A2DP_AAC_SAMPLING_FREQ_22050:
+ return 22050;
+ case A2DP_AAC_SAMPLING_FREQ_24000:
+ return 24000;
+ case A2DP_AAC_SAMPLING_FREQ_32000:
+ return 32000;
+ case A2DP_AAC_SAMPLING_FREQ_44100:
+ return 44100;
+ case A2DP_AAC_SAMPLING_FREQ_48000:
+ return 48000;
+ case A2DP_AAC_SAMPLING_FREQ_64000:
+ return 64000;
+ case A2DP_AAC_SAMPLING_FREQ_88200:
+ return 88200;
+ case A2DP_AAC_SAMPLING_FREQ_96000:
+ return 96000;
+ }
+
+ return -1;
+}
+
+int A2DP_GetTrackBitsPerSampleAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE aac_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ // NOTE: Hard-coded value - currently the AAC encoder library
+ // is compiled with 16 bits per sample
+ return 16;
+}
+
+int A2DP_GetTrackChannelCountAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE aac_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ switch (aac_cie.channelMode) {
+ case A2DP_AAC_CHANNEL_MODE_MONO:
+ return 1;
+ case A2DP_AAC_CHANNEL_MODE_STEREO:
+ return 2;
+ }
+
+ return -1;
+}
+
+int A2DP_GetSinkTrackChannelTypeAac(UNUSED_ATTR const uint8_t* p_codec_info) {
+ return -1;
+}
+
+int A2DP_GetSinkFramesCountToProcessAac(
+ UNUSED_ATTR uint64_t time_interval_ms,
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+ return -1;
+}
+
+int A2DP_GetObjectTypeCodeAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE aac_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ switch (aac_cie.objectType) {
+ case A2DP_AAC_OBJECT_TYPE_MPEG2_LC:
+ case A2DP_AAC_OBJECT_TYPE_MPEG4_LC:
+ case A2DP_AAC_OBJECT_TYPE_MPEG4_LTP:
+ case A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE:
+ return aac_cie.objectType;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+int A2DP_GetChannelModeCodeAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE aac_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ switch (aac_cie.channelMode) {
+ case A2DP_AAC_CHANNEL_MODE_MONO:
+ case A2DP_AAC_CHANNEL_MODE_STEREO:
+ return aac_cie.channelMode;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+int A2DP_GetVariableBitRateSupportAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE aac_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ switch (aac_cie.variableBitRateSupport) {
+ case A2DP_AAC_VARIABLE_BIT_RATE_ENABLED:
+ case A2DP_AAC_VARIABLE_BIT_RATE_DISABLED:
+ return aac_cie.variableBitRateSupport;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+int A2DP_GetBitRateAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE aac_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ return aac_cie.bitRate;
+}
+
+bool A2DP_GetPacketTimestampAac(const uint8_t* p_codec_info,
+ const uint8_t* p_data, uint32_t* p_timestamp) {
+ // TODO: Is this function really codec-specific?
+ *p_timestamp = *(const uint32_t*)p_data;
+ return true;
+}
+
+bool A2DP_BuildCodecHeaderAac(UNUSED_ATTR const uint8_t* p_codec_info,
+ UNUSED_ATTR BT_HDR* p_buf,
+ UNUSED_ATTR uint16_t frames_per_packet) {
+ return true;
+}
+
+void A2DP_DumpCodecInfoAac(const uint8_t* p_codec_info) {
+ tA2DP_STATUS a2dp_status;
+ tA2DP_AAC_CIE aac_cie;
+
+ LOG_DEBUG(LOG_TAG, "%s", __func__);
+
+ a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, true);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: A2DP_ParseInfoAac fail:%d", __func__, a2dp_status);
+ return;
+ }
+
+ LOG_DEBUG(LOG_TAG, "\tobjectType: 0x%x", aac_cie.objectType);
+ if (aac_cie.objectType & A2DP_AAC_OBJECT_TYPE_MPEG2_LC) {
+ LOG_DEBUG(LOG_TAG, "\tobjectType: (MPEG-2 AAC LC)");
+ }
+ if (aac_cie.objectType & A2DP_AAC_OBJECT_TYPE_MPEG4_LC) {
+ LOG_DEBUG(LOG_TAG, "\tobjectType: (MPEG-4 AAC LC)");
+ }
+ if (aac_cie.objectType & A2DP_AAC_OBJECT_TYPE_MPEG4_LTP) {
+ LOG_DEBUG(LOG_TAG, "\tobjectType: (MPEG-4 AAC LTP)");
+ }
+ if (aac_cie.objectType & A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE) {
+ LOG_DEBUG(LOG_TAG, "\tobjectType: (MPEG-4 AAC Scalable)");
+ }
+
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: 0x%x", aac_cie.sampleRate);
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_8000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (8000)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_11025) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (11025)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_12000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (12000)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_16000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (16000)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_22050) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (22050)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_24000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (24000)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_32000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (32000)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_44100) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (44100)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_48000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (48000)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_64000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (64000)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_88200) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (88200)");
+ }
+ if (aac_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_96000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (96000)");
+ }
+
+ LOG_DEBUG(LOG_TAG, "\tch_mode: 0x%x", aac_cie.channelMode);
+ if (aac_cie.channelMode == A2DP_AAC_CHANNEL_MODE_MONO) {
+ LOG_DEBUG(LOG_TAG, "\tch_mode: (Mono)");
+ }
+ if (aac_cie.channelMode == A2DP_AAC_CHANNEL_MODE_STEREO) {
+ LOG_DEBUG(LOG_TAG, "\tch_mode: (Stereo)");
+ }
+
+ LOG_DEBUG(LOG_TAG, "\tvariableBitRateSupport: %s",
+ (aac_cie.variableBitRateSupport != 0) ? "true" : "false");
+
+ LOG_DEBUG(LOG_TAG, "\tbitRate: %u", aac_cie.bitRate);
+}
+
+const tA2DP_ENCODER_INTERFACE* A2DP_GetEncoderInterfaceAac(
+ const uint8_t* p_codec_info) {
+ if (!A2DP_IsSourceCodecValidAac(p_codec_info)) return NULL;
+
+ return &a2dp_encoder_interface_aac;
+}
+
+bool A2DP_AdjustCodecAac(uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE cfg_cie;
+
+ // Nothing to do: just verify the codec info is valid
+ if (A2DP_ParseInfoAac(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS)
+ return false;
+
+ return true;
+}
+
+btav_a2dp_codec_index_t A2DP_SourceCodecIndexAac(
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+ return BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
+}
+
+const char* A2DP_CodecIndexStrAac(void) { return "AAC"; }
+
+bool A2DP_InitCodecConfigAac(tAVDT_CFG* p_cfg) {
+ if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aac_caps,
+ p_cfg->codec_info) != A2DP_SUCCESS) {
+ return false;
+ }
+
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+ /* Content protection info - support SCMS-T */
+ uint8_t* p = p_cfg->protect_info;
+ *p++ = AVDT_CP_LOSC;
+ UINT16_TO_STREAM(p, AVDT_CP_SCMS_T_ID);
+ p_cfg->num_protect = 1;
+#endif
+
+ return true;
+}
+
+UNUSED_ATTR static void build_codec_config(const tA2DP_AAC_CIE& config_cie,
+ btav_a2dp_codec_config_t* result) {
+ if (config_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_44100)
+ result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+ if (config_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_48000)
+ result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+ if (config_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_88200)
+ result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_88200;
+ if (config_cie.sampleRate & A2DP_AAC_SAMPLING_FREQ_96000)
+ result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_96000;
+
+ result->bits_per_sample = config_cie.bits_per_sample;
+
+ if (config_cie.channelMode & A2DP_AAC_CHANNEL_MODE_MONO)
+ result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+ if (config_cie.channelMode & A2DP_AAC_CHANNEL_MODE_STEREO) {
+ result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+ }
+}
+
+A2dpCodecConfigAac::A2dpCodecConfigAac()
+ : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC, "AAC") {}
+
+A2dpCodecConfigAac::~A2dpCodecConfigAac() {}
+
+bool A2dpCodecConfigAac::init() {
+ if (!isValid()) return false;
+
+ // Load the encoder
+ if (!A2DP_LoadEncoderAac()) {
+ LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+ return false;
+ }
+
+ return true;
+}
+
+//
+// Selects the best sample rate from |sampleRate|.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_sample_rate(uint16_t sampleRate,
+ tA2DP_AAC_CIE* p_result,
+ btav_a2dp_codec_config_t* p_codec_config) {
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_96000) {
+ p_result->sampleRate = A2DP_AAC_SAMPLING_FREQ_96000;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_96000;
+ return true;
+ }
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_88200) {
+ p_result->sampleRate = A2DP_AAC_SAMPLING_FREQ_88200;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_88200;
+ return true;
+ }
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_48000) {
+ p_result->sampleRate = A2DP_AAC_SAMPLING_FREQ_48000;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+ return true;
+ }
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_44100) {
+ p_result->sampleRate = A2DP_AAC_SAMPLING_FREQ_44100;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+ return true;
+ }
+ return false;
+}
+
+//
+// Selects the audio sample rate from |p_codec_audio_config|.
+// |sampleRate| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_sample_rate(
+ const btav_a2dp_codec_config_t* p_codec_audio_config, uint16_t sampleRate,
+ tA2DP_AAC_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+ switch (p_codec_audio_config->sample_rate) {
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_44100) {
+ p_result->sampleRate = A2DP_AAC_SAMPLING_FREQ_44100;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_48000) {
+ p_result->sampleRate = A2DP_AAC_SAMPLING_FREQ_48000;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_88200) {
+ p_result->sampleRate = A2DP_AAC_SAMPLING_FREQ_88200;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_88200;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_96000) {
+ p_result->sampleRate = A2DP_AAC_SAMPLING_FREQ_96000;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_96000;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
+ break;
+ }
+ return false;
+}
+
+//
+// Selects the best bits per sample from |bits_per_sample|.
+// |bits_per_sample| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_bits_per_sample(
+ btav_a2dp_codec_bits_per_sample_t bits_per_sample, tA2DP_AAC_CIE* p_result,
+ btav_a2dp_codec_config_t* p_codec_config) {
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) {
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+ p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+ return true;
+ }
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) {
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+ p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+ return true;
+ }
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) {
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+ p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+ return true;
+ }
+ return false;
+}
+
+//
+// Selects the audio bits per sample from |p_codec_audio_config|.
+// |bits_per_sample| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_bits_per_sample(
+ const btav_a2dp_codec_config_t* p_codec_audio_config,
+ btav_a2dp_codec_bits_per_sample_t bits_per_sample, tA2DP_AAC_CIE* p_result,
+ btav_a2dp_codec_config_t* p_codec_config) {
+ switch (p_codec_audio_config->bits_per_sample) {
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) {
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+ p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) {
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+ p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) {
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+ p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+ break;
+ }
+ return false;
+}
+
+//
+// Selects the best channel mode from |channelMode|.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_channel_mode(uint8_t channelMode,
+ tA2DP_AAC_CIE* p_result,
+ btav_a2dp_codec_config_t* p_codec_config) {
+ if (channelMode & A2DP_AAC_CHANNEL_MODE_STEREO) {
+ p_result->channelMode = A2DP_AAC_CHANNEL_MODE_STEREO;
+ p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+ return true;
+ }
+ if (channelMode & A2DP_AAC_CHANNEL_MODE_MONO) {
+ p_result->channelMode = A2DP_AAC_CHANNEL_MODE_MONO;
+ p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+ return true;
+ }
+ return false;
+}
+
+//
+// Selects the audio channel mode from |p_codec_audio_config|.
+// |channelMode| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_channel_mode(
+ const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t channelMode,
+ tA2DP_AAC_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+ switch (p_codec_audio_config->channel_mode) {
+ case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+ if (channelMode & A2DP_AAC_CHANNEL_MODE_MONO) {
+ p_result->channelMode = A2DP_AAC_CHANNEL_MODE_MONO;
+ p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+ if (channelMode & A2DP_AAC_CHANNEL_MODE_STEREO) {
+ p_result->channelMode = A2DP_AAC_CHANNEL_MODE_STEREO;
+ p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
+ break;
+ }
+
+ return false;
+}
+
+bool A2dpCodecConfigAac::setCodecConfig(const uint8_t* p_peer_codec_info,
+ bool is_capability,
+ uint8_t* p_result_codec_config) {
+ std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+ tA2DP_AAC_CIE sink_info_cie;
+ tA2DP_AAC_CIE result_config_cie;
+ uint8_t channelMode;
+ uint16_t sampleRate;
+ btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+
+ // Save the internal state
+ btav_a2dp_codec_config_t saved_codec_config = codec_config_;
+ btav_a2dp_codec_config_t saved_codec_capability = codec_capability_;
+ btav_a2dp_codec_config_t saved_codec_user_config = codec_user_config_;
+ btav_a2dp_codec_config_t saved_codec_audio_config = codec_audio_config_;
+ uint8_t saved_ota_codec_config[AVDT_CODEC_SIZE];
+ uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE];
+ uint8_t saved_ota_codec_peer_config[AVDT_CODEC_SIZE];
+ memcpy(saved_ota_codec_config, ota_codec_config_, sizeof(ota_codec_config_));
+ memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_,
+ sizeof(ota_codec_peer_capability_));
+ memcpy(saved_ota_codec_peer_config, ota_codec_peer_config_,
+ sizeof(ota_codec_peer_config_));
+
+ tA2DP_STATUS status =
+ A2DP_ParseInfoAac(&sink_info_cie, p_peer_codec_info, is_capability);
+ if (status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: can't parse peer's Sink capabilities: error = %d",
+ __func__, status);
+ goto fail;
+ }
+
+ //
+ // Build the preferred configuration
+ //
+ memset(&result_config_cie, 0, sizeof(result_config_cie));
+
+ // NOTE: Always assign the Object Type and Variable Bit Rate Support.
+ result_config_cie.objectType = a2dp_aac_caps.objectType;
+ result_config_cie.variableBitRateSupport =
+ a2dp_aac_caps.variableBitRateSupport;
+
+ // Set the bit rate to the smaller of the local and peer bit rate
+ // However, make sure the bit rate doesn't go beyond a certain threshold
+ result_config_cie.bitRate =
+ std::min(a2dp_aac_caps.bitRate, sink_info_cie.bitRate);
+ result_config_cie.bitRate = std::max(
+ result_config_cie.bitRate, static_cast<uint32_t>(A2DP_AAC_MIN_BITRATE));
+
+ //
+ // Select the sample frequency
+ //
+ sampleRate = a2dp_aac_caps.sampleRate & sink_info_cie.sampleRate;
+ codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+ switch (codec_user_config_.sample_rate) {
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_44100) {
+ result_config_cie.sampleRate = A2DP_AAC_SAMPLING_FREQ_44100;
+ codec_capability_.sample_rate = codec_user_config_.sample_rate;
+ codec_config_.sample_rate = codec_user_config_.sample_rate;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_48000) {
+ result_config_cie.sampleRate = A2DP_AAC_SAMPLING_FREQ_48000;
+ codec_capability_.sample_rate = codec_user_config_.sample_rate;
+ codec_config_.sample_rate = codec_user_config_.sample_rate;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_88200) {
+ result_config_cie.sampleRate = A2DP_AAC_SAMPLING_FREQ_88200;
+ codec_capability_.sample_rate = codec_user_config_.sample_rate;
+ codec_config_.sample_rate = codec_user_config_.sample_rate;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_96000) {
+ result_config_cie.sampleRate = A2DP_AAC_SAMPLING_FREQ_96000;
+ codec_capability_.sample_rate = codec_user_config_.sample_rate;
+ codec_config_.sample_rate = codec_user_config_.sample_rate;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
+ codec_capability_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+ codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+ break;
+ }
+
+ // Select the sample frequency if there is no user preference
+ do {
+ if (codec_config_.sample_rate != BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) break;
+
+ // Compute the common capability
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_44100)
+ codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_48000)
+ codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_88200)
+ codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_88200;
+ if (sampleRate & A2DP_AAC_SAMPLING_FREQ_96000)
+ codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_96000;
+
+ // No user preference - try the codec audio config
+ if (select_audio_sample_rate(&codec_audio_config_, sampleRate,
+ &result_config_cie, &codec_config_)) {
+ break;
+ }
+
+ // No user preference - try the default config
+ if (select_best_sample_rate(
+ a2dp_aac_default_config.sampleRate & sink_info_cie.sampleRate,
+ &result_config_cie, &codec_config_)) {
+ break;
+ }
+
+ // No user preference - use the best match
+ if (select_best_sample_rate(sampleRate, &result_config_cie,
+ &codec_config_)) {
+ break;
+ }
+ } while (false);
+ if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot match sample frequency: source caps = 0x%x "
+ "sink info = 0x%x",
+ __func__, a2dp_aac_caps.sampleRate, sink_info_cie.sampleRate);
+ goto fail;
+ }
+
+ //
+ // Select the bits per sample
+ //
+ // NOTE: this information is NOT included in the AAC A2DP codec description
+ // that is sent OTA.
+ bits_per_sample = a2dp_aac_caps.bits_per_sample;
+ codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+ switch (codec_user_config_.bits_per_sample) {
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) {
+ result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample;
+ codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample;
+ codec_config_.bits_per_sample = codec_user_config_.bits_per_sample;
+ }
+ break;
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) {
+ result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample;
+ codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample;
+ codec_config_.bits_per_sample = codec_user_config_.bits_per_sample;
+ }
+ break;
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+ if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) {
+ result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample;
+ codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample;
+ codec_config_.bits_per_sample = codec_user_config_.bits_per_sample;
+ }
+ break;
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+ result_config_cie.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+ codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+ codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+ break;
+ }
+
+ // Select the bits per sample if there is no user preference
+ do {
+ if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE)
+ break;
+
+ // Compute the common capability
+ codec_capability_.bits_per_sample = bits_per_sample;
+
+ // No user preference - the the codec audio config
+ if (select_audio_bits_per_sample(&codec_audio_config_,
+ a2dp_aac_caps.bits_per_sample,
+ &result_config_cie, &codec_config_)) {
+ break;
+ }
+
+ // No user preference - try the default config
+ if (select_best_bits_per_sample(a2dp_aac_default_config.bits_per_sample,
+ &result_config_cie, &codec_config_)) {
+ break;
+ }
+
+ // No user preference - use the best match
+ if (select_best_bits_per_sample(a2dp_aac_caps.bits_per_sample,
+ &result_config_cie, &codec_config_)) {
+ break;
+ }
+ } while (false);
+ if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot match bits per sample: default = 0x%x "
+ "user preference = 0x%x",
+ __func__, a2dp_aac_default_config.bits_per_sample,
+ codec_user_config_.bits_per_sample);
+ goto fail;
+ }
+
+ //
+ // Select the channel mode
+ //
+ channelMode = a2dp_aac_caps.channelMode & sink_info_cie.channelMode;
+ codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+ switch (codec_user_config_.channel_mode) {
+ case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+ if (channelMode & A2DP_AAC_CHANNEL_MODE_MONO) {
+ result_config_cie.channelMode = A2DP_AAC_CHANNEL_MODE_MONO;
+ codec_capability_.channel_mode = codec_user_config_.channel_mode;
+ codec_config_.channel_mode = codec_user_config_.channel_mode;
+ }
+ break;
+ case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+ if (channelMode & A2DP_AAC_CHANNEL_MODE_STEREO) {
+ result_config_cie.channelMode = A2DP_AAC_CHANNEL_MODE_STEREO;
+ codec_capability_.channel_mode = codec_user_config_.channel_mode;
+ codec_config_.channel_mode = codec_user_config_.channel_mode;
+ }
+ break;
+ case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
+ codec_capability_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+ codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+ break;
+ }
+
+ // Select the channel mode if there is no user preference
+ do {
+ if (codec_config_.channel_mode != BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) break;
+
+ // Compute the common capability
+ if (channelMode & A2DP_AAC_CHANNEL_MODE_MONO)
+ codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+ if (channelMode & A2DP_AAC_CHANNEL_MODE_STEREO) {
+ codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+ }
+
+ // No user preference - try the codec audio config
+ if (select_audio_channel_mode(&codec_audio_config_, channelMode,
+ &result_config_cie, &codec_config_)) {
+ break;
+ }
+
+ // No user preference - try the default config
+ if (select_best_channel_mode(
+ a2dp_aac_default_config.channelMode & sink_info_cie.channelMode,
+ &result_config_cie, &codec_config_)) {
+ break;
+ }
+
+ // No user preference - use the best match
+ if (select_best_channel_mode(channelMode, &result_config_cie,
+ &codec_config_)) {
+ break;
+ }
+ } while (false);
+ if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot match channel mode: source caps = 0x%x "
+ "sink info = 0x%x",
+ __func__, a2dp_aac_caps.channelMode, sink_info_cie.channelMode);
+ goto fail;
+ }
+
+ if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+ p_result_codec_config) != A2DP_SUCCESS) {
+ goto fail;
+ }
+
+ //
+ // Copy the codec-specific fields if they are not zero
+ //
+ if (codec_user_config_.codec_specific_1 != 0)
+ codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+ if (codec_user_config_.codec_specific_2 != 0)
+ codec_config_.codec_specific_2 = codec_user_config_.codec_specific_2;
+ if (codec_user_config_.codec_specific_3 != 0)
+ codec_config_.codec_specific_3 = codec_user_config_.codec_specific_3;
+ if (codec_user_config_.codec_specific_4 != 0)
+ codec_config_.codec_specific_4 = codec_user_config_.codec_specific_4;
+
+ // Create a local copy of the peer codec capability, and the
+ // result codec config.
+ if (is_capability) {
+ status = A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+ ota_codec_peer_capability_);
+ } else {
+ status = A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+ ota_codec_peer_config_);
+ }
+ CHECK(status == A2DP_SUCCESS);
+ status = A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+ ota_codec_config_);
+ CHECK(status == A2DP_SUCCESS);
+ return true;
+
+fail:
+ // Restore the internal state
+ codec_config_ = saved_codec_config;
+ codec_capability_ = saved_codec_capability;
+ codec_user_config_ = saved_codec_user_config;
+ codec_audio_config_ = saved_codec_audio_config;
+ memcpy(ota_codec_config_, saved_ota_codec_config, sizeof(ota_codec_config_));
+ memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability,
+ sizeof(ota_codec_peer_capability_));
+ memcpy(ota_codec_peer_config_, saved_ota_codec_peer_config,
+ sizeof(ota_codec_peer_config_));
+ return false;
+}
diff --git a/stack/a2dp/a2dp_aac_encoder.cc b/stack/a2dp/a2dp_aac_encoder.cc
new file mode 100644
index 0000000..cdf5156
--- /dev/null
+++ b/stack/a2dp/a2dp_aac_encoder.cc
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "a2dp_aac_encoder"
+
+#include "a2dp_aac_encoder.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <aacenc_lib.h>
+#include <base/logging.h>
+
+#include "a2dp_aac.h"
+#include "bt_common.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+//
+// Encoder for AAC Source Codec
+//
+
+#define STATS_UPDATE_MAX(current_value_storage, new_value) \
+ do { \
+ if ((new_value) > (current_value_storage)) \
+ (current_value_storage) = (new_value); \
+ } while (0)
+
+// A2DP AAC encoder interval in milliseconds
+#define A2DP_AAC_ENCODER_INTERVAL_MS 20
+
+// offset
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+#define A2DP_AAC_OFFSET (AVDT_MEDIA_OFFSET + 1)
+#else
+#define A2DP_AAC_OFFSET AVDT_MEDIA_OFFSET
+#endif
+
+typedef struct {
+ uint32_t sample_rate;
+ uint8_t channel_mode;
+ uint8_t bits_per_sample;
+ uint32_t frame_length; // Samples per channel in a frame
+ uint8_t input_channels_n; // Number of channels
+ int max_encoded_buffer_bytes; // Max encoded bytes per frame
+} tA2DP_AAC_ENCODER_PARAMS;
+
+typedef struct {
+ uint32_t counter;
+ uint32_t bytes_per_tick; /* pcm bytes read each media task tick */
+ uint64_t last_frame_us;
+} tA2DP_AAC_FEEDING_STATE;
+
+typedef struct {
+ uint64_t session_start_us;
+
+ size_t media_read_total_expected_packets;
+ size_t media_read_total_expected_reads_count;
+ size_t media_read_total_expected_read_bytes;
+
+ size_t media_read_total_dropped_packets;
+ size_t media_read_total_actual_reads_count;
+ size_t media_read_total_actual_read_bytes;
+} a2dp_aac_encoder_stats_t;
+
+typedef struct {
+ a2dp_source_read_callback_t read_callback;
+ a2dp_source_enqueue_callback_t enqueue_callback;
+ uint16_t TxAaMtuSize;
+
+ bool use_SCMS_T;
+ bool is_peer_edr; // True if the peer device supports EDR
+ bool peer_supports_3mbps; // True if the peer device supports 3Mbps EDR
+ uint16_t peer_mtu; // MTU of the A2DP peer
+ uint32_t timestamp; // Timestamp for the A2DP frames
+
+ HANDLE_AACENCODER aac_handle;
+ bool has_aac_handle; // True if aac_handle is valid
+
+ tA2DP_FEEDING_PARAMS feeding_params;
+ tA2DP_AAC_ENCODER_PARAMS aac_encoder_params;
+ tA2DP_AAC_FEEDING_STATE aac_feeding_state;
+
+ a2dp_aac_encoder_stats_t stats;
+} tA2DP_AAC_ENCODER_CB;
+
+static tA2DP_AAC_ENCODER_CB a2dp_aac_encoder_cb;
+
+static void a2dp_aac_encoder_update(uint16_t peer_mtu,
+ A2dpCodecConfig* a2dp_codec_config,
+ bool* p_restart_input,
+ bool* p_restart_output,
+ bool* p_config_updated);
+static void a2dp_aac_get_num_frame_iteration(uint8_t* num_of_iterations,
+ uint8_t* num_of_frames,
+ uint64_t timestamp_us);
+static void a2dp_aac_encode_frames(uint8_t nb_frame);
+static bool a2dp_aac_read_feeding(uint8_t* read_buffer);
+
+bool A2DP_LoadEncoderAac(void) {
+ // Nothing to do - the library is statically linked
+ return true;
+}
+
+void A2DP_UnloadEncoderAac(void) {
+ // Nothing to do - the library is statically linked
+ if (a2dp_aac_encoder_cb.has_aac_handle)
+ aacEncClose(&a2dp_aac_encoder_cb.aac_handle);
+ memset(&a2dp_aac_encoder_cb, 0, sizeof(a2dp_aac_encoder_cb));
+}
+
+void a2dp_aac_encoder_init(const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+ A2dpCodecConfig* a2dp_codec_config,
+ a2dp_source_read_callback_t read_callback,
+ a2dp_source_enqueue_callback_t enqueue_callback) {
+ if (a2dp_aac_encoder_cb.has_aac_handle)
+ aacEncClose(&a2dp_aac_encoder_cb.aac_handle);
+ memset(&a2dp_aac_encoder_cb, 0, sizeof(a2dp_aac_encoder_cb));
+
+ a2dp_aac_encoder_cb.stats.session_start_us = time_get_os_boottime_us();
+
+ a2dp_aac_encoder_cb.read_callback = read_callback;
+ a2dp_aac_encoder_cb.enqueue_callback = enqueue_callback;
+ a2dp_aac_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+ a2dp_aac_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+ a2dp_aac_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+ a2dp_aac_encoder_cb.timestamp = 0;
+
+ a2dp_aac_encoder_cb.use_SCMS_T = false; // TODO: should be a parameter
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+ a2dp_aac_encoder_cb.use_SCMS_T = true;
+#endif
+
+ // NOTE: Ignore the restart_input / restart_output flags - this initization
+ // happens when the connection is (re)started.
+ bool restart_input = false;
+ bool restart_output = false;
+ bool config_updated = false;
+ a2dp_aac_encoder_update(a2dp_aac_encoder_cb.peer_mtu, a2dp_codec_config,
+ &restart_input, &restart_output, &config_updated);
+}
+
+bool A2dpCodecConfigAac::updateEncoderUserConfig(
+ const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input,
+ bool* p_restart_output, bool* p_config_updated) {
+ a2dp_aac_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+ a2dp_aac_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+ a2dp_aac_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+ a2dp_aac_encoder_cb.timestamp = 0;
+
+ if (a2dp_aac_encoder_cb.peer_mtu == 0) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot update the codec encoder for %s: "
+ "invalid peer MTU",
+ __func__, name().c_str());
+ return false;
+ }
+
+ a2dp_aac_encoder_update(a2dp_aac_encoder_cb.peer_mtu, this, p_restart_input,
+ p_restart_output, p_config_updated);
+ return true;
+}
+
+// Update the A2DP AAC encoder.
+// |peer_mtu| is the peer MTU.
+// |a2dp_codec_config| is the A2DP codec to use for the update.
+static void a2dp_aac_encoder_update(uint16_t peer_mtu,
+ A2dpCodecConfig* a2dp_codec_config,
+ bool* p_restart_input,
+ bool* p_restart_output,
+ bool* p_config_updated) {
+ tA2DP_AAC_ENCODER_PARAMS* p_encoder_params =
+ &a2dp_aac_encoder_cb.aac_encoder_params;
+ uint8_t codec_info[AVDT_CODEC_SIZE];
+ AACENC_ERROR aac_error;
+ int aac_param_value;
+
+ *p_restart_input = false;
+ *p_restart_output = false;
+ *p_config_updated = false;
+
+ if (!a2dp_aac_encoder_cb.has_aac_handle) {
+ AACENC_ERROR aac_error = aacEncOpen(&a2dp_aac_encoder_cb.aac_handle, 0,
+ 2 /* max 2 channels: stereo */);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG, "%s: Cannot open AAC encoder handle: AAC error 0x%x",
+ __func__, aac_error);
+ return; // TODO: Return an error?
+ }
+ a2dp_aac_encoder_cb.has_aac_handle = true;
+ }
+
+ if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot update the codec encoder for %s: "
+ "invalid codec config",
+ __func__, a2dp_codec_config->name().c_str());
+ return;
+ }
+ const uint8_t* p_codec_info = codec_info;
+
+ // The feeding parameters
+ tA2DP_FEEDING_PARAMS* p_feeding_params = &a2dp_aac_encoder_cb.feeding_params;
+ p_feeding_params->sample_rate = A2DP_GetTrackSampleRateAac(p_codec_info);
+ p_feeding_params->bits_per_sample =
+ a2dp_codec_config->getAudioBitsPerSample();
+ p_feeding_params->channel_count = A2DP_GetTrackChannelCountAac(p_codec_info);
+ LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
+ __func__, p_feeding_params->sample_rate,
+ p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+
+ // The codec parameters
+ p_encoder_params->sample_rate =
+ a2dp_aac_encoder_cb.feeding_params.sample_rate;
+ p_encoder_params->channel_mode = A2DP_GetChannelModeCodeAac(p_codec_info);
+
+ uint16_t mtu_size = BT_DEFAULT_BUFFER_SIZE - A2DP_AAC_OFFSET - sizeof(BT_HDR);
+ if (mtu_size < peer_mtu) {
+ a2dp_aac_encoder_cb.TxAaMtuSize = mtu_size;
+ } else {
+ a2dp_aac_encoder_cb.TxAaMtuSize = peer_mtu;
+ }
+
+ LOG_DEBUG(LOG_TAG, "%s: MTU=%d, peer_mtu=%d", __func__,
+ a2dp_aac_encoder_cb.TxAaMtuSize, peer_mtu);
+ LOG_DEBUG(LOG_TAG, "%s: sample_rate: %d channel_mode: %d ", __func__,
+ p_encoder_params->sample_rate, p_encoder_params->channel_mode);
+
+ // Set the encoder's parameters: Audio Object Type - MANDATORY
+ // A2DP_AAC_OBJECT_TYPE_MPEG2_LC -> AOT_AAC_LC
+ // A2DP_AAC_OBJECT_TYPE_MPEG4_LC -> AOT_AAC_LC
+ // A2DP_AAC_OBJECT_TYPE_MPEG4_LTP -> AOT_AAC_LTP
+ // A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE -> AOT_AAC_SCAL
+ aac_param_value = AOT_AAC_LC;
+ int object_type = A2DP_GetObjectTypeCodeAac(p_codec_info);
+ switch (object_type) {
+ case A2DP_AAC_OBJECT_TYPE_MPEG2_LC:
+ aac_param_value = AOT_AAC_LC;
+ break;
+ case A2DP_AAC_OBJECT_TYPE_MPEG4_LC:
+ aac_param_value = AOT_AAC_LC;
+ break;
+ case A2DP_AAC_OBJECT_TYPE_MPEG4_LTP:
+ aac_param_value = AOT_AAC_LTP;
+ break;
+ case A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE:
+ aac_param_value = AOT_AAC_SCAL;
+ break;
+ default:
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_AOT: "
+ "invalid object type %d",
+ __func__, object_type);
+ return; // TODO: Return an error?
+ }
+ aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle, AACENC_AOT,
+ aac_param_value);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_AOT to %d: "
+ "AAC error 0x%x",
+ __func__, aac_param_value, aac_error);
+ return; // TODO: Return an error?
+ }
+
+ // Set the encoder's parameters: Bit Rate - MANDATORY
+ aac_param_value = A2DP_GetBitRateAac(p_codec_info);
+ if (aac_param_value == -1) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_BITRATE: "
+ "invalid codec bit rate",
+ __func__);
+ return; // TODO: Return an error?
+ }
+ aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
+ AACENC_BITRATE, aac_param_value);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_BITRATE to %d: "
+ "AAC error 0x%x",
+ __func__, aac_param_value, aac_error);
+ return; // TODO: Return an error?
+ }
+
+ // Set the encoder's parameters: Sample Rate - MANDATORY
+ aac_param_value = A2DP_GetTrackSampleRateAac(p_codec_info);
+ aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
+ AACENC_SAMPLERATE, aac_param_value);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_SAMPLERATE to %d: "
+ "AAC error 0x%x",
+ __func__, aac_param_value, aac_error);
+ return; // TODO: Return an error?
+ }
+
+ // Set the encoder's parameters: Channel Mode - MANDATORY
+ if (A2DP_GetTrackChannelCountAac(p_codec_info) == 1) {
+ aac_param_value = MODE_1; // Mono
+ } else {
+ aac_param_value = MODE_2; // Stereo
+ }
+ aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
+ AACENC_CHANNELMODE, aac_param_value);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_CHANNELMODE to %d: "
+ "AAC error 0x%x",
+ __func__, aac_param_value, aac_error);
+ return; // TODO: Return an error?
+ }
+
+ // Set the encoder's parameters: Transport Type
+ aac_param_value = TT_MP4_LATM_MCP1; // muxConfigPresent = 1
+ aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
+ AACENC_TRANSMUX, aac_param_value);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_TRANSMUX to %d: "
+ "AAC error 0x%x",
+ __func__, aac_param_value, aac_error);
+ return; // TODO: Return an error?
+ }
+
+ // Set the encoder's parameters: Header Period
+ aac_param_value = 1;
+ aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
+ AACENC_HEADER_PERIOD, aac_param_value);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_HEADER_PERIOD to %d: "
+ "AAC error 0x%x",
+ __func__, aac_param_value, aac_error);
+ return; // TODO: Return an error?
+ }
+
+ // Set the encoder's parameters: Variable Bit Rate Support
+ aac_param_value = A2DP_GetVariableBitRateSupportAac(p_codec_info);
+ if (aac_param_value == -1) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_BITRATEMODE: "
+ "invalid codec bit rate mode",
+ __func__);
+ return; // TODO: Return an error?
+ }
+ aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
+ AACENC_BITRATEMODE, aac_param_value);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot set AAC parameter AACENC_BITRATEMODE to %d: "
+ "AAC error 0x%x",
+ __func__, aac_param_value, aac_error);
+ return; // TODO: Return an error?
+ }
+
+ // Mark the end of setting the encoder's parameters
+ aac_error =
+ aacEncEncode(a2dp_aac_encoder_cb.aac_handle, NULL, NULL, NULL, NULL);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot complete setting the AAC parameters: AAC error 0x%x",
+ __func__, aac_error);
+ return; // TODO: Return an error?
+ }
+
+ // Retrieve the encoder info so we can save the frame length
+ AACENC_InfoStruct aac_info;
+ aac_error = aacEncInfo(a2dp_aac_encoder_cb.aac_handle, &aac_info);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG,
+ "%s: Cannot retrieve the AAC encoder info: AAC error 0x%x",
+ __func__, aac_error);
+ return; // TODO: Return an error?
+ }
+ p_encoder_params->frame_length = aac_info.frameLength;
+ p_encoder_params->input_channels_n = aac_info.inputChannels;
+ p_encoder_params->max_encoded_buffer_bytes = aac_info.maxOutBufBytes;
+ LOG_DEBUG(LOG_TAG,
+ "%s: AAC frame_length = %u input_channels_n = %u "
+ "max_encoded_buffer_bytes = %d",
+ __func__, p_encoder_params->frame_length,
+ p_encoder_params->input_channels_n,
+ p_encoder_params->max_encoded_buffer_bytes);
+}
+
+void a2dp_aac_encoder_cleanup(void) {
+ if (a2dp_aac_encoder_cb.has_aac_handle)
+ aacEncClose(&a2dp_aac_encoder_cb.aac_handle);
+ memset(&a2dp_aac_encoder_cb, 0, sizeof(a2dp_aac_encoder_cb));
+}
+
+void a2dp_aac_feeding_reset(void) {
+ /* By default, just clear the entire state */
+ memset(&a2dp_aac_encoder_cb.aac_feeding_state, 0,
+ sizeof(a2dp_aac_encoder_cb.aac_feeding_state));
+
+ a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick =
+ (a2dp_aac_encoder_cb.feeding_params.sample_rate *
+ a2dp_aac_encoder_cb.feeding_params.bits_per_sample / 8 *
+ a2dp_aac_encoder_cb.feeding_params.channel_count *
+ A2DP_AAC_ENCODER_INTERVAL_MS) /
+ 1000;
+
+ LOG_DEBUG(LOG_TAG, "%s: PCM bytes per tick %u", __func__,
+ a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick);
+}
+
+void a2dp_aac_feeding_flush(void) {
+ a2dp_aac_encoder_cb.aac_feeding_state.counter = 0;
+}
+
+period_ms_t a2dp_aac_get_encoder_interval_ms(void) {
+ return A2DP_AAC_ENCODER_INTERVAL_MS;
+}
+
+void a2dp_aac_send_frames(uint64_t timestamp_us) {
+ uint8_t nb_frame = 0;
+ uint8_t nb_iterations = 0;
+
+ a2dp_aac_get_num_frame_iteration(&nb_iterations, &nb_frame, timestamp_us);
+ LOG_VERBOSE(LOG_TAG, "%s: Sending %d frames per iteration, %d iterations",
+ __func__, nb_frame, nb_iterations);
+ if (nb_frame == 0) return;
+
+ for (uint8_t counter = 0; counter < nb_iterations; counter++) {
+ // Transcode frame and enqueue
+ a2dp_aac_encode_frames(nb_frame);
+ }
+}
+
+// Obtains the number of frames to send and number of iterations
+// to be used. |num_of_iterations| and |num_of_frames| parameters
+// are used as output param for returning the respective values.
+static void a2dp_aac_get_num_frame_iteration(uint8_t* num_of_iterations,
+ uint8_t* num_of_frames,
+ uint64_t timestamp_us) {
+ uint32_t result = 0;
+ uint8_t nof = 0;
+ uint8_t noi = 1;
+
+ uint32_t pcm_bytes_per_frame =
+ a2dp_aac_encoder_cb.aac_encoder_params.frame_length *
+ a2dp_aac_encoder_cb.feeding_params.channel_count *
+ a2dp_aac_encoder_cb.feeding_params.bits_per_sample / 8;
+ LOG_VERBOSE(LOG_TAG, "%s: pcm_bytes_per_frame %u", __func__,
+ pcm_bytes_per_frame);
+
+ uint32_t us_this_tick = A2DP_AAC_ENCODER_INTERVAL_MS * 1000;
+ uint64_t now_us = timestamp_us;
+ if (a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us != 0)
+ us_this_tick =
+ (now_us - a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us);
+ a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us = now_us;
+
+ a2dp_aac_encoder_cb.aac_feeding_state.counter +=
+ a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick * us_this_tick /
+ (A2DP_AAC_ENCODER_INTERVAL_MS * 1000);
+
+#if 1
+ result = a2dp_aac_encoder_cb.aac_feeding_state.counter / pcm_bytes_per_frame;
+ a2dp_aac_encoder_cb.aac_feeding_state.counter -= result * pcm_bytes_per_frame;
+ nof = result;
+
+ LOG_VERBOSE(LOG_TAG, "%s: effective num of frames %u, iterations %u",
+ __func__, nof, noi);
+
+ *num_of_frames = nof;
+ *num_of_iterations = noi;
+
+#else
+ pcm_bytes_per_frame = 800;
+ result = a2dp_aac_encoder_cb.aac_feeding_state.counter / pcm_bytes_per_frame;
+ nof = result;
+ noi = 1;
+
+ a2dp_aac_encoder_cb.aac_feeding_state.counter -= result * pcm_bytes_per_frame;
+
+ LOG_VERBOSE(LOG_TAG, "%s: effective num of frames %u, iterations %u",
+ __func__, nof, noi);
+
+ *num_of_frames = nof;
+ *num_of_iterations = noi;
+
+#endif
+}
+
+static void a2dp_aac_encode_frames(uint8_t nb_frame) {
+ tA2DP_AAC_ENCODER_PARAMS* p_encoder_params =
+ &a2dp_aac_encoder_cb.aac_encoder_params;
+ tA2DP_FEEDING_PARAMS* p_feeding_params = &a2dp_aac_encoder_cb.feeding_params;
+ uint8_t remain_nb_frame = nb_frame;
+ uint8_t read_buffer[BT_DEFAULT_BUFFER_SIZE];
+ int pcm_bytes_per_frame = p_encoder_params->frame_length *
+ p_feeding_params->channel_count *
+ p_feeding_params->bits_per_sample / 8;
+ CHECK(pcm_bytes_per_frame <= static_cast<int>(sizeof(read_buffer)));
+
+ // Setup the input buffer
+ AACENC_BufDesc in_buf_desc;
+ void* in_buf_vector[1] = {nullptr};
+ int in_buf_identifiers[1] = {IN_AUDIO_DATA};
+ int in_buf_sizes[1] = {pcm_bytes_per_frame};
+ int in_buf_element_sizes[1] = {p_feeding_params->bits_per_sample / 8};
+ in_buf_desc.numBufs = 1;
+ in_buf_desc.bufs = in_buf_vector;
+ in_buf_desc.bufferIdentifiers = in_buf_identifiers;
+ in_buf_desc.bufSizes = in_buf_sizes;
+ in_buf_desc.bufElSizes = in_buf_element_sizes;
+
+ // Setup the output buffer (partially)
+ AACENC_BufDesc out_buf_desc;
+ void* out_buf_vector[1] = {nullptr};
+ int out_buf_identifiers[1] = {OUT_BITSTREAM_DATA};
+ int out_buf_sizes[1] = {p_encoder_params->max_encoded_buffer_bytes};
+ // NOTE: out_buf_element_sizes below is probably unused by the encoder
+ int out_buf_element_sizes[1] = {p_feeding_params->bits_per_sample / 8};
+ out_buf_desc.numBufs = 1;
+ out_buf_desc.bufs = out_buf_vector;
+ out_buf_desc.bufferIdentifiers = out_buf_identifiers;
+ out_buf_desc.bufSizes = out_buf_sizes;
+ out_buf_desc.bufElSizes = out_buf_element_sizes;
+ CHECK(p_encoder_params->max_encoded_buffer_bytes <=
+ static_cast<int>(BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR)));
+
+ AACENC_InArgs aac_in_args;
+ aac_in_args.numInSamples =
+ p_encoder_params->frame_length * p_feeding_params->channel_count;
+ aac_in_args.numAncBytes = 0;
+
+ AACENC_OutArgs aac_out_args = {
+ .numOutBytes = 0, .numInSamples = 0, .numAncBytes = 0};
+
+ uint32_t count;
+ int written = 0;
+
+ while (nb_frame) {
+ BT_HDR* p_buf = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
+
+ /* Init buffer */
+ p_buf->offset = A2DP_AAC_OFFSET;
+ p_buf->len = 0;
+ p_buf->layer_specific = 0;
+
+ count = 0;
+ do {
+ /* Read PCM data */
+ if (a2dp_aac_read_feeding(read_buffer)) {
+ uint8_t* packet = (uint8_t*)(p_buf + 1) + p_buf->offset + p_buf->len;
+ if (!a2dp_aac_encoder_cb.has_aac_handle) {
+ LOG_ERROR(LOG_TAG, "%s: invalid AAC handle", __func__);
+ osi_free(p_buf);
+ return;
+ }
+ in_buf_vector[0] = read_buffer;
+ out_buf_vector[0] = packet + count;
+ AACENC_ERROR aac_error =
+ aacEncEncode(a2dp_aac_encoder_cb.aac_handle, &in_buf_desc,
+ &out_buf_desc, &aac_in_args, &aac_out_args);
+ if (aac_error != AACENC_OK) {
+ LOG_ERROR(LOG_TAG, "%s: AAC encoding error: 0x%x", __func__,
+ aac_error);
+ osi_free(p_buf);
+ return;
+ }
+ written = aac_out_args.numOutBytes;
+ count += written;
+ p_buf->len += written;
+ nb_frame--;
+ p_buf->layer_specific++; // added a frame to the buffer
+ } else {
+ LOG_WARN(LOG_TAG, "%s: underflow %d", __func__, nb_frame);
+ a2dp_aac_encoder_cb.aac_feeding_state.counter +=
+ nb_frame * p_encoder_params->frame_length *
+ p_feeding_params->channel_count *
+ p_feeding_params->bits_per_sample / 8;
+
+ // no more pcm to read
+ nb_frame = 0;
+ }
+ } while ((written == 0) && nb_frame);
+
+ // NOTE: We don't check whether the packet will fit in the MTU,
+ // because AAC doesn't give us control over the encoded frame size.
+ // If the packet is larger than the MTU, it will be fragmented before
+ // transmission.
+ if (p_buf->len) {
+ /*
+ * Timestamp of the media packet header represent the TS of the
+ * first frame, i.e the timestamp before including this frame.
+ */
+ *((uint32_t*)(p_buf + 1)) = a2dp_aac_encoder_cb.timestamp;
+
+ a2dp_aac_encoder_cb.timestamp +=
+ p_buf->layer_specific * p_encoder_params->frame_length;
+
+ uint8_t done_nb_frame = remain_nb_frame - nb_frame;
+ remain_nb_frame = nb_frame;
+ if (!a2dp_aac_encoder_cb.enqueue_callback(p_buf, done_nb_frame)) return;
+ } else {
+ osi_free(p_buf);
+ }
+ }
+}
+
+static bool a2dp_aac_read_feeding(uint8_t* read_buffer) {
+ uint32_t read_size = a2dp_aac_encoder_cb.aac_encoder_params.frame_length *
+ a2dp_aac_encoder_cb.feeding_params.channel_count *
+ a2dp_aac_encoder_cb.feeding_params.bits_per_sample / 8;
+
+ /* Read Data from UIPC channel */
+ uint32_t nb_byte_read =
+ a2dp_aac_encoder_cb.read_callback(read_buffer, read_size);
+
+ if (nb_byte_read < read_size) {
+ if (nb_byte_read == 0) return false;
+
+ /* Fill the unfilled part of the read buffer with silence (0) */
+ memset(((uint8_t*)read_buffer) + nb_byte_read, 0, read_size - nb_byte_read);
+ nb_byte_read = read_size;
+ }
+ return true;
+}
+
+void a2dp_aac_debug_codec_dump(int fd) {
+ a2dp_aac_encoder_stats_t* stats = &a2dp_aac_encoder_cb.stats;
+
+ dprintf(fd, "\nA2DP AAC State:\n");
+
+ dprintf(fd,
+ " Packets expected/dropped : %zu / "
+ "%zu\n",
+ stats->media_read_total_expected_packets,
+ stats->media_read_total_dropped_packets);
+
+ dprintf(fd,
+ " PCM reads count expected/actual : %zu / "
+ "%zu\n",
+ stats->media_read_total_expected_reads_count,
+ stats->media_read_total_actual_reads_count);
+
+ dprintf(fd,
+ " PCM read bytes expected/actual : %zu / "
+ "%zu\n",
+ stats->media_read_total_expected_read_bytes,
+ stats->media_read_total_actual_read_bytes);
+}
diff --git a/stack/a2dp/a2dp_api.cc b/stack/a2dp/a2dp_api.cc
index b8fe869..a52924a 100644
--- a/stack/a2dp/a2dp_api.cc
+++ b/stack/a2dp/a2dp_api.cc
@@ -348,16 +348,10 @@
* A2DP_SET_ZERO_BIT, if all bits clear
* A2DP_SET_MULTL_BIT, if multiple bits are set
*****************************************************************************/
-uint8_t A2DP_BitsSet(uint8_t num) {
- uint8_t count;
- uint8_t res;
- if (num == 0)
- res = A2DP_SET_ZERO_BIT;
- else {
- count = (num & (num - 1));
- res = ((count == 0) ? A2DP_SET_ONE_BIT : A2DP_SET_MULTL_BIT);
- }
- return res;
+uint8_t A2DP_BitsSet(uint64_t num) {
+ if (num == 0) return A2DP_SET_ZERO_BIT;
+ if ((num & (num - 1)) == 0) return A2DP_SET_ONE_BIT;
+ return A2DP_SET_MULTL_BIT;
}
/*******************************************************************************
diff --git a/stack/a2dp/a2dp_codec_config.cc b/stack/a2dp/a2dp_codec_config.cc
index 69659ce..fb86eee 100644
--- a/stack/a2dp/a2dp_codec_config.cc
+++ b/stack/a2dp/a2dp_codec_config.cc
@@ -24,6 +24,7 @@
#include <base/logging.h>
+#include "a2dp_aac.h"
#include "a2dp_sbc.h"
#include "a2dp_vendor.h"
#include "a2dp_vendor_aptx.h"
@@ -85,6 +86,9 @@
case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
codec_config = new A2dpCodecConfigSbcSink();
break;
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+ codec_config = new A2dpCodecConfigAac();
+ break;
case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
codec_config = new A2dpCodecConfigAptx();
break;
@@ -550,6 +554,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_IsSourceCodecValidSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_IsSourceCodecValidAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_IsVendorSourceCodecValid(p_codec_info);
default:
@@ -567,6 +573,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_IsSinkCodecValidSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_IsSinkCodecValidAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_IsVendorSinkCodecValid(p_codec_info);
default:
@@ -584,6 +592,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_IsPeerSourceCodecValidSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_IsPeerSourceCodecValidAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_IsVendorPeerSourceCodecValid(p_codec_info);
default:
@@ -601,6 +611,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_IsPeerSinkCodecValidSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_IsPeerSinkCodecValidAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_IsVendorPeerSinkCodecValid(p_codec_info);
default:
@@ -618,6 +630,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_IsSinkCodecSupportedSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_IsSinkCodecSupportedAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_IsVendorSinkCodecSupported(p_codec_info);
default:
@@ -636,6 +650,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_IsPeerSourceCodecSupportedSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_IsPeerSourceCodecSupportedAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_IsVendorPeerSourceCodecSupported(p_codec_info);
default:
@@ -659,6 +675,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_BuildSrc2SinkConfigSbc(p_src_cap, p_pref_cfg);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_BuildSrc2SinkConfigAac(p_src_cap, p_pref_cfg);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorBuildSrc2SinkConfig(p_src_cap, p_pref_cfg);
default:
@@ -691,6 +709,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_CodecNameSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_CodecNameAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorCodecName(p_codec_info);
default:
@@ -711,6 +731,8 @@
switch (codec_type_a) {
case A2DP_MEDIA_CT_SBC:
return A2DP_CodecTypeEqualsSbc(p_codec_info_a, p_codec_info_b);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_CodecTypeEqualsAac(p_codec_info_a, p_codec_info_b);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorCodecTypeEquals(p_codec_info_a, p_codec_info_b);
default:
@@ -731,6 +753,8 @@
switch (codec_type_a) {
case A2DP_MEDIA_CT_SBC:
return A2DP_CodecEqualsSbc(p_codec_info_a, p_codec_info_b);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_CodecEqualsAac(p_codec_info_a, p_codec_info_b);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorCodecEquals(p_codec_info_a, p_codec_info_b);
default:
@@ -749,6 +773,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_GetTrackSampleRateSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_GetTrackSampleRateAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorGetTrackSampleRate(p_codec_info);
default:
@@ -767,6 +793,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_GetTrackBitsPerSampleSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_GetTrackBitsPerSampleAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorGetTrackBitsPerSample(p_codec_info);
default:
@@ -785,6 +813,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_GetTrackChannelCountSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_GetTrackChannelCountAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorGetTrackChannelCount(p_codec_info);
default:
@@ -803,6 +833,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_GetSinkTrackChannelTypeSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_GetSinkTrackChannelTypeAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorGetSinkTrackChannelType(p_codec_info);
default:
@@ -823,6 +855,9 @@
case A2DP_MEDIA_CT_SBC:
return A2DP_GetSinkFramesCountToProcessSbc(time_interval_ms,
p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_GetSinkFramesCountToProcessAac(time_interval_ms,
+ p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorGetSinkFramesCountToProcess(time_interval_ms,
p_codec_info);
@@ -841,6 +876,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_GetPacketTimestampSbc(p_codec_info, p_data, p_timestamp);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_GetPacketTimestampAac(p_codec_info, p_data, p_timestamp);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorGetPacketTimestamp(p_codec_info, p_data, p_timestamp);
default:
@@ -858,6 +895,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_BuildCodecHeaderSbc(p_codec_info, p_buf, frames_per_packet);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_BuildCodecHeaderAac(p_codec_info, p_buf, frames_per_packet);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorBuildCodecHeader(p_codec_info, p_buf,
frames_per_packet);
@@ -878,6 +917,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_GetEncoderInterfaceSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_GetEncoderInterfaceAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorGetEncoderInterface(p_codec_info);
default:
@@ -894,6 +935,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_AdjustCodecSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_AdjustCodecAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorAdjustCodec(p_codec_info);
default:
@@ -912,6 +955,8 @@
switch (codec_type) {
case A2DP_MEDIA_CT_SBC:
return A2DP_SourceCodecIndexSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_SourceCodecIndexAac(p_codec_info);
case A2DP_MEDIA_CT_NON_A2DP:
return A2DP_VendorSourceCodecIndex(p_codec_info);
default:
@@ -928,6 +973,8 @@
return A2DP_CodecIndexStrSbc();
case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
return A2DP_CodecIndexStrSbcSink();
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+ return A2DP_CodecIndexStrAac();
default:
break;
}
@@ -952,6 +999,8 @@
return A2DP_InitCodecConfigSbc(p_cfg);
case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
return A2DP_InitCodecConfigSbcSink(p_cfg);
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+ return A2DP_InitCodecConfigAac(p_cfg);
default:
break;
}
diff --git a/stack/a2dp/a2dp_vendor.cc b/stack/a2dp/a2dp_vendor.cc
index a49d76f..e1d88b1 100644
--- a/stack/a2dp/a2dp_vendor.cc
+++ b/stack/a2dp/a2dp_vendor.cc
@@ -532,7 +532,8 @@
switch (codec_index) {
case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
- break; // This is not a vendor-specific codec
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+ break; // These are not vendor-specific codecs
case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
return A2DP_VendorCodecIndexStrAptx();
case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
@@ -553,7 +554,8 @@
switch (codec_index) {
case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
- break; // This is not a vendor-specific codec
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+ break; // These are not vendor-specific codecs
case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
return A2DP_VendorInitCodecConfigAptx(p_cfg);
case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
diff --git a/stack/include/a2dp_aac.h b/stack/include/a2dp_aac.h
new file mode 100644
index 0000000..42483c6
--- /dev/null
+++ b/stack/include/a2dp_aac.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// A2DP Codec API for AAC
+//
+
+#ifndef A2DP_AAC_H
+#define A2DP_AAC_H
+
+#include "a2dp_aac_constants.h"
+#include "a2dp_codec_api.h"
+#include "avdt_api.h"
+
+class A2dpCodecConfigAac : public A2dpCodecConfig {
+ public:
+ A2dpCodecConfigAac();
+ virtual ~A2dpCodecConfigAac();
+
+ bool init() override;
+ bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
+ uint8_t* p_result_codec_config) override;
+
+ private:
+ bool updateEncoderUserConfig(
+ const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+ bool* p_restart_input, bool* p_restart_output,
+ bool* p_config_updated) override;
+};
+
+// Checks whether the codec capabilities contain a valid A2DP AAC Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid AAC
+// codec, otherwise false.
+bool A2DP_IsSourceCodecValidAac(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid A2DP AAC Sink codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid AAC codec,
+// otherwise false.
+bool A2DP_IsSinkCodecValidAac(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP AAC Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid AAC codec,
+// otherwise false.
+bool A2DP_IsPeerSourceCodecValidAac(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP AAC Sink
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid AAC
+// codec, otherwise false.
+bool A2DP_IsPeerSinkCodecValidAac(const uint8_t* p_codec_info);
+
+// Checks whether A2DP AAC Sink codec is supported.
+// |p_codec_info| contains information about the codec capabilities.
+// Returns true if the A2DP AAC Sink codec is supported, otherwise false.
+bool A2DP_IsSinkCodecSupportedAac(const uint8_t* p_codec_info);
+
+// Checks whether an A2DP AAC Source codec for a peer Source device is
+// supported.
+// |p_codec_info| contains information about the codec capabilities of the
+// peer device.
+// Returns true if the A2DP AAC Source codec for a peer Source device is
+// supported, otherwise false.
+bool A2DP_IsPeerSourceCodecSupportedAac(const uint8_t* p_codec_info);
+
+// Checks whether the A2DP data packets should contain RTP header.
+// |content_protection_enabled| is true if Content Protection is
+// enabled. |p_codec_info| contains information about the codec capabilities.
+// Returns true if the A2DP data packets should contain RTP header, otherwise
+// false.
+bool A2DP_UsesRtpHeaderAac(bool content_protection_enabled,
+ const uint8_t* p_codec_info);
+
+// Builds A2DP preferred AAC Sink capability from AAC Source capability.
+// |p_src_cap| is the Source capability to use.
+// |p_pref_cfg| is the result Sink capability to store.
+// Returns |A2DP_SUCCESS| on success, otherwise the corresponding A2DP error
+// status code.
+tA2DP_STATUS A2DP_BuildSrc2SinkConfigAac(const uint8_t* p_src_cap,
+ uint8_t* p_pref_cfg);
+
+// Gets the A2DP AAC codec name for a given |p_codec_info|.
+const char* A2DP_CodecNameAac(const uint8_t* p_codec_info);
+
+// Checks whether two A2DP AAC codecs |p_codec_info_a| and |p_codec_info_b|
+// have the same type.
+// Returns true if the two codecs have the same type, otherwise false.
+bool A2DP_CodecTypeEqualsAac(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b);
+
+// Checks whether two A2DP AAC codecs |p_codec_info_a| and |p_codec_info_b|
+// are exactly the same.
+// Returns true if the two codecs are exactly the same, otherwise false.
+// If the codec type is not AAC, the return value is false.
+bool A2DP_CodecEqualsAac(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b);
+
+// Gets the track sample rate value for the A2DP AAC codec.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackSampleRateAac(const uint8_t* p_codec_info);
+
+// Gets the bits per audio sample for the A2DP AAC codec.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the bits per audio sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackBitsPerSampleAac(const uint8_t* p_codec_info);
+
+// Gets the channel count for the A2DP AAC codec.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackChannelCountAac(const uint8_t* p_codec_info);
+
+// Gets the channel type for the A2DP AAC Sink codec:
+// 1 for mono, or 3 for dual/stereo/joint.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the channel type on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetSinkTrackChannelTypeAac(const uint8_t* p_codec_info);
+
+// Computes the number of frames to process in a time window for the A2DP
+// AAC sink codec. |time_interval_ms| is the time interval (in milliseconds).
+// |p_codec_info| is a pointer to the codec_info to decode.
+// Returns the number of frames to process on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetSinkFramesCountToProcessAac(uint64_t time_interval_ms,
+ const uint8_t* p_codec_info);
+
+// Gets the object type code for the A2DP AAC codec.
+// The actual value is codec-specific - see |A2DP_AAC_OBJECT_TYPE_*|.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the object type code on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetObjectTypeCodeAac(const uint8_t* p_codec_info);
+
+// Gets the channel mode code for the A2DP AAC codec.
+// The actual value is codec-specific - see |A2DP_AAC_CHANNEL_MODE_*|.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the channel mode code on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetChannelModeCodeAac(const uint8_t* p_codec_info);
+
+// Gets the variable bit rate support for the A2DP AAC codec.
+// The actual value is codec-specific - see |A2DP_AAC_VARIABLE_BIT_RATE_*|.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the variable bit rate support on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetVariableBitRateSupportAac(const uint8_t* p_codec_info);
+
+// Gets the bit rate for the A2DP AAC codec.
+// The actual value is codec-specific - for AAC it is in bits per second.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the bit rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetBitRateAac(const uint8_t* p_codec_info);
+
+// Gets the A2DP AAC audio data timestamp from an audio packet.
+// |p_codec_info| contains the codec information.
+// |p_data| contains the audio data.
+// The timestamp is stored in |p_timestamp|.
+// Returns true on success, otherwise false.
+bool A2DP_GetPacketTimestampAac(const uint8_t* p_codec_info,
+ const uint8_t* p_data, uint32_t* p_timestamp);
+
+// Builds A2DP AAC codec header for audio data.
+// |p_codec_info| contains the codec information.
+// |p_buf| contains the audio data.
+// |frames_per_packet| is the number of frames in this packet.
+// Returns true on success, otherwise false.
+bool A2DP_BuildCodecHeaderAac(const uint8_t* p_codec_info, BT_HDR* p_buf,
+ uint16_t frames_per_packet);
+
+// Decodes and displays AAC codec info (for debugging).
+// |p_codec_info| is a pointer to the AAC codec_info to decode and display.
+void A2DP_DumpCodecInfoAac(const uint8_t* p_codec_info);
+
+// Gets the A2DP AAC encoder interface that can be used to encode and prepare
+// A2DP packets for transmission - see |tA2DP_ENCODER_INTERFACE|.
+// |p_codec_info| contains the codec information.
+// Returns the A2DP AAC encoder interface if the |p_codec_info| is valid and
+// supported, otherwise NULL.
+const tA2DP_ENCODER_INTERFACE* A2DP_GetEncoderInterfaceAac(
+ const uint8_t* p_codec_info);
+
+// Adjusts the A2DP AAC codec, based on local support and Bluetooth
+// specification.
+// |p_codec_info| contains the codec information to adjust.
+// Returns true if |p_codec_info| is valid and supported, otherwise false.
+bool A2DP_AdjustCodecAac(uint8_t* p_codec_info);
+
+// Gets the A2DP AAC Source codec index for a given |p_codec_info|.
+// Returns the corresponding |btav_a2dp_codec_index_t| on success,
+// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|.
+btav_a2dp_codec_index_t A2DP_SourceCodecIndexAac(const uint8_t* p_codec_info);
+
+// Gets the A2DP AAC Source codec name.
+const char* A2DP_CodecIndexStrAac(void);
+
+// Initializes A2DP AAC Source codec information into |tAVDT_CFG|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_InitCodecConfigAac(tAVDT_CFG* p_cfg);
+
+#endif // A2DP_AAC_H
diff --git a/stack/include/a2dp_aac_constants.h b/stack/include/a2dp_aac_constants.h
new file mode 100644
index 0000000..6102f92
--- /dev/null
+++ b/stack/include/a2dp_aac_constants.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// A2DP constants for AAC codec
+//
+
+#ifndef A2DP_AAC_CONSTANTS_H
+#define A2DP_AAC_CONSTANTS_H
+
+// AAC codec specific settings
+#define A2DP_AAC_CODEC_LEN 8
+
+// [Octet 0] Object Type
+#define A2DP_AAC_OBJECT_TYPE_MPEG2_LC 0x80 /* MPEG-2 Low Complexity */
+#define A2DP_AAC_OBJECT_TYPE_MPEG4_LC 0x40 /* MPEG-4 Low Complexity */
+#define A2DP_AAC_OBJECT_TYPE_MPEG4_LTP 0x20 /* MPEG-4 Long Term Prediction */
+#define A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE 0x10
+// [Octet 1] Sampling Frequency - 8000 to 44100
+#define A2DP_AAC_SAMPLING_FREQ_MASK0 0xFF
+#define A2DP_AAC_SAMPLING_FREQ_8000 0x80
+#define A2DP_AAC_SAMPLING_FREQ_11025 0x40
+#define A2DP_AAC_SAMPLING_FREQ_12000 0x20
+#define A2DP_AAC_SAMPLING_FREQ_16000 0x10
+#define A2DP_AAC_SAMPLING_FREQ_22050 0x08
+#define A2DP_AAC_SAMPLING_FREQ_24000 0x04
+#define A2DP_AAC_SAMPLING_FREQ_32000 0x02
+#define A2DP_AAC_SAMPLING_FREQ_44100 0x01
+// [Octet 2], [Bits 4-7] Sampling Frequency - 48000 to 96000
+// NOTE: Bits offset for the higher-order octet 16-bit integer
+#define A2DP_AAC_SAMPLING_FREQ_MASK1 (0xF0 << 8)
+#define A2DP_AAC_SAMPLING_FREQ_48000 (0x80 << 8)
+#define A2DP_AAC_SAMPLING_FREQ_64000 (0x40 << 8)
+#define A2DP_AAC_SAMPLING_FREQ_88200 (0x20 << 8)
+#define A2DP_AAC_SAMPLING_FREQ_96000 (0x10 << 8)
+// [Octet 2], [Bits 2-3] Channel Mode
+#define A2DP_AAC_CHANNEL_MODE_MASK 0x0C
+#define A2DP_AAC_CHANNEL_MODE_MONO 0x08
+#define A2DP_AAC_CHANNEL_MODE_STEREO 0x04
+// [Octet 2], [Bits 0-1] RFA
+// [Octet 3], [Bit 7] Variable Bit Rate Supported
+#define A2DP_AAC_VARIABLE_BIT_RATE_MASK 0x80
+#define A2DP_AAC_VARIABLE_BIT_RATE_ENABLED 0x80
+#define A2DP_AAC_VARIABLE_BIT_RATE_DISABLED 0x00
+// [Octet 3], [Bits 0-6] Bit Rate - Bits 16-22 in the 23-bit UiMsbf
+#define A2DP_AAC_BIT_RATE_MASK0 (0x7F << 16)
+#define A2DP_AAC_BIT_RATE_MASK1 (0xFF << 8)
+#define A2DP_AAC_BIT_RATE_MASK2 0xFF
+// [Octet 4], [Bits 0-7] Bit Rate - Bits 8-15 in the 23-bit UiMsfb
+// [Octet 5], [Bits 0-7] Bit Rate - Bits 0-7 in the 23-bit UiMsfb
+
+#endif // A2DP_AAC_CONSTANTS_H
diff --git a/stack/include/a2dp_aac_encoder.h b/stack/include/a2dp_aac_encoder.h
new file mode 100644
index 0000000..d07a9a5
--- /dev/null
+++ b/stack/include/a2dp_aac_encoder.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Interface to the A2DP AAC Encoder
+//
+
+#ifndef A2DP_AAC_ENCODER_H
+#define A2DP_AAC_ENCODER_H
+
+#include "a2dp_codec_api.h"
+#include "osi/include/time.h"
+
+// Loads the A2DP AAC encoder.
+// Return true on success, otherwise false.
+bool A2DP_LoadEncoderAac(void);
+
+// Unloads the A2DP AAC encoder.
+void A2DP_UnloadEncoderAac(void);
+
+// Initialize the A2DP AAC encoder.
+// |p_peer_params| contains the A2DP peer information
+// The current A2DP codec config is in |a2dp_codec_config|.
+// |read_callback| is the callback for reading the input audio data.
+// |enqueue_callback| is the callback for enqueueing the encoded audio data.
+void a2dp_aac_encoder_init(const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+ A2dpCodecConfig* a2dp_codec_config,
+ a2dp_source_read_callback_t read_callback,
+ a2dp_source_enqueue_callback_t enqueue_callback);
+
+// Cleanup the A2DP AAC encoder.
+void a2dp_aac_encoder_cleanup(void);
+
+// Reset the feeding for the A2DP AAC encoder.
+void a2dp_aac_feeding_reset(void);
+
+// Flush the feeding for the A2DP AAC encoder.
+void a2dp_aac_feeding_flush(void);
+
+// Get the A2DP AAC encoder interval (in milliseconds).
+period_ms_t a2dp_aac_get_encoder_interval_ms(void);
+
+// Prepare and send A2DP AAC encoded frames.
+// |timestamp_us| is the current timestamp (in microseconds).
+void a2dp_aac_send_frames(uint64_t timestamp_us);
+
+// Dump AAC codec-related statistics.
+// |fd| is the file descriptor to use to dump the statistics information
+// in user-friendly test format.
+void a2dp_aac_debug_codec_dump(int fd);
+
+#endif // A2DP_AAC_ENCODER_H
diff --git a/stack/include/a2dp_api.h b/stack/include/a2dp_api.h
index 2a27de5..c4ab737 100644
--- a/stack/include/a2dp_api.h
+++ b/stack/include/a2dp_api.h
@@ -191,7 +191,7 @@
* A2DP_SET_ZERO_BIT, if all bits clear
* A2DP_SET_MULTL_BIT, if multiple bits are set
*****************************************************************************/
-extern uint8_t A2DP_BitsSet(uint8_t num);
+extern uint8_t A2DP_BitsSet(uint64_t num);
// Initializes the A2DP control block.
void A2DP_Init(void);
diff --git a/stack/include/a2dp_constants.h b/stack/include/a2dp_constants.h
index f49a7a2..479d9e4 100644
--- a/stack/include/a2dp_constants.h
+++ b/stack/include/a2dp_constants.h
@@ -37,8 +37,8 @@
#define A2DP_SUPF_AMP 0x0008
/* AV Media Codec Types (Audio Codec ID) */
-/* SBC media codec type */
-#define A2DP_MEDIA_CT_SBC 0x00
+#define A2DP_MEDIA_CT_SBC 0x00 /* SBC media codec type */
+#define A2DP_MEDIA_CT_AAC 0x02 /* AAC media codec type */
/* Non-A2DP media codec type (vendor-specific codec) */
#define A2DP_MEDIA_CT_NON_A2DP 0xFF
diff --git a/stack/l2cap/l2c_main.cc b/stack/l2cap/l2c_main.cc
index 4d69eac..8a39383 100644
--- a/stack/l2cap/l2c_main.cc
+++ b/stack/l2cap/l2c_main.cc
@@ -876,8 +876,9 @@
if (p_data->len > mtu) {
L2CAP_TRACE_WARNING(
- "L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size",
- cid);
+ "L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size: "
+ "len=%u mtu=%u",
+ cid, p_data->len, mtu);
osi_free(p_data);
return (L2CAP_DW_FAILED);
}
diff --git a/stack/test/stack_a2dp_test.cc b/stack/test/stack_a2dp_test.cc
index 8c3653e..305a240 100644
--- a/stack/test/stack_a2dp_test.cc
+++ b/stack/test/stack_a2dp_test.cc
@@ -22,6 +22,7 @@
#include <gtest/gtest.h>
+#include "stack/include/a2dp_aac.h"
#include "stack/include/a2dp_api.h"
#include "stack/include/a2dp_codec_api.h"
#include "stack/include/a2dp_sbc.h"
@@ -73,6 +74,71 @@
9 // Dummy
};
+const uint8_t codec_info_aac[AVDT_CODEC_SIZE] = {
+ 8, // Length (A2DP_AAC_INFO_LEN)
+ 0, // Media Type: AVDT_MEDIA_TYPE_AUDIO
+ 2, // Media Codec Type: A2DP_MEDIA_CT_AAC
+ 0x80, // Object Type: A2DP_AAC_OBJECT_TYPE_MPEG2_LC
+ 0x01, // Sampling Frequency: A2DP_AAC_SAMPLING_FREQ_44100
+ 0x04, // Channels: A2DP_AAC_CHANNEL_MODE_STEREO
+ 0x00 | 0x4, // Variable Bit Rate:
+ // A2DP_AAC_VARIABLE_BIT_RATE_DISABLED
+ // Bit Rate: 320000 = 0x4e200
+ 0xe2, // Bit Rate: 320000 = 0x4e200
+ 0x00, // Bit Rate: 320000 = 0x4e200
+ 7, // Dummy
+ 8, // Dummy
+ 9 // Dummy
+};
+
+const uint8_t codec_info_aac_capability[AVDT_CODEC_SIZE] = {
+ 8, // Length (A2DP_AAC_INFO_LEN)
+ 0, // Media Type: AVDT_MEDIA_TYPE_AUDIO
+ 2, // Media Codec Type: A2DP_MEDIA_CT_AAC
+ 0x80, // Object Type: A2DP_AAC_OBJECT_TYPE_MPEG2_LC
+ 0x01, // Sampling Frequency: A2DP_AAC_SAMPLING_FREQ_44100
+ 0x80 | 0x20 | 0x10 | 0x04, // Sampling Frequency:
+ // A2DP_AAC_SAMPLING_FREQ_48000 |
+ // A2DP_AAC_SAMPLING_FREQ_88200 |
+ // A2DP_AAC_SAMPLING_FREQ_96000 |
+ // Channels:
+ // A2DP_AAC_CHANNEL_MODE_STEREO
+ 0x00 | 0x4, // Variable Bit Rate:
+ // A2DP_AAC_VARIABLE_BIT_RATE_DISABLED
+ // Bit Rate: 320000 = 0x4e200
+ 0xe2, // Bit Rate: 320000 = 0x4e200
+ 0x00, // Bit Rate: 320000 = 0x4e200
+ 7, // Dummy
+ 8, // Dummy
+ 9 // Dummy
+};
+
+const uint8_t codec_info_aac_sink_capability[AVDT_CODEC_SIZE] = {
+ 8, // Length (A2DP_AAC_INFO_LEN)
+ 0, // Media Type: AVDT_MEDIA_TYPE_AUDIO
+ 2, // Media Codec Type: A2DP_MEDIA_CT_AAC
+ 0x80 | 0x40 | 0x20 | 0x10, // Object Type: A2DP_AAC_OBJECT_TYPE_MPEG2_LC |
+ // A2DP_AAC_OBJECT_TYPE_MPEG4_LC
+ // A2DP_AAC_OBJECT_TYPE_MPEG4_LTP
+ // A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE
+ 0x01, // Sampling Frequency: A2DP_AAC_SAMPLING_FREQ_44100
+ 0x80 | 0x20 | 0x10 | 0x08 | 0x04, // Sampling Frequency:
+ // A2DP_AAC_SAMPLING_FREQ_48000 |
+ // A2DP_AAC_SAMPLING_FREQ_88200 |
+ // A2DP_AAC_SAMPLING_FREQ_96000 |
+ // Channels:
+ // A2DP_AAC_CHANNEL_MODE_MONO |
+ // A2DP_AAC_CHANNEL_MODE_STEREO
+ 0x80 | 0x4, // Variable Bit Rate:
+ // A2DP_AAC_VARIABLE_BIT_RATE_ENABLED
+ // Bit Rate: 320000 = 0x4e200
+ 0xe2, // Bit Rate: 320000 = 0x4e200
+ 0x00, // Bit Rate: 320000 = 0x4e200
+ 7, // Dummy
+ 8, // Dummy
+ 9 // Dummy
+};
+
const uint8_t codec_info_non_a2dp[AVDT_CODEC_SIZE] = {
8, // Length
0, // Media Type: AVDT_MEDIA_TYPE_AUDIO
@@ -120,6 +186,9 @@
case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
supported = true;
break;
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+ supported = true;
+ break;
case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
// Codec aptX is supported only if the device has the corresponding
// shared library installed.
@@ -168,10 +237,27 @@
EXPECT_TRUE(A2DP_BitsSet(0x7f) == A2DP_SET_MULTL_BIT);
EXPECT_TRUE(A2DP_BitsSet(0x80) == A2DP_SET_ONE_BIT);
EXPECT_TRUE(A2DP_BitsSet(0x81) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xc0) == A2DP_SET_MULTL_BIT);
EXPECT_TRUE(A2DP_BitsSet(0xff) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0x8000) == A2DP_SET_ONE_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0x8001) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xc000) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xffff) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0x80000) == A2DP_SET_ONE_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0x80001) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xc0000) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xfffff) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0x80000000) == A2DP_SET_ONE_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0x80000001) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xc0000000) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xffffffff) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0x8000000000000000) == A2DP_SET_ONE_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0x8000000000000001) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xc000000000000000) == A2DP_SET_MULTL_BIT);
+ EXPECT_TRUE(A2DP_BitsSet(0xffffffffffffffff) == A2DP_SET_MULTL_BIT);
}
-TEST_F(StackA2dpTest, test_a2dp_is_codec_valid) {
+TEST_F(StackA2dpTest, test_a2dp_is_codec_valid_sbc) {
EXPECT_TRUE(A2DP_IsSourceCodecValid(codec_info_sbc));
EXPECT_TRUE(A2DP_IsPeerSourceCodecValid(codec_info_sbc));
@@ -206,10 +292,35 @@
EXPECT_FALSE(A2DP_IsPeerSinkCodecValid(codec_info_sbc_invalid));
}
+TEST_F(StackA2dpTest, test_a2dp_is_codec_valid_aac) {
+ EXPECT_TRUE(A2DP_IsSourceCodecValid(codec_info_aac));
+ EXPECT_TRUE(A2DP_IsPeerSinkCodecValid(codec_info_aac_capability));
+ EXPECT_TRUE(A2DP_IsPeerSinkCodecValid(codec_info_aac_sink_capability));
+
+ // Test with invalid AAC codecs
+ uint8_t codec_info_aac_invalid[AVDT_CODEC_SIZE];
+ memset(codec_info_aac_invalid, 0, sizeof(codec_info_aac_invalid));
+ EXPECT_FALSE(A2DP_IsSourceCodecValid(codec_info_aac_invalid));
+ EXPECT_FALSE(A2DP_IsPeerSinkCodecValid(codec_info_aac_invalid));
+
+ memcpy(codec_info_aac_invalid, codec_info_aac, sizeof(codec_info_aac));
+ codec_info_aac_invalid[0] = 0; // Corrupt the Length field
+ EXPECT_FALSE(A2DP_IsSourceCodecValid(codec_info_aac_invalid));
+ EXPECT_FALSE(A2DP_IsPeerSinkCodecValid(codec_info_aac_invalid));
+
+ memcpy(codec_info_aac_invalid, codec_info_aac, sizeof(codec_info_aac));
+ codec_info_aac_invalid[1] = 0xff; // Corrupt the Media Type field
+ EXPECT_FALSE(A2DP_IsSourceCodecValid(codec_info_aac_invalid));
+ EXPECT_FALSE(A2DP_IsPeerSinkCodecValid(codec_info_aac_invalid));
+}
+
TEST_F(StackA2dpTest, test_a2dp_get_codec_type) {
tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(codec_info_sbc);
EXPECT_EQ(codec_type, A2DP_MEDIA_CT_SBC);
+ codec_type = A2DP_GetCodecType(codec_info_aac);
+ EXPECT_EQ(codec_type, A2DP_MEDIA_CT_AAC);
+
codec_type = A2DP_GetCodecType(codec_info_non_a2dp);
EXPECT_EQ(codec_type, A2DP_MEDIA_CT_NON_A2DP);
}
@@ -217,12 +328,22 @@
TEST_F(StackA2dpTest, test_a2dp_is_sink_codec_supported) {
EXPECT_TRUE(A2DP_IsSinkCodecSupported(codec_info_sbc));
EXPECT_FALSE(A2DP_IsSinkCodecSupported(codec_info_sbc_sink_capability));
+
+ EXPECT_FALSE(A2DP_IsSinkCodecSupported(codec_info_aac));
+ EXPECT_FALSE(A2DP_IsSinkCodecSupported(codec_info_aac_capability));
+ EXPECT_FALSE(A2DP_IsSinkCodecSupported(codec_info_aac_sink_capability));
+
EXPECT_FALSE(A2DP_IsSinkCodecSupported(codec_info_non_a2dp));
}
TEST_F(StackA2dpTest, test_a2dp_is_peer_source_codec_supported) {
EXPECT_TRUE(A2DP_IsPeerSourceCodecSupported(codec_info_sbc));
EXPECT_TRUE(A2DP_IsPeerSourceCodecSupported(codec_info_sbc_sink_capability));
+
+ EXPECT_FALSE(A2DP_IsPeerSourceCodecSupported(codec_info_aac));
+ EXPECT_FALSE(A2DP_IsPeerSourceCodecSupported(codec_info_aac_capability));
+ EXPECT_FALSE(A2DP_IsPeerSourceCodecSupported(codec_info_aac_sink_capability));
+
EXPECT_FALSE(A2DP_IsPeerSourceCodecSupported(codec_info_non_a2dp));
}
@@ -249,7 +370,7 @@
EXPECT_EQ(codec_info_result[i], codec_info_sbc[i]);
}
- // Include extra (less preferred) capabilities and test again
+ // Include extra (less preferred) capabilities and test again - SBC
uint8_t codec_info_sbc_test1[AVDT_CODEC_SIZE];
memcpy(codec_info_sbc_test1, codec_info_sbc, sizeof(codec_info_sbc));
codec_info_sbc_test1[3] |= (A2DP_SBC_IE_CH_MD_STEREO |
@@ -266,6 +387,10 @@
EXPECT_EQ(codec_info_result[i], codec_info_sbc[i]);
}
+ memset(codec_info_result, 0, sizeof(codec_info_result));
+ EXPECT_NE(A2DP_BuildSrc2SinkConfig(codec_info_aac, codec_info_result),
+ A2DP_SUCCESS);
+
// Test invalid codec info
memset(codec_info_result, 0, sizeof(codec_info_result));
memset(codec_info_sbc_test1, 0, sizeof(codec_info_sbc_test1));
@@ -276,6 +401,10 @@
TEST_F(StackA2dpTest, test_a2dp_uses_rtp_header) {
EXPECT_TRUE(A2DP_UsesRtpHeader(true, codec_info_sbc));
EXPECT_TRUE(A2DP_UsesRtpHeader(false, codec_info_sbc));
+
+ EXPECT_TRUE(A2DP_UsesRtpHeader(true, codec_info_aac));
+ EXPECT_TRUE(A2DP_UsesRtpHeader(false, codec_info_aac));
+
EXPECT_TRUE(A2DP_UsesRtpHeader(true, codec_info_non_a2dp));
EXPECT_TRUE(A2DP_UsesRtpHeader(false, codec_info_non_a2dp));
}
@@ -284,6 +413,7 @@
uint8_t codec_info_test[AVDT_CODEC_SIZE];
EXPECT_EQ(A2DP_GetMediaType(codec_info_sbc), AVDT_MEDIA_TYPE_AUDIO);
+ EXPECT_EQ(A2DP_GetMediaType(codec_info_aac), AVDT_MEDIA_TYPE_AUDIO);
EXPECT_EQ(A2DP_GetMediaType(codec_info_non_a2dp), AVDT_MEDIA_TYPE_AUDIO);
// Prepare dummy codec info for video and for multimedia
@@ -301,11 +431,14 @@
// Explicit tests for known codecs
EXPECT_STREQ(A2DP_CodecName(codec_info_sbc), "SBC");
EXPECT_STREQ(A2DP_CodecName(codec_info_sbc_sink_capability), "SBC");
+ EXPECT_STREQ(A2DP_CodecName(codec_info_aac), "AAC");
+ EXPECT_STREQ(A2DP_CodecName(codec_info_aac_capability), "AAC");
+ EXPECT_STREQ(A2DP_CodecName(codec_info_aac_sink_capability), "AAC");
EXPECT_STREQ(A2DP_CodecName(codec_info_non_a2dp), "UNKNOWN VENDOR CODEC");
// Test all unknown codecs
memcpy(codec_info_test, codec_info_sbc, sizeof(codec_info_sbc));
- for (uint8_t codec_type = A2DP_MEDIA_CT_SBC + 1;
+ for (uint8_t codec_type = A2DP_MEDIA_CT_AAC + 1;
codec_type < A2DP_MEDIA_CT_NON_A2DP; codec_type++) {
codec_info_test[2] = codec_type; // Unknown codec type
EXPECT_STREQ(A2DP_CodecName(codec_info_test), "UNKNOWN CODEC");
@@ -323,13 +456,19 @@
TEST_F(StackA2dpTest, test_a2dp_codec_type_equals) {
EXPECT_TRUE(
A2DP_CodecTypeEquals(codec_info_sbc, codec_info_sbc_sink_capability));
+ EXPECT_TRUE(A2DP_CodecTypeEquals(codec_info_aac, codec_info_aac_capability));
+ EXPECT_TRUE(
+ A2DP_CodecTypeEquals(codec_info_aac, codec_info_aac_sink_capability));
EXPECT_TRUE(
A2DP_CodecTypeEquals(codec_info_non_a2dp, codec_info_non_a2dp_dummy));
EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_non_a2dp));
+ EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_aac, codec_info_non_a2dp));
+ EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_aac));
}
TEST_F(StackA2dpTest, test_a2dp_codec_equals) {
uint8_t codec_info_sbc_test[AVDT_CODEC_SIZE];
+ uint8_t codec_info_aac_test[AVDT_CODEC_SIZE];
uint8_t codec_info_non_a2dp_test[AVDT_CODEC_SIZE];
// Test two identical SBC codecs
@@ -337,6 +476,11 @@
memcpy(codec_info_sbc_test, codec_info_sbc, sizeof(codec_info_sbc));
EXPECT_TRUE(A2DP_CodecEquals(codec_info_sbc, codec_info_sbc_test));
+ // Test two identical AAC codecs
+ memset(codec_info_aac_test, 0xAB, sizeof(codec_info_aac_test));
+ memcpy(codec_info_aac_test, codec_info_aac, sizeof(codec_info_aac));
+ EXPECT_TRUE(A2DP_CodecEquals(codec_info_aac, codec_info_aac_test));
+
// Test two identical non-A2DP codecs that are not recognized
memset(codec_info_non_a2dp_test, 0xAB, sizeof(codec_info_non_a2dp_test));
memcpy(codec_info_non_a2dp_test, codec_info_non_a2dp,
@@ -345,6 +489,7 @@
// Test two codecs that have different types
EXPECT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_non_a2dp));
+ EXPECT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_aac));
// Test two SBC codecs that are slightly different
memset(codec_info_sbc_test, 0xAB, sizeof(codec_info_sbc_test));
@@ -355,76 +500,128 @@
codec_info_sbc_test[6] = codec_info_sbc[6] + 1;
EXPECT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_sbc_test));
+ // Test two AAC codecs that are slightly different
+ memset(codec_info_aac_test, 0xAB, sizeof(codec_info_aac_test));
+ memcpy(codec_info_aac_test, codec_info_aac, sizeof(codec_info_aac));
+ codec_info_aac_test[7] = codec_info_aac[7] + 1;
+ EXPECT_FALSE(A2DP_CodecEquals(codec_info_aac, codec_info_aac_test));
+ codec_info_aac_test[7] = codec_info_aac[7];
+ codec_info_aac_test[8] = codec_info_aac[8] + 1;
+ EXPECT_FALSE(A2DP_CodecEquals(codec_info_aac, codec_info_aac_test));
+
// Test two SBC codecs that are identical, but with different dummy
// trailer data.
memset(codec_info_sbc_test, 0xAB, sizeof(codec_info_sbc_test));
memcpy(codec_info_sbc_test, codec_info_sbc, sizeof(codec_info_sbc));
codec_info_sbc_test[7] = codec_info_sbc[7] + 1;
EXPECT_TRUE(A2DP_CodecEquals(codec_info_sbc, codec_info_sbc_test));
+
+ // Test two AAC codecs that are identical, but with different dummy
+ // trailer data.
+ memset(codec_info_aac_test, 0xAB, sizeof(codec_info_aac_test));
+ memcpy(codec_info_aac_test, codec_info_aac, sizeof(codec_info_aac));
+ codec_info_aac_test[9] = codec_info_aac[9] + 1;
+ EXPECT_TRUE(A2DP_CodecEquals(codec_info_aac, codec_info_aac_test));
}
TEST_F(StackA2dpTest, test_a2dp_get_track_sample_rate) {
EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_sbc), 44100);
+ EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_aac), 44100);
EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_track_bits_per_sample) {
EXPECT_EQ(A2DP_GetTrackBitsPerSample(codec_info_sbc), 16);
+ EXPECT_EQ(A2DP_GetTrackBitsPerSample(codec_info_aac), 16);
EXPECT_EQ(A2DP_GetTrackBitsPerSample(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_track_channel_count) {
EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_sbc), 2);
+ EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_aac), 2);
EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_number_of_subbands_sbc) {
EXPECT_EQ(A2DP_GetNumberOfSubbandsSbc(codec_info_sbc), 8);
+ EXPECT_EQ(A2DP_GetNumberOfSubbandsSbc(codec_info_aac), -1);
EXPECT_EQ(A2DP_GetNumberOfSubbandsSbc(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_number_of_blocks_sbc) {
EXPECT_EQ(A2DP_GetNumberOfBlocksSbc(codec_info_sbc), 16);
+ EXPECT_EQ(A2DP_GetNumberOfBlocksSbc(codec_info_aac), -1);
EXPECT_EQ(A2DP_GetNumberOfBlocksSbc(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_allocation_method_code_sbc) {
EXPECT_EQ(A2DP_GetAllocationMethodCodeSbc(codec_info_sbc), 0);
+ EXPECT_EQ(A2DP_GetAllocationMethodCodeSbc(codec_info_aac), -1);
EXPECT_EQ(A2DP_GetAllocationMethodCodeSbc(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_channel_mode_code_sbc) {
EXPECT_EQ(A2DP_GetChannelModeCodeSbc(codec_info_sbc), 3);
+ EXPECT_EQ(A2DP_GetChannelModeCodeSbc(codec_info_aac), -1);
EXPECT_EQ(A2DP_GetChannelModeCodeSbc(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_sampling_frequency_code_sbc) {
EXPECT_EQ(A2DP_GetSamplingFrequencyCodeSbc(codec_info_sbc), 2);
+ EXPECT_EQ(A2DP_GetSamplingFrequencyCodeSbc(codec_info_aac), -1);
EXPECT_EQ(A2DP_GetSamplingFrequencyCodeSbc(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_min_bitpool_sbc) {
EXPECT_EQ(A2DP_GetMinBitpoolSbc(codec_info_sbc), 2);
EXPECT_EQ(A2DP_GetMinBitpoolSbc(codec_info_sbc_sink_capability), 2);
+ EXPECT_EQ(A2DP_GetMinBitpoolSbc(codec_info_aac), -1);
EXPECT_EQ(A2DP_GetMinBitpoolSbc(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_max_bitpool_sbc) {
EXPECT_EQ(A2DP_GetMaxBitpoolSbc(codec_info_sbc), 53);
EXPECT_EQ(A2DP_GetMaxBitpoolSbc(codec_info_sbc_sink_capability), 53);
+ EXPECT_EQ(A2DP_GetMaxBitpoolSbc(codec_info_aac), -1);
EXPECT_EQ(A2DP_GetMaxBitpoolSbc(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_sink_track_channel_type) {
EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_sbc), 3);
+ EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_aac), -1);
EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_non_a2dp), -1);
}
TEST_F(StackA2dpTest, test_a2dp_get_sink_frames_count_to_process) {
EXPECT_EQ(A2DP_GetSinkFramesCountToProcess(20, codec_info_sbc), 7);
+ EXPECT_EQ(A2DP_GetSinkFramesCountToProcess(20, codec_info_aac), -1);
EXPECT_EQ(A2DP_GetSinkFramesCountToProcess(20, codec_info_non_a2dp), -1);
}
+TEST_F(StackA2dpTest, test_a2dp_get_object_type_code_aac) {
+ EXPECT_EQ(A2DP_GetObjectTypeCodeAac(codec_info_sbc), -1);
+ EXPECT_EQ(A2DP_GetObjectTypeCodeAac(codec_info_aac), 0x80);
+ EXPECT_EQ(A2DP_GetObjectTypeCodeAac(codec_info_non_a2dp), -1);
+}
+
+TEST_F(StackA2dpTest, test_a2dp_get_channel_mode_code_aac) {
+ EXPECT_EQ(A2DP_GetChannelModeCodeAac(codec_info_sbc), -1);
+ EXPECT_EQ(A2DP_GetChannelModeCodeAac(codec_info_aac), 0x04);
+ EXPECT_EQ(A2DP_GetChannelModeCodeAac(codec_info_non_a2dp), -1);
+}
+
+TEST_F(StackA2dpTest, test_a2dp_get_variable_bit_rate_support_aac) {
+ EXPECT_EQ(A2DP_GetVariableBitRateSupportAac(codec_info_sbc), -1);
+ EXPECT_EQ(A2DP_GetVariableBitRateSupportAac(codec_info_aac), 0);
+ EXPECT_EQ(A2DP_GetVariableBitRateSupportAac(codec_info_non_a2dp), -1);
+}
+
+TEST_F(StackA2dpTest, test_a2dp_get_bit_rate_aac) {
+ EXPECT_EQ(A2DP_GetBitRateAac(codec_info_sbc), -1);
+ EXPECT_EQ(A2DP_GetBitRateAac(codec_info_aac), 320000);
+ EXPECT_EQ(A2DP_GetBitRateAac(codec_info_non_a2dp), -1);
+}
+
TEST_F(StackA2dpTest, test_a2dp_get_packet_timestamp) {
uint8_t a2dp_data[1000];
uint32_t timestamp;
@@ -439,6 +636,12 @@
memset(a2dp_data, 0xAB, sizeof(a2dp_data));
*p_ts = 0x12345678;
timestamp = 0xFFFFFFFF;
+ EXPECT_TRUE(A2DP_GetPacketTimestamp(codec_info_aac, a2dp_data, ×tamp));
+ EXPECT_EQ(timestamp, static_cast<uint32_t>(0x12345678));
+
+ memset(a2dp_data, 0xAB, sizeof(a2dp_data));
+ *p_ts = 0x12345678;
+ timestamp = 0xFFFFFFFF;
EXPECT_FALSE(
A2DP_GetPacketTimestamp(codec_info_non_a2dp, a2dp_data, ×tamp));
}
@@ -465,12 +668,18 @@
memset(a2dp_data, 0xAB, sizeof(a2dp_data));
p_buf->len = BT_HDR_LEN;
p_buf->offset = BT_HDR_OFFSET;
+ EXPECT_TRUE(A2DP_BuildCodecHeader(codec_info_aac, p_buf, FRAMES_PER_PACKET));
+
+ memset(a2dp_data, 0xAB, sizeof(a2dp_data));
+ p_buf->len = BT_HDR_LEN;
+ p_buf->offset = BT_HDR_OFFSET;
EXPECT_FALSE(
A2DP_BuildCodecHeader(codec_info_non_a2dp, p_buf, FRAMES_PER_PACKET));
}
TEST_F(StackA2dpTest, test_a2dp_adjust_codec) {
uint8_t codec_info_sbc_test[AVDT_CODEC_SIZE];
+ uint8_t codec_info_aac_test[AVDT_CODEC_SIZE];
uint8_t codec_info_non_a2dp_test[AVDT_CODEC_SIZE];
// Test updating a valid SBC codec that doesn't need adjustment
@@ -494,6 +703,13 @@
codec_info_sbc_test[6] = 255; // Invalid MAX_BITPOOL
EXPECT_FALSE(A2DP_AdjustCodec(codec_info_sbc_test));
+ // Test updating a valid AAC codec that doesn't need adjustment
+ memset(codec_info_aac_test, 0xAB, sizeof(codec_info_aac_test));
+ memcpy(codec_info_aac_test, codec_info_aac, sizeof(codec_info_aac));
+ EXPECT_TRUE(A2DP_AdjustCodec(codec_info_aac_test));
+ EXPECT_TRUE(
+ memcmp(codec_info_aac_test, codec_info_aac, sizeof(codec_info_aac)) == 0);
+
// Test updating a non-A2DP codec that is not recognized
memset(codec_info_non_a2dp_test, 0xAB, sizeof(codec_info_non_a2dp_test));
memcpy(codec_info_non_a2dp_test, codec_info_non_a2dp,
@@ -507,6 +723,12 @@
BTAV_A2DP_CODEC_INDEX_SOURCE_SBC);
EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_sbc_sink_capability),
BTAV_A2DP_CODEC_INDEX_SOURCE_SBC);
+ EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_aac),
+ BTAV_A2DP_CODEC_INDEX_SOURCE_AAC);
+ EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_aac_capability),
+ BTAV_A2DP_CODEC_INDEX_SOURCE_AAC);
+ EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_aac_sink_capability),
+ BTAV_A2DP_CODEC_INDEX_SOURCE_AAC);
EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_non_a2dp),
BTAV_A2DP_CODEC_INDEX_MAX);
}
@@ -515,6 +737,7 @@
// Explicit tests for known codecs
EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC), "SBC");
EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SINK_SBC), "SBC SINK");
+ EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC), "AAC");
// Test that the unknown codec string has not changed
EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_MAX),
@@ -558,6 +781,24 @@
for (size_t i = 0; i < codec_info_sbc_sink_capability[0] + 1; i++) {
EXPECT_EQ(avdt_cfg.codec_info[i], codec_info_sbc_sink_capability[i]);
}
+
+ //
+ // Test for AAC Source
+ //
+ memset(&avdt_cfg, 0, sizeof(avdt_cfg));
+ EXPECT_TRUE(
+ A2DP_InitCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC, &avdt_cfg));
+ // Compare the result codec with the local test codec info
+ for (size_t i = 0; i < codec_info_aac_capability[0] + 1; i++) {
+ EXPECT_EQ(avdt_cfg.codec_info[i], codec_info_aac_capability[i]);
+ }
+// Test for content protection
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+ EXPECT_EQ(avdt_cfg.protect_info[0], AVDT_CP_LOSC);
+ EXPECT_EQ(avdt_cfg.protect_info[1], (AVDT_CP_SCMS_T_ID & 0xFF));
+ EXPECT_EQ(avdt_cfg.protect_info[2], ((AVDT_CP_SCMS_T_ID >> 8) & 0xFF));
+ EXPECT_EQ(avdt_cfg.num_protect, 1);
+#endif
}
TEST_F(A2dpCodecConfigTest, createCodec) {
@@ -587,7 +828,7 @@
EXPECT_TRUE(a2dp_codecs->init());
- // Create the codec capability
+ // Create the codec capability - SBC
memset(codec_info_result, 0, sizeof(codec_info_result));
peer_codec_index = A2DP_SourceCodecIndex(codec_info_sbc_sink_capability);
EXPECT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX);
@@ -602,7 +843,22 @@
EXPECT_EQ(codec_info_result[i], codec_info_sbc[i]);
}
- // Create the codec config
+ // Create the codec capability - AAC
+ memset(codec_info_result, 0, sizeof(codec_info_result));
+ peer_codec_index = A2DP_SourceCodecIndex(codec_info_aac_sink_capability);
+ EXPECT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX);
+ codec_config =
+ a2dp_codecs->findSourceCodecConfig(codec_info_aac_sink_capability);
+ EXPECT_NE(codec_config, nullptr);
+ EXPECT_TRUE(a2dp_codecs->setCodecConfig(codec_info_aac_sink_capability, true,
+ codec_info_result));
+ EXPECT_EQ(a2dp_codecs->getCurrentCodecConfig(), codec_config);
+ // Compare the result codec with the local test codec info
+ for (size_t i = 0; i < codec_info_aac[0] + 1; i++) {
+ EXPECT_EQ(codec_info_result[i], codec_info_aac[i]);
+ }
+
+ // Create the codec config - SBC
memset(codec_info_result, 0, sizeof(codec_info_result));
peer_codec_index = A2DP_SourceCodecIndex(codec_info_sbc);
EXPECT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX);
@@ -616,6 +872,20 @@
EXPECT_EQ(codec_info_result[i], codec_info_sbc[i]);
}
+ // Create the codec config - AAC
+ memset(codec_info_result, 0, sizeof(codec_info_result));
+ peer_codec_index = A2DP_SourceCodecIndex(codec_info_aac);
+ EXPECT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX);
+ codec_config = a2dp_codecs->findSourceCodecConfig(codec_info_aac);
+ EXPECT_NE(codec_config, nullptr);
+ EXPECT_TRUE(
+ a2dp_codecs->setCodecConfig(codec_info_aac, false, codec_info_result));
+ EXPECT_EQ(a2dp_codecs->getCurrentCodecConfig(), codec_config);
+ // Compare the result codec with the local test codec info
+ for (size_t i = 0; i < codec_info_aac[0] + 1; i++) {
+ EXPECT_EQ(codec_info_result[i], codec_info_aac[i]);
+ }
+
// Test invalid codec info
uint8_t codec_info_sbc_test1[AVDT_CODEC_SIZE];
memset(codec_info_result, 0, sizeof(codec_info_result));