Integration of the aptX and aptX-HD codecs for A2DP source
Each of the codecs can be used if the corresponding encoding
shared library is installed on the device:
- aptX: libaptX.so
- aptX-HD: libaptXHD.so
Test: A2DP streaming to aptX and aptX-HD headsets
Bug: 30958229
Change-Id: I1ca36da653721576f5a5b6bdac186b9ea2790c85
diff --git a/stack/Android.mk b/stack/Android.mk
index 64a5ca2..0b1615f 100644
--- a/stack/Android.mk
+++ b/stack/Android.mk
@@ -39,6 +39,10 @@
./a2dp/a2dp_sbc_encoder.cc \
./a2dp/a2dp_sbc_up_sample.cc \
./a2dp/a2dp_vendor.cc \
+ ./a2dp/a2dp_vendor_aptx.cc \
+ ./a2dp/a2dp_vendor_aptx_hd.cc \
+ ./a2dp/a2dp_vendor_aptx_encoder.cc \
+ ./a2dp/a2dp_vendor_aptx_hd_encoder.cc \
./avct/avct_api.cc \
./avct/avct_bcb_act.cc \
./avct/avct_ccb.cc \
diff --git a/stack/a2dp/a2dp_codec_config.cc b/stack/a2dp/a2dp_codec_config.cc
index 73e1f67..a0182c3 100644
--- a/stack/a2dp/a2dp_codec_config.cc
+++ b/stack/a2dp/a2dp_codec_config.cc
@@ -26,6 +26,8 @@
#include "a2dp_sbc.h"
#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx.h"
+#include "a2dp_vendor_aptx_hd.h"
#include "osi/include/log.h"
/* The Media Type offset within the codec info byte array */
@@ -82,6 +84,12 @@
case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
codec_config = new A2dpCodecConfigSbcSink();
break;
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
+ codec_config = new A2dpCodecConfigAptx();
+ break;
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+ codec_config = new A2dpCodecConfigAptxHd();
+ break;
case BTAV_A2DP_CODEC_INDEX_MAX:
break;
}
diff --git a/stack/a2dp/a2dp_vendor.cc b/stack/a2dp/a2dp_vendor.cc
index 387f445..7224140 100644
--- a/stack/a2dp/a2dp_vendor.cc
+++ b/stack/a2dp/a2dp_vendor.cc
@@ -21,13 +21,27 @@
#define LOG_TAG "a2dp_vendor"
#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx.h"
+#include "a2dp_vendor_aptx_hd.h"
#include "bt_target.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
-bool A2DP_IsVendorSourceCodecValid(UNUSED_ATTR const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_IsVendorSourceCodecValid(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_IsVendorSourceCodecValidAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_IsVendorSourceCodecValidAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
@@ -55,9 +69,21 @@
return false;
}
-bool A2DP_IsVendorPeerSinkCodecValid(UNUSED_ATTR const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_IsVendorPeerSinkCodecValid(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_IsVendorPeerSinkCodecValidAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_IsVendorPeerSinkCodecValidAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
@@ -114,10 +140,24 @@
return codec_id;
}
-bool A2DP_VendorUsesRtpHeader(UNUSED_ATTR bool content_protection_enabled,
- UNUSED_ATTR const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_VendorUsesRtpHeader(bool content_protection_enabled,
+ const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorUsesRtpHeaderAptx(content_protection_enabled,
+ p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorUsesRtpHeaderAptxHd(content_protection_enabled,
+ p_codec_info);
+ }
// Add checks based on <content_protection_enabled, vendor_id, codec_id>
@@ -125,8 +165,20 @@
}
const char* A2DP_VendorCodecName(UNUSED_ATTR const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_src_config);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_src_config);
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorCodecNameAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorCodecNameAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
@@ -148,10 +200,24 @@
uint32_t vendor_id_b = A2DP_VendorCodecGetVendorId(p_codec_info_b);
uint16_t codec_id_b = A2DP_VendorCodecGetCodecId(p_codec_info_b);
+ if (vendor_id_a != vendor_id_b || codec_id_a != codec_id_b) return false;
+
+ // Check for aptX
+ if (vendor_id_a == A2DP_APTX_VENDOR_ID &&
+ codec_id_a == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorCodecTypeEqualsAptx(p_codec_info_a, p_codec_info_b);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id_a == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id_a == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorCodecTypeEqualsAptxHd(p_codec_info_a, p_codec_info_b);
+ }
+
// OPTIONAL: Add extra vendor-specific checks based on the
// vendor-specific data stored in "p_codec_info_a" and "p_codec_info_b".
- return (vendor_id_a == vendor_id_b) && (codec_id_a == codec_id_b);
+ return true;
}
bool A2DP_VendorCodecEquals(const uint8_t* p_codec_info_a,
@@ -171,33 +237,81 @@
if ((vendor_id_a != vendor_id_b) || (codec_id_a != codec_id_b)) return false;
+ // Check for aptX
+ if (vendor_id_a == A2DP_APTX_VENDOR_ID &&
+ codec_id_a == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorCodecEqualsAptx(p_codec_info_a, p_codec_info_b);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id_a == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id_a == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorCodecEqualsAptxHd(p_codec_info_a, p_codec_info_b);
+ }
+
// Add extra vendor-specific checks based on the
// vendor-specific data stored in "p_codec_info_a" and "p_codec_info_b".
return false;
}
-int A2DP_VendorGetTrackSampleRate(UNUSED_ATTR const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+int A2DP_VendorGetTrackSampleRate(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetTrackSampleRateAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetTrackSampleRateAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
return -1;
}
-int A2DP_VendorGetTrackBitsPerSample(UNUSED_ATTR const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+int A2DP_VendorGetTrackBitsPerSample(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetTrackBitsPerSampleAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetTrackBitsPerSampleAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
return -1;
}
-int A2DP_VendorGetTrackChannelCount(UNUSED_ATTR const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+int A2DP_VendorGetTrackChannelCount(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetTrackChannelCountAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetTrackChannelCountAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
@@ -227,22 +341,48 @@
return -1;
}
-bool A2DP_VendorGetPacketTimestamp(UNUSED_ATTR const uint8_t* p_codec_info,
- UNUSED_ATTR const uint8_t* p_data,
- UNUSED_ATTR uint32_t* p_timestamp) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_VendorGetPacketTimestamp(const uint8_t* p_codec_info,
+ const uint8_t* p_data,
+ uint32_t* p_timestamp) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetPacketTimestampAptx(p_codec_info, p_data, p_timestamp);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetPacketTimestampAptxHd(p_codec_info, p_data,
+ p_timestamp);
+ }
// Add checks based on <vendor_id, codec_id>
return false;
}
-bool A2DP_VendorBuildCodecHeader(UNUSED_ATTR const uint8_t* p_codec_info,
- UNUSED_ATTR BT_HDR* p_buf,
- UNUSED_ATTR uint16_t frames_per_packet) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_VendorBuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf,
+ uint16_t frames_per_packet) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorBuildCodecHeaderAptx(p_codec_info, p_buf,
+ frames_per_packet);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorBuildCodecHeaderAptxHd(p_codec_info, p_buf,
+ frames_per_packet);
+ }
// Add checks based on <vendor_id, codec_id>
@@ -251,8 +391,20 @@
const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterface(
const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetEncoderInterfaceAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorGetEncoderInterfaceAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
@@ -260,8 +412,20 @@
}
bool A2DP_VendorAdjustCodec(uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorAdjustCodecAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorAdjustCodecAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
@@ -269,9 +433,21 @@
}
btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndex(
- UNUSED_ATTR const uint8_t* p_codec_info) {
- // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
- // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+ const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Check for aptX
+ if (vendor_id == A2DP_APTX_VENDOR_ID &&
+ codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorSourceCodecIndexAptx(p_codec_info);
+ }
+
+ // Check for aptX-HD
+ if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+ codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_VendorSourceCodecIndexAptxHd(p_codec_info);
+ }
// Add checks based on <vendor_id, codec_id>
@@ -284,6 +460,10 @@
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_APTX:
+ return A2DP_VendorCodecIndexStrAptx();
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+ return A2DP_VendorCodecIndexStrAptxHd();
// Add a switch statement for each vendor-specific codec
case BTAV_A2DP_CODEC_INDEX_MAX:
break;
@@ -299,6 +479,10 @@
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_APTX:
+ return A2DP_VendorInitCodecConfigAptx(p_cfg);
+ case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+ return A2DP_VendorInitCodecConfigAptxHd(p_cfg);
// Add a switch statement for each vendor-specific codec
case BTAV_A2DP_CODEC_INDEX_MAX:
break;
diff --git a/stack/a2dp/a2dp_vendor_aptx.cc b/stack/a2dp/a2dp_vendor_aptx.cc
new file mode 100644
index 0000000..0a74529
--- /dev/null
+++ b/stack/a2dp/a2dp_vendor_aptx.cc
@@ -0,0 +1,843 @@
+/*
+ * 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 aptX Codec Information
+ * Element and Media Payload.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "a2dp_vendor_aptx"
+
+#include "bt_target.h"
+
+#include "a2dp_vendor_aptx.h"
+
+#include <string.h>
+
+#include <base/logging.h>
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx_encoder.h"
+#include "bt_utils.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+// data type for the aptX Codec Information Element */
+typedef struct {
+ uint32_t vendorId;
+ uint16_t codecId; /* Codec ID for aptX */
+ uint8_t sampleRate; /* Sampling Frequency */
+ uint8_t channelMode; /* STEREO/DUAL/MONO */
+ uint8_t future1;
+ uint8_t future2;
+ btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+} tA2DP_APTX_CIE;
+
+/* aptX Source codec capabilities */
+static const tA2DP_APTX_CIE a2dp_aptx_caps = {
+ A2DP_APTX_VENDOR_ID, /* vendorId */
+ A2DP_APTX_CODEC_ID_BLUETOOTH, /* codecId */
+ (A2DP_APTX_SAMPLERATE_44100 | A2DP_APTX_SAMPLERATE_48000), /* sampleRate */
+ A2DP_APTX_CHANNELS_STEREO, /* channelMode */
+ A2DP_APTX_FUTURE_1, /* future1 */
+ A2DP_APTX_FUTURE_2, /* future2 */
+ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 /* bits_per_sample */
+};
+
+/* Default aptX codec configuration */
+static const tA2DP_APTX_CIE a2dp_aptx_default_config = {
+ A2DP_APTX_VENDOR_ID, /* vendorId */
+ A2DP_APTX_CODEC_ID_BLUETOOTH, /* codecId */
+ A2DP_APTX_SAMPLERATE_44100, /* sampleRate */
+ A2DP_APTX_CHANNELS_STEREO, /* channelMode */
+ A2DP_APTX_FUTURE_1, /* future1 */
+ A2DP_APTX_FUTURE_2, /* future2 */
+ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 /* bits_per_sample */
+};
+
+static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_aptx = {
+ a2dp_vendor_aptx_encoder_init, a2dp_vendor_aptx_encoder_cleanup,
+ a2dp_vendor_aptx_feeding_init, a2dp_vendor_aptx_feeding_reset,
+ a2dp_vendor_aptx_feeding_flush, a2dp_vendor_aptx_get_encoder_interval_ms,
+ a2dp_vendor_aptx_send_frames, a2dp_vendor_aptx_debug_codec_dump};
+
+UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAptx(
+ const tA2DP_APTX_CIE* p_cap, const uint8_t* p_codec_info,
+ bool is_peer_codec_info);
+
+// Builds the aptX 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 aptX 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_BuildInfoAptx(uint8_t media_type,
+ const tA2DP_APTX_CIE* p_ie,
+ uint8_t* p_result) {
+ tA2DP_STATUS status;
+
+ if (p_ie == NULL || p_result == NULL) {
+ status = A2DP_INVALID_PARAMS;
+ } else {
+ status = A2DP_SUCCESS;
+ *p_result++ = A2DP_APTX_CODEC_LEN;
+ *p_result++ = media_type;
+ *p_result++ = A2DP_MEDIA_CT_NON_A2DP;
+ *p_result++ = (uint8_t)(p_ie->vendorId & 0x000000FF);
+ *p_result++ = (uint8_t)((p_ie->vendorId & 0x0000FF00) >> 8);
+ *p_result++ = (uint8_t)((p_ie->vendorId & 0x00FF0000) >> 16);
+ *p_result++ = (uint8_t)((p_ie->vendorId & 0xFF000000) >> 24);
+ *p_result++ = (uint8_t)(p_ie->codecId & 0x00FF);
+ *p_result++ = (uint8_t)((p_ie->codecId & 0xFF00) >> 8);
+ *p_result++ = p_ie->sampleRate | p_ie->channelMode;
+ }
+ return status;
+}
+
+// Parses the aptX 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_ParseInfoAptx(tA2DP_APTX_CIE* p_ie,
+ const uint8_t* p_codec_info,
+ bool is_capability) {
+ tA2DP_STATUS status = A2DP_SUCCESS;
+ 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_APTX_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_NON_A2DP) {
+ return A2DP_WRONG_CODEC;
+ }
+
+ // Check the Vendor ID and Codec ID */
+ p_ie->vendorId = (*p_codec_info & 0x000000FF) |
+ (*(p_codec_info + 1) << 8 & 0x0000FF00) |
+ (*(p_codec_info + 2) << 16 & 0x00FF0000) |
+ (*(p_codec_info + 3) << 24 & 0xFF000000);
+ p_codec_info = p_codec_info + 4;
+ p_ie->codecId =
+ (*p_codec_info & 0x00FF) | (*(p_codec_info + 1) << 8 & 0xFF00);
+ p_codec_info = p_codec_info + 2;
+ if (p_ie->vendorId != A2DP_APTX_VENDOR_ID ||
+ p_ie->codecId != A2DP_APTX_CODEC_ID_BLUETOOTH) {
+ return A2DP_WRONG_CODEC;
+ }
+
+ p_ie->channelMode = *p_codec_info & 0x0F;
+ p_ie->sampleRate = *p_codec_info & 0xF0;
+
+ status = A2DP_SUCCESS;
+
+ if (is_capability) return status;
+
+ if (A2DP_BitsSet(p_ie->sampleRate) != A2DP_SET_ONE_BIT)
+ status = A2DP_BAD_SAMP_FREQ;
+ if (A2DP_BitsSet(p_ie->channelMode) != A2DP_SET_ONE_BIT)
+ status = A2DP_BAD_CH_MODE;
+
+ return status;
+}
+
+bool A2DP_IsVendorSourceCodecValidAptx(const uint8_t* p_codec_info) {
+ tA2DP_APTX_CIE cfg_cie;
+
+ /* Use a liberal check when parsing the codec info */
+ return (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+ (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsVendorPeerSinkCodecValidAptx(const uint8_t* p_codec_info) {
+ tA2DP_APTX_CIE cfg_cie;
+
+ /* Use a liberal check when parsing the codec info */
+ return (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+ (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+// Checks whether A2DP aptX codec configuration matches with a device's codec
+// capabilities. |p_cap| is the aptX 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_CodecInfoMatchesCapabilityAptx(
+ const tA2DP_APTX_CIE* p_cap, const uint8_t* p_codec_info,
+ bool is_capability) {
+ tA2DP_STATUS status;
+ tA2DP_APTX_CIE cfg_cie;
+
+ /* parse configuration */
+ status = A2DP_ParseInfoAptx(&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, "FREQ peer: 0x%x, capability 0x%x", cfg_cie.sampleRate,
+ p_cap->sampleRate);
+ LOG_DEBUG(LOG_TAG, "CH_MODE peer: 0x%x, capability 0x%x", cfg_cie.channelMode,
+ p_cap->channelMode);
+
+ /* sampling frequency */
+ if ((cfg_cie.sampleRate & p_cap->sampleRate) == 0) return A2DP_NS_SAMP_FREQ;
+
+ /* channel mode */
+ if ((cfg_cie.channelMode & p_cap->channelMode) == 0) return A2DP_NS_CH_MODE;
+
+ return A2DP_SUCCESS;
+}
+
+bool A2DP_VendorUsesRtpHeaderAptx(UNUSED_ATTR bool content_protection_enabled,
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+ return true;
+#else
+ // no RTP header for aptX classic and no Copy Protection byte
+ return false;
+#endif
+}
+
+const char* A2DP_VendorCodecNameAptx(UNUSED_ATTR const uint8_t* p_codec_info) {
+ return "aptX";
+}
+
+bool A2DP_VendorCodecTypeEqualsAptx(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b) {
+ tA2DP_APTX_CIE aptx_cie_a;
+ tA2DP_APTX_CIE aptx_cie_b;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAptx(&aptx_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_ParseInfoAptx(&aptx_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_VendorCodecEqualsAptx(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b) {
+ tA2DP_APTX_CIE aptx_cie_a;
+ tA2DP_APTX_CIE aptx_cie_b;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAptx(&aptx_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_ParseInfoAptx(&aptx_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 (aptx_cie_a.sampleRate == aptx_cie_b.sampleRate) &&
+ (aptx_cie_a.channelMode == aptx_cie_b.channelMode);
+}
+
+int A2DP_VendorGetTrackSampleRateAptx(const uint8_t* p_codec_info) {
+ tA2DP_APTX_CIE aptx_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_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;
+ }
+
+ if (aptx_cie.sampleRate == A2DP_APTX_SAMPLERATE_44100) return 44100;
+ if (aptx_cie.sampleRate == A2DP_APTX_SAMPLERATE_48000) return 48000;
+
+ return -1;
+}
+
+int A2DP_VendorGetTrackBitsPerSampleAptx(const uint8_t* p_codec_info) {
+ tA2DP_APTX_CIE aptx_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_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 16; // For aptX we always use 16 bits per audio sample
+}
+
+int A2DP_VendorGetTrackChannelCountAptx(const uint8_t* p_codec_info) {
+ tA2DP_APTX_CIE aptx_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_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 (aptx_cie.channelMode) {
+ case A2DP_APTX_CHANNELS_MONO:
+ return 1;
+ case A2DP_APTX_CHANNELS_STEREO:
+ return 2;
+ }
+
+ return -1;
+}
+
+bool A2DP_VendorGetPacketTimestampAptx(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_VendorBuildCodecHeaderAptx(UNUSED_ATTR const uint8_t* p_codec_info,
+ UNUSED_ATTR BT_HDR* p_buf,
+ UNUSED_ATTR uint16_t frames_per_packet) {
+ // Nothing to do
+ return true;
+}
+
+void A2DP_VendorDumpCodecInfoAptx(const uint8_t* p_codec_info) {
+ tA2DP_STATUS a2dp_status;
+ tA2DP_APTX_CIE aptx_cie;
+
+ LOG_DEBUG(LOG_TAG, "%s", __func__);
+
+ a2dp_status = A2DP_ParseInfoAptx(&aptx_cie, p_codec_info, true);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: A2DP_ParseInfoAptx fail:%d", __func__, a2dp_status);
+ return;
+ }
+
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: 0x%x", aptx_cie.sampleRate);
+ if (aptx_cie.sampleRate == A2DP_APTX_SAMPLERATE_44100) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (44100)");
+ }
+ if (aptx_cie.sampleRate == A2DP_APTX_SAMPLERATE_48000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (48000)");
+ }
+
+ LOG_DEBUG(LOG_TAG, "\tch_mode: 0x%x", aptx_cie.channelMode);
+ if (aptx_cie.channelMode == A2DP_APTX_CHANNELS_MONO) {
+ LOG_DEBUG(LOG_TAG, "\tch_mode: (Mono)");
+ }
+ if (aptx_cie.channelMode == A2DP_APTX_CHANNELS_STEREO) {
+ LOG_DEBUG(LOG_TAG, "\tch_mode: (Stereo)");
+ }
+}
+
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceAptx(
+ const uint8_t* p_codec_info) {
+ if (!A2DP_IsVendorSourceCodecValidAptx(p_codec_info)) return NULL;
+
+ return &a2dp_encoder_interface_aptx;
+}
+
+bool A2DP_VendorAdjustCodecAptx(uint8_t* p_codec_info) {
+ tA2DP_APTX_CIE cfg_cie;
+
+ // Nothing to do: just verify the codec info is valid
+ if (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS)
+ return false;
+
+ return true;
+}
+
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexAptx(
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+ return BTAV_A2DP_CODEC_INDEX_SOURCE_APTX;
+}
+
+const char* A2DP_VendorCodecIndexStrAptx(void) { return "aptX"; }
+
+bool A2DP_VendorInitCodecConfigAptx(tAVDT_CFG* p_cfg) {
+ if (A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_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;
+}
+
+A2dpCodecConfigAptx::A2dpCodecConfigAptx()
+ : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, "aptX") {}
+
+A2dpCodecConfigAptx::~A2dpCodecConfigAptx() {}
+
+bool A2dpCodecConfigAptx::init() {
+ if (!isValid()) return false;
+
+ // Load the encoder
+ if (!A2DP_VendorLoadEncoderAptx()) {
+ 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(uint8_t sampleRate,
+ tA2DP_APTX_CIE* p_result,
+ btav_a2dp_codec_config_t* p_codec_config) {
+ if (sampleRate & A2DP_APTX_SAMPLERATE_48000) {
+ p_result->sampleRate = A2DP_APTX_SAMPLERATE_48000;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+ return true;
+ }
+ if (sampleRate & A2DP_APTX_SAMPLERATE_44100) {
+ p_result->sampleRate = A2DP_APTX_SAMPLERATE_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, uint8_t sampleRate,
+ tA2DP_APTX_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_APTX_SAMPLERATE_44100) {
+ p_result->sampleRate = A2DP_APTX_SAMPLERATE_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_APTX_SAMPLERATE_48000) {
+ p_result->sampleRate = A2DP_APTX_SAMPLERATE_48000;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+ 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.
+// The result is stored in |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_bits_per_sample(
+ btav_a2dp_codec_config_t* p_codec_config) {
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+ return true;
+}
+
+//
+// Selects the audio bits per sample from |p_codec_audio_config|.
+// The result is stored in |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_config_t* p_codec_config) {
+ switch (p_codec_audio_config->bits_per_sample) {
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+ return true;
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+ 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_APTX_CIE* p_result,
+ btav_a2dp_codec_config_t* p_codec_config) {
+ if (channelMode & A2DP_APTX_CHANNELS_STEREO) {
+ p_result->channelMode = A2DP_APTX_CHANNELS_STEREO;
+ p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+ return true;
+ }
+ if (channelMode & A2DP_APTX_CHANNELS_MONO) {
+ p_result->channelMode = A2DP_APTX_CHANNELS_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_APTX_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_APTX_CHANNELS_MONO) {
+ p_result->channelMode = A2DP_APTX_CHANNELS_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_APTX_CHANNELS_STEREO) {
+ p_result->channelMode = A2DP_APTX_CHANNELS_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 A2dpCodecConfigAptx::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_APTX_CIE sink_info_cie;
+ tA2DP_APTX_CIE result_config_cie;
+ uint8_t sampleRate;
+ uint8_t channelMode;
+
+ // 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_ParseInfoAptx(&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));
+ result_config_cie.vendorId = a2dp_aptx_caps.vendorId;
+ result_config_cie.codecId = a2dp_aptx_caps.codecId;
+
+ //
+ // Select the sample frequency
+ //
+ sampleRate = a2dp_aptx_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_APTX_SAMPLERATE_44100) {
+ result_config_cie.sampleRate = A2DP_APTX_SAMPLERATE_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_APTX_SAMPLERATE_48000) {
+ result_config_cie.sampleRate = A2DP_APTX_SAMPLERATE_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:
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+ 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_APTX_SAMPLERATE_44100)
+ codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+ if (sampleRate & A2DP_APTX_SAMPLERATE_48000)
+ codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+
+ // 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_aptx_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_aptx_caps.sampleRate, sink_info_cie.sampleRate);
+ goto fail;
+ }
+
+ //
+ // Select the bits per sample
+ //
+ // NOTE: this information is NOT included in the aptX A2DP codec
+ // description that is sent OTA.
+ 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:
+ 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:
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+ case 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 = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+
+ // No user preference - try the codec audio config
+ if (select_audio_bits_per_sample(&codec_audio_config_, &codec_config_)) {
+ break;
+ }
+
+ // No user preference - try the default config
+ if (select_best_bits_per_sample(&codec_config_)) {
+ break;
+ }
+
+ // No user preference - use the best match
+ // NOTE: no-op - kept here for consistency
+ if (select_best_bits_per_sample(&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: user preference = 0x%x",
+ __func__, codec_user_config_.bits_per_sample);
+ goto fail;
+ }
+
+ //
+ // Select the channel mode
+ //
+ channelMode = a2dp_aptx_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_APTX_CHANNELS_MONO) {
+ result_config_cie.channelMode = A2DP_APTX_CHANNELS_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_APTX_CHANNELS_STEREO) {
+ result_config_cie.channelMode = A2DP_APTX_CHANNELS_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_APTX_CHANNELS_MONO)
+ codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+ if (channelMode & A2DP_APTX_CHANNELS_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_aptx_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_aptx_caps.channelMode, sink_info_cie.channelMode);
+ goto fail;
+ }
+
+ //
+ // Set the rest of the fields as bit-wise AND operation
+ //
+ result_config_cie.future1 = a2dp_aptx_caps.future1 & sink_info_cie.future1;
+ result_config_cie.future2 = a2dp_aptx_caps.future2 & sink_info_cie.future2;
+
+ if (A2DP_BuildInfoAptx(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/config, and the
+ // result codec config.
+ if (is_capability) {
+ status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+ ota_codec_peer_capability_);
+ } else {
+ status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+ ota_codec_peer_config_);
+ }
+ CHECK(status == A2DP_SUCCESS);
+ status = A2DP_BuildInfoAptx(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_vendor_aptx_encoder.cc b/stack/a2dp/a2dp_vendor_aptx_encoder.cc
new file mode 100644
index 0000000..05a9114
--- /dev/null
+++ b/stack/a2dp/a2dp_vendor_aptx_encoder.cc
@@ -0,0 +1,581 @@
+/*
+ * 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_vendor_aptx_encoder"
+
+#include "a2dp_vendor_aptx_encoder.h"
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx.h"
+#include "bt_common.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+//
+// Encoder for aptX Source Codec
+//
+
+//
+// The aptX encoder shared library, and the functions to use
+//
+static const char* APTX_ENCODER_LIB_NAME = "libaptX.so";
+static void* aptx_encoder_lib_handle = NULL;
+
+static const char* APTX_ENCODER_INIT_NAME = "aptxbtenc_init";
+typedef int (*tAPTX_ENCODER_INIT)(void* state, short endian);
+
+static const char* APTX_ENCODER_ENCODE_STEREO_NAME = "aptxbtenc_encodestereo";
+typedef int (*tAPTX_ENCODER_ENCODE_STEREO)(void* state, void* pcmL, void* pcmR,
+ void* buffer);
+
+static const char* APTX_ENCODER_SIZEOF_PARAMS_NAME = "SizeofAptxbtenc";
+typedef int (*tAPTX_ENCODER_SIZEOF_PARAMS)(void);
+
+static tAPTX_ENCODER_INIT aptx_encoder_init_func;
+static tAPTX_ENCODER_ENCODE_STEREO aptx_encoder_encode_stereo_func;
+static tAPTX_ENCODER_SIZEOF_PARAMS aptx_encoder_sizeof_params_func;
+
+// offset
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+#define A2DP_APTX_OFFSET (AVDT_MEDIA_OFFSET + 1)
+#else
+// no RTP header for aptX classic
+#define A2DP_APTX_OFFSET (AVDT_MEDIA_OFFSET - AVDT_MEDIA_HDR_SIZE)
+#endif
+
+#define A2DP_APTX_MAX_PCM_BYTES_PER_READ 1024
+
+typedef struct {
+ uint64_t sleep_time_ns;
+ uint32_t pcm_reads;
+ uint32_t pcm_bytes_per_read;
+ uint32_t aptx_bytes;
+ uint32_t frame_size_counter;
+} tAPTX_FRAMING_PARAMS;
+
+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_aptx_encoder_stats_t;
+
+typedef struct {
+ a2dp_source_read_callback_t read_callback;
+ a2dp_source_enqueue_callback_t enqueue_callback;
+
+ 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
+
+ tA2DP_FEEDING_PARAMS feeding_params;
+ tAPTX_FRAMING_PARAMS framing_params;
+ void* aptx_encoder_state;
+ a2dp_aptx_encoder_stats_t stats;
+} tA2DP_APTX_ENCODER_CB;
+
+static tA2DP_APTX_ENCODER_CB a2dp_aptx_encoder_cb;
+
+static void a2dp_vendor_aptx_encoder_update(uint16_t peer_mtu,
+ A2dpCodecConfig* a2dp_codec_config,
+ bool* p_restart_input,
+ bool* p_restart_output,
+ bool* p_config_updated);
+static void aptx_init_framing_params(tAPTX_FRAMING_PARAMS* framing_params);
+static void aptx_update_framing_params(tAPTX_FRAMING_PARAMS* framing_params);
+static size_t aptx_encode_16bit(tAPTX_FRAMING_PARAMS* framing_params,
+ size_t* data_out_index, uint16_t* data16_in,
+ uint8_t* data_out);
+
+bool A2DP_VendorLoadEncoderAptx(void) {
+ if (aptx_encoder_lib_handle != NULL) return true; // Already loaded
+
+ // Open the encoder library
+ aptx_encoder_lib_handle = dlopen(APTX_ENCODER_LIB_NAME, RTLD_NOW);
+ if (aptx_encoder_lib_handle == NULL) {
+ LOG_ERROR(LOG_TAG, "%s: cannot open aptX encoder library %s: %s", __func__,
+ APTX_ENCODER_LIB_NAME, dlerror());
+ return false;
+ }
+
+ aptx_encoder_init_func = (tAPTX_ENCODER_INIT)dlsym(aptx_encoder_lib_handle,
+ APTX_ENCODER_INIT_NAME);
+ if (aptx_encoder_init_func == NULL) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot find function '%s' in the encoder library: %s",
+ __func__, APTX_ENCODER_INIT_NAME, dlerror());
+ A2DP_VendorUnloadEncoderAptx();
+ return false;
+ }
+
+ aptx_encoder_encode_stereo_func = (tAPTX_ENCODER_ENCODE_STEREO)dlsym(
+ aptx_encoder_lib_handle, APTX_ENCODER_ENCODE_STEREO_NAME);
+ if (aptx_encoder_encode_stereo_func == NULL) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot find function '%s' in the encoder library: %s",
+ __func__, APTX_ENCODER_ENCODE_STEREO_NAME, dlerror());
+ A2DP_VendorUnloadEncoderAptx();
+ return false;
+ }
+
+ aptx_encoder_sizeof_params_func = (tAPTX_ENCODER_SIZEOF_PARAMS)dlsym(
+ aptx_encoder_lib_handle, APTX_ENCODER_SIZEOF_PARAMS_NAME);
+ if (aptx_encoder_sizeof_params_func == NULL) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot find function '%s' in the encoder library: %s",
+ __func__, APTX_ENCODER_SIZEOF_PARAMS_NAME, dlerror());
+ A2DP_VendorUnloadEncoderAptx();
+ return false;
+ }
+
+ return true;
+}
+
+void A2DP_VendorUnloadEncoderAptx(void) {
+ aptx_encoder_init_func = NULL;
+ aptx_encoder_encode_stereo_func = NULL;
+ aptx_encoder_sizeof_params_func = NULL;
+
+ if (aptx_encoder_lib_handle != NULL) {
+ dlclose(aptx_encoder_lib_handle);
+ aptx_encoder_lib_handle = NULL;
+ }
+}
+
+void a2dp_vendor_aptx_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) {
+ memset(&a2dp_aptx_encoder_cb, 0, sizeof(a2dp_aptx_encoder_cb));
+
+ a2dp_aptx_encoder_cb.stats.session_start_us = time_get_os_boottime_us();
+
+ a2dp_aptx_encoder_cb.read_callback = read_callback;
+ a2dp_aptx_encoder_cb.enqueue_callback = enqueue_callback;
+ a2dp_aptx_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+ a2dp_aptx_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+ a2dp_aptx_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+ a2dp_aptx_encoder_cb.timestamp = 0;
+
+ /* aptX encoder config */
+ a2dp_aptx_encoder_cb.use_SCMS_T = false; // TODO: should be a parameter
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+ a2dp_aptx_encoder_cb.use_SCMS_T = true;
+#endif
+
+ a2dp_aptx_encoder_cb.aptx_encoder_state =
+ osi_malloc(aptx_encoder_sizeof_params_func());
+ if (a2dp_aptx_encoder_cb.aptx_encoder_state != NULL) {
+ aptx_encoder_init_func(a2dp_aptx_encoder_cb.aptx_encoder_state, 0);
+ } else {
+ LOG_ERROR(LOG_TAG, "%s: Cannot allocate aptX encoder state", __func__);
+ // TODO: Return an error?
+ }
+
+ // 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_vendor_aptx_encoder_update(a2dp_aptx_encoder_cb.peer_mtu,
+ a2dp_codec_config, &restart_input,
+ &restart_output, &config_updated);
+ aptx_init_framing_params(&a2dp_aptx_encoder_cb.framing_params);
+}
+
+bool A2dpCodecConfigAptx::updateEncoderUserConfig(
+ const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input,
+ bool* p_restart_output, bool* p_config_updated) {
+ a2dp_aptx_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+ a2dp_aptx_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+ a2dp_aptx_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+ a2dp_aptx_encoder_cb.timestamp = 0;
+
+ if (a2dp_aptx_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_vendor_aptx_encoder_update(a2dp_aptx_encoder_cb.peer_mtu, this,
+ p_restart_input, p_restart_output,
+ p_config_updated);
+ return true;
+}
+
+// Update the A2DP aptX encoder.
+// |peer_mtu| is the peer MTU.
+// |a2dp_codec_config| is the A2DP codec to use for the update.
+static void a2dp_vendor_aptx_encoder_update(uint16_t peer_mtu,
+ A2dpCodecConfig* a2dp_codec_config,
+ bool* p_restart_input,
+ bool* p_restart_output,
+ bool* p_config_updated) {
+ uint8_t codec_info[AVDT_CODEC_SIZE];
+
+ *p_restart_input = false;
+ *p_restart_output = false;
+ *p_config_updated = false;
+ 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
+ a2dp_aptx_encoder_cb.feeding_params.sample_rate =
+ A2DP_VendorGetTrackSampleRateAptx(p_codec_info);
+ a2dp_aptx_encoder_cb.feeding_params.bits_per_sample =
+ A2DP_VendorGetTrackBitsPerSampleAptx(p_codec_info);
+ a2dp_aptx_encoder_cb.feeding_params.channel_count =
+ A2DP_VendorGetTrackChannelCountAptx(p_codec_info);
+}
+
+void a2dp_vendor_aptx_encoder_cleanup(void) {
+ osi_free(a2dp_aptx_encoder_cb.aptx_encoder_state);
+ memset(&a2dp_aptx_encoder_cb, 0, sizeof(a2dp_aptx_encoder_cb));
+}
+
+void a2dp_vendor_aptx_feeding_init(
+ const tA2DP_FEEDING_PARAMS* p_feeding_params) {
+ LOG_DEBUG(LOG_TAG,
+ "%s: PCM feeding: sample_rate:%d bits_per_sample:%d "
+ "channel_count:%d",
+ __func__, p_feeding_params->sample_rate,
+ p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+
+ // Check the PCM feeding sample_rate
+ switch (p_feeding_params->sample_rate) {
+ case 44100:
+ case 48000:
+ break;
+ default:
+ LOG_WARN(LOG_TAG, "%s: feeding PCM sample_rate %u is not supported",
+ __func__, p_feeding_params->sample_rate);
+ return;
+ }
+
+ // Check the bits per sample
+ switch (p_feeding_params->bits_per_sample) {
+ case 16:
+ break;
+ default:
+ LOG_WARN(LOG_TAG, "%s: feeding PCM bits_per_sample %u is not supported",
+ __func__, p_feeding_params->bits_per_sample);
+ return;
+ }
+
+ // Check the number of channels
+ switch (p_feeding_params->channel_count) {
+ case 1: // Mono
+ case 2: // Stereo
+ break;
+ default:
+ LOG_WARN(LOG_TAG, "%s: feeding PCM channel_count %u is not supported",
+ __func__, p_feeding_params->channel_count);
+ return;
+ }
+
+ bool reconfig_needed = false;
+ if (a2dp_aptx_encoder_cb.feeding_params.sample_rate !=
+ p_feeding_params->sample_rate) {
+ LOG_DEBUG(LOG_TAG,
+ "%s: codec reconfiguration: feeding PCM sample_rate "
+ "from %u to %u",
+ __func__, a2dp_aptx_encoder_cb.feeding_params.sample_rate,
+ p_feeding_params->sample_rate);
+ reconfig_needed = true;
+ a2dp_aptx_encoder_cb.feeding_params.sample_rate =
+ p_feeding_params->sample_rate;
+ }
+ if (a2dp_aptx_encoder_cb.feeding_params.bits_per_sample !=
+ p_feeding_params->bits_per_sample) {
+ LOG_DEBUG(LOG_TAG,
+ "%s: aptX reconfiguration needed: "
+ "feeding PCM bits_per_sample from %u to %u",
+ __func__, a2dp_aptx_encoder_cb.feeding_params.bits_per_sample,
+ p_feeding_params->bits_per_sample);
+ a2dp_aptx_encoder_cb.feeding_params.bits_per_sample =
+ p_feeding_params->bits_per_sample;
+ reconfig_needed = true;
+ }
+ if (a2dp_aptx_encoder_cb.feeding_params.channel_count !=
+ p_feeding_params->channel_count) {
+ LOG_DEBUG(LOG_TAG,
+ "%s: aptX reconfiguration needed: "
+ "feeding PCM channel_count from %u to %u",
+ __func__, a2dp_aptx_encoder_cb.feeding_params.channel_count,
+ p_feeding_params->channel_count);
+ a2dp_aptx_encoder_cb.feeding_params.channel_count =
+ p_feeding_params->channel_count;
+ reconfig_needed = true;
+ }
+
+ if (reconfig_needed) {
+ 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);
+ aptx_init_framing_params(&a2dp_aptx_encoder_cb.framing_params);
+ } else {
+ LOG_DEBUG(LOG_TAG, "%s: no aptX reconfiguration needed", __func__);
+ }
+}
+
+//
+// Initialize the framing parameters, and set those that don't change
+// while streaming (e.g., 'sleep_time_ns').
+//
+static void aptx_init_framing_params(tAPTX_FRAMING_PARAMS* framing_params) {
+ framing_params->sleep_time_ns = 0;
+ framing_params->pcm_reads = 0;
+ framing_params->pcm_bytes_per_read = 0;
+ framing_params->aptx_bytes = 0;
+ framing_params->frame_size_counter = 0;
+
+ if (a2dp_aptx_encoder_cb.feeding_params.sample_rate == 48000) {
+ if (a2dp_aptx_encoder_cb.use_SCMS_T) {
+ framing_params->sleep_time_ns = 13000000;
+ } else {
+ framing_params->sleep_time_ns = 14000000;
+ }
+ } else {
+ // Assume the sample rate is 44100
+ if (a2dp_aptx_encoder_cb.use_SCMS_T) {
+ framing_params->sleep_time_ns = 14000000;
+ } else {
+ framing_params->sleep_time_ns = 15000000;
+ }
+ }
+
+ LOG_DEBUG(LOG_TAG, "%s: sleep_time_ns = %" PRIu64, __func__,
+ framing_params->sleep_time_ns);
+}
+
+//
+// Set frame size and transmission interval needed to stream the required
+// sample rate using 2-DH5 packets for aptX and 2-DH3 packets for aptX-LL.
+// With SCMS-T enabled we need to reserve room for extra headers added later.
+// Packets are always sent at equals time intervals but to achieve the
+// required sample rate, the frame size needs to change on occasion.
+//
+// Also need to specify how many of the required PCM samples are read at a
+// time:
+// aptx_bytes = pcm_reads * pcm_bytes_per_read / 4
+// and
+// number of aptX samples produced = pcm_bytes_per_read / 16
+//
+static void aptx_update_framing_params(tAPTX_FRAMING_PARAMS* framing_params) {
+ if (a2dp_aptx_encoder_cb.feeding_params.sample_rate == 48000) {
+ if (a2dp_aptx_encoder_cb.use_SCMS_T) {
+ framing_params->aptx_bytes = 624;
+ framing_params->pcm_bytes_per_read = 208;
+ framing_params->pcm_reads = 12;
+ } else {
+ framing_params->aptx_bytes = 672;
+ framing_params->pcm_bytes_per_read = 224;
+ framing_params->pcm_reads = 12;
+ }
+ } else {
+ // Assume the sample rate is 44100
+ if (a2dp_aptx_encoder_cb.use_SCMS_T) {
+ if (++framing_params->frame_size_counter < 20) {
+ framing_params->aptx_bytes = 616;
+ framing_params->pcm_bytes_per_read = 224;
+ framing_params->pcm_reads = 11;
+ } else {
+ framing_params->aptx_bytes = 644;
+ framing_params->pcm_bytes_per_read = 368;
+ framing_params->pcm_reads = 7;
+ framing_params->frame_size_counter = 0;
+ }
+ } else {
+ if (++framing_params->frame_size_counter < 8) {
+ framing_params->aptx_bytes = 660;
+ framing_params->pcm_bytes_per_read = 240;
+ framing_params->pcm_reads = 11;
+ } else {
+ framing_params->aptx_bytes = 672;
+ framing_params->pcm_bytes_per_read = 224;
+ framing_params->pcm_reads = 12;
+ framing_params->frame_size_counter = 0;
+ }
+ }
+ }
+
+ LOG_DEBUG(LOG_TAG,
+ "%s: sleep_time_ns = %" PRIu64
+ " aptx_bytes = %u "
+ "pcm_bytes_per_read = %u pcm_reads = %u frame_size_counter = %u",
+ __func__, framing_params->sleep_time_ns, framing_params->aptx_bytes,
+ framing_params->pcm_bytes_per_read, framing_params->pcm_reads,
+ framing_params->frame_size_counter);
+}
+
+void a2dp_vendor_aptx_feeding_reset(void) {
+ aptx_init_framing_params(&a2dp_aptx_encoder_cb.framing_params);
+}
+
+void a2dp_vendor_aptx_feeding_flush(void) {
+ aptx_init_framing_params(&a2dp_aptx_encoder_cb.framing_params);
+}
+
+period_ms_t a2dp_vendor_aptx_get_encoder_interval_ms(void) {
+ return a2dp_aptx_encoder_cb.framing_params.sleep_time_ns / (1000 * 1000);
+}
+
+void a2dp_vendor_aptx_send_frames(uint64_t timestamp_us) {
+ tAPTX_FRAMING_PARAMS* framing_params = &a2dp_aptx_encoder_cb.framing_params;
+
+ // Prepare the packet to send
+ BT_HDR* p_buf = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
+ p_buf->offset = A2DP_APTX_OFFSET;
+ p_buf->len = 0;
+ p_buf->layer_specific = 0;
+
+ uint8_t* encoded_ptr = (uint8_t*)(p_buf + 1);
+ encoded_ptr += p_buf->offset;
+
+ aptx_update_framing_params(framing_params);
+
+ //
+ // Read the PCM data and encode it
+ //
+ LOG_VERBOSE(LOG_TAG, "%s: %u PCM reads of size %u", __func__,
+ framing_params->pcm_reads, framing_params->pcm_bytes_per_read);
+ size_t encoded_ptr_index = 0;
+ size_t pcm_bytes_encoded = 0;
+ a2dp_aptx_encoder_cb.stats.media_read_total_expected_packets++;
+ a2dp_aptx_encoder_cb.stats.media_read_total_expected_reads_count +=
+ framing_params->pcm_reads;
+ a2dp_aptx_encoder_cb.stats.media_read_total_expected_read_bytes +=
+ framing_params->pcm_reads * framing_params->pcm_bytes_per_read;
+ for (size_t reads = 0; reads < framing_params->pcm_reads; reads++) {
+ uint16_t read_buffer16[A2DP_APTX_MAX_PCM_BYTES_PER_READ / sizeof(uint16_t)];
+ size_t pcm_bytes_read = a2dp_aptx_encoder_cb.read_callback(
+ (uint8_t*)read_buffer16, framing_params->pcm_bytes_per_read);
+ a2dp_aptx_encoder_cb.stats.media_read_total_actual_read_bytes +=
+ pcm_bytes_read;
+ if (pcm_bytes_read < framing_params->pcm_bytes_per_read) {
+ LOG_WARN(LOG_TAG,
+ "%s: underflow at PCM reading iteration %zu: read %zu "
+ "instead of %d",
+ __func__, reads, pcm_bytes_read,
+ framing_params->pcm_bytes_per_read);
+ break;
+ }
+ a2dp_aptx_encoder_cb.stats.media_read_total_actual_reads_count++;
+ pcm_bytes_encoded += aptx_encode_16bit(framing_params, &encoded_ptr_index,
+ read_buffer16, encoded_ptr);
+ }
+
+ // Compute the number of encoded bytes
+ const int COMPRESSION_RATIO = 4;
+ size_t encoded_bytes = pcm_bytes_encoded / COMPRESSION_RATIO;
+ p_buf->len += encoded_bytes;
+ LOG_DEBUG(LOG_TAG, "%s: encoded %zu PCM bytes to %zu", __func__,
+ pcm_bytes_encoded, encoded_bytes);
+
+ // Update the RTP timestamp
+ *((uint32_t*)(p_buf + 1)) = a2dp_aptx_encoder_cb.timestamp;
+ const uint8_t BYTES_PER_FRAME = 2;
+ uint32_t rtp_timestamp =
+ (pcm_bytes_encoded / a2dp_aptx_encoder_cb.feeding_params.channel_count) /
+ BYTES_PER_FRAME;
+ a2dp_aptx_encoder_cb.timestamp += rtp_timestamp;
+
+ if (p_buf->len > 0) {
+ a2dp_aptx_encoder_cb.enqueue_callback(p_buf, 1);
+ } else {
+ a2dp_aptx_encoder_cb.stats.media_read_total_dropped_packets++;
+ osi_free(p_buf);
+ }
+}
+
+static size_t aptx_encode_16bit(tAPTX_FRAMING_PARAMS* framing_params,
+ size_t* data_out_index, uint16_t* data16_in,
+ uint8_t* data_out) {
+ size_t pcm_bytes_encoded = 0;
+ size_t frame = 0;
+
+ for (size_t aptx_samples = 0;
+ aptx_samples < framing_params->pcm_bytes_per_read / 16; aptx_samples++) {
+ uint32_t pcmL[4];
+ uint32_t pcmR[4];
+ uint16_t encoded_sample[2];
+
+ for (size_t i = 0, j = frame; i < 4; i++, j++) {
+ pcmL[i] = (uint16_t) * (data16_in + (2 * j));
+ pcmR[i] = (uint16_t) * (data16_in + ((2 * j) + 1));
+ }
+
+ aptx_encoder_encode_stereo_func(a2dp_aptx_encoder_cb.aptx_encoder_state,
+ &pcmL, &pcmR, &encoded_sample);
+
+ data_out[*data_out_index + 0] = (uint8_t)((encoded_sample[0] >> 8) & 0xff);
+ data_out[*data_out_index + 1] = (uint8_t)((encoded_sample[0] >> 0) & 0xff);
+ data_out[*data_out_index + 2] = (uint8_t)((encoded_sample[1] >> 8) & 0xff);
+ data_out[*data_out_index + 3] = (uint8_t)((encoded_sample[1] >> 0) & 0xff);
+
+ frame += 4;
+ pcm_bytes_encoded += 16;
+ *data_out_index += 4;
+ }
+
+ return pcm_bytes_encoded;
+}
+
+void a2dp_vendor_aptx_debug_codec_dump(int fd) {
+ a2dp_aptx_encoder_stats_t* stats = &a2dp_aptx_encoder_cb.stats;
+
+ dprintf(fd, "\nA2DP aptX 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_vendor_aptx_hd.cc b/stack/a2dp/a2dp_vendor_aptx_hd.cc
new file mode 100644
index 0000000..f86f4c2
--- /dev/null
+++ b/stack/a2dp/a2dp_vendor_aptx_hd.cc
@@ -0,0 +1,876 @@
+/*
+ * 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 aptX-HD Codec Information
+ * Element and Media Payload.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "a2dp_vendor_aptx_hd"
+
+#include "bt_target.h"
+
+#include "a2dp_vendor_aptx_hd.h"
+
+#include <string.h>
+
+#include <base/logging.h>
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx_hd_encoder.h"
+#include "bt_utils.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+// data type for the aptX-HD Codec Information Element */
+typedef struct {
+ uint32_t vendorId;
+ uint16_t codecId; /* Codec ID for aptX-HD */
+ uint8_t sampleRate; /* Sampling Frequency */
+ uint8_t channelMode; /* STEREO/DUAL/MONO */
+ uint8_t acl_sprint_reserved0;
+ uint8_t acl_sprint_reserved1;
+ uint8_t acl_sprint_reserved2;
+ uint8_t acl_sprint_reserved3;
+ btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+} tA2DP_APTX_HD_CIE;
+
+/* aptX-HD Source codec capabilities */
+static const tA2DP_APTX_HD_CIE a2dp_aptx_hd_caps = {
+ A2DP_APTX_HD_VENDOR_ID, /* vendorId */
+ A2DP_APTX_HD_CODEC_ID_BLUETOOTH, /* codecId */
+ (A2DP_APTX_HD_SAMPLERATE_44100 |
+ A2DP_APTX_HD_SAMPLERATE_48000), /* sampleRate */
+ A2DP_APTX_HD_CHANNELS_STEREO, /* channelMode */
+ A2DP_APTX_HD_ACL_SPRINT_RESERVED0, /* acl_sprint_reserved0 */
+ A2DP_APTX_HD_ACL_SPRINT_RESERVED1, /* acl_sprint_reserved1 */
+ A2DP_APTX_HD_ACL_SPRINT_RESERVED2, /* acl_sprint_reserved2 */
+ A2DP_APTX_HD_ACL_SPRINT_RESERVED3, /* acl_sprint_reserved3 */
+ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24 /* bits_per_sample */
+};
+
+/* Default aptX-HD codec configuration */
+static const tA2DP_APTX_HD_CIE a2dp_aptx_hd_default_config = {
+ A2DP_APTX_HD_VENDOR_ID, /* vendorId */
+ A2DP_APTX_HD_CODEC_ID_BLUETOOTH, /* codecId */
+ A2DP_APTX_HD_SAMPLERATE_44100, /* sampleRate */
+ A2DP_APTX_HD_CHANNELS_STEREO, /* channelMode */
+ A2DP_APTX_HD_ACL_SPRINT_RESERVED0, /* acl_sprint_reserved0 */
+ A2DP_APTX_HD_ACL_SPRINT_RESERVED1, /* acl_sprint_reserved1 */
+ A2DP_APTX_HD_ACL_SPRINT_RESERVED2, /* acl_sprint_reserved2 */
+ A2DP_APTX_HD_ACL_SPRINT_RESERVED3, /* acl_sprint_reserved3 */
+ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24 /* bits_per_sample */
+};
+
+static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_aptx_hd = {
+ a2dp_vendor_aptx_hd_encoder_init,
+ a2dp_vendor_aptx_hd_encoder_cleanup,
+ a2dp_vendor_aptx_hd_feeding_init,
+ a2dp_vendor_aptx_hd_feeding_reset,
+ a2dp_vendor_aptx_hd_feeding_flush,
+ a2dp_vendor_aptx_hd_get_encoder_interval_ms,
+ a2dp_vendor_aptx_hd_send_frames,
+ a2dp_vendor_aptx_hd_debug_codec_dump};
+
+UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAptxHd(
+ const tA2DP_APTX_HD_CIE* p_cap, const uint8_t* p_codec_info,
+ bool is_peer_codec_info);
+
+// Builds the aptX-HD 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 aptX-HD 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_BuildInfoAptxHd(uint8_t media_type,
+ const tA2DP_APTX_HD_CIE* p_ie,
+ uint8_t* p_result) {
+ tA2DP_STATUS status;
+
+ if (p_ie == NULL || p_result == NULL) {
+ status = A2DP_INVALID_PARAMS;
+ } else {
+ status = A2DP_SUCCESS;
+ *p_result++ = A2DP_APTX_HD_CODEC_LEN;
+ *p_result++ = media_type;
+ *p_result++ = A2DP_MEDIA_CT_NON_A2DP;
+ *p_result++ = (uint8_t)(p_ie->vendorId & 0x000000FF);
+ *p_result++ = (uint8_t)((p_ie->vendorId & 0x0000FF00) >> 8);
+ *p_result++ = (uint8_t)((p_ie->vendorId & 0x00FF0000) >> 16);
+ *p_result++ = (uint8_t)((p_ie->vendorId & 0xFF000000) >> 24);
+ *p_result++ = (uint8_t)(p_ie->codecId & 0x00FF);
+ *p_result++ = (uint8_t)((p_ie->codecId & 0xFF00) >> 8);
+ *p_result++ = p_ie->sampleRate | p_ie->channelMode;
+ *p_result++ = p_ie->acl_sprint_reserved0;
+ *p_result++ = p_ie->acl_sprint_reserved1;
+ *p_result++ = p_ie->acl_sprint_reserved2;
+ *p_result++ = p_ie->acl_sprint_reserved3;
+ }
+ return status;
+}
+
+// Parses the aptX-HD 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_ParseInfoAptxHd(tA2DP_APTX_HD_CIE* p_ie,
+ const uint8_t* p_codec_info,
+ bool is_capability) {
+ tA2DP_STATUS status = A2DP_SUCCESS;
+ 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_APTX_HD_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_NON_A2DP) {
+ return A2DP_WRONG_CODEC;
+ }
+
+ // Check the Vendor ID and Codec ID */
+ p_ie->vendorId = (*p_codec_info & 0x000000FF) |
+ (*(p_codec_info + 1) << 8 & 0x0000FF00) |
+ (*(p_codec_info + 2) << 16 & 0x00FF0000) |
+ (*(p_codec_info + 3) << 24 & 0xFF000000);
+ p_codec_info = p_codec_info + 4;
+ p_ie->codecId =
+ (*p_codec_info & 0x00FF) | (*(p_codec_info + 1) << 8 & 0xFF00);
+ p_codec_info = p_codec_info + 2;
+ if (p_ie->vendorId != A2DP_APTX_HD_VENDOR_ID ||
+ p_ie->codecId != A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+ return A2DP_WRONG_CODEC;
+ }
+
+ p_ie->channelMode = *p_codec_info & 0x0F;
+ p_ie->sampleRate = *p_codec_info & 0xF0;
+
+ p_codec_info++;
+ p_ie->acl_sprint_reserved0 = *(p_codec_info++);
+ p_ie->acl_sprint_reserved1 = *(p_codec_info++);
+ p_ie->acl_sprint_reserved2 = *(p_codec_info++);
+ p_ie->acl_sprint_reserved3 = *(p_codec_info++);
+
+ status = A2DP_SUCCESS;
+
+ if (is_capability) return status;
+
+ if (A2DP_BitsSet(p_ie->sampleRate) != A2DP_SET_ONE_BIT)
+ status = A2DP_BAD_SAMP_FREQ;
+ if (A2DP_BitsSet(p_ie->channelMode) != A2DP_SET_ONE_BIT)
+ status = A2DP_BAD_CH_MODE;
+
+ return status;
+}
+
+bool A2DP_IsVendorSourceCodecValidAptxHd(const uint8_t* p_codec_info) {
+ tA2DP_APTX_HD_CIE cfg_cie;
+
+ /* Use a liberal check when parsing the codec info */
+ return (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, false) ==
+ A2DP_SUCCESS) ||
+ (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsVendorPeerSinkCodecValidAptxHd(const uint8_t* p_codec_info) {
+ tA2DP_APTX_HD_CIE cfg_cie;
+
+ /* Use a liberal check when parsing the codec info */
+ return (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, false) ==
+ A2DP_SUCCESS) ||
+ (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+// Checks whether A2DP aptX-HD codec configuration matches with a device's
+// codec capabilities. |p_cap| is the aptX-HD 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_CodecInfoMatchesCapabilityAptxHd(
+ const tA2DP_APTX_HD_CIE* p_cap, const uint8_t* p_codec_info,
+ bool is_capability) {
+ tA2DP_STATUS status;
+ tA2DP_APTX_HD_CIE cfg_cie;
+
+ /* parse configuration */
+ status = A2DP_ParseInfoAptxHd(&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, "FREQ peer: 0x%x, capability 0x%x", cfg_cie.sampleRate,
+ p_cap->sampleRate);
+ LOG_DEBUG(LOG_TAG, "CH_MODE peer: 0x%x, capability 0x%x", cfg_cie.channelMode,
+ p_cap->channelMode);
+
+ /* sampling frequency */
+ if ((cfg_cie.sampleRate & p_cap->sampleRate) == 0) return A2DP_NS_SAMP_FREQ;
+
+ /* channel mode */
+ if ((cfg_cie.channelMode & p_cap->channelMode) == 0) return A2DP_NS_CH_MODE;
+
+ return A2DP_SUCCESS;
+}
+
+bool A2DP_VendorUsesRtpHeaderAptxHd(UNUSED_ATTR bool content_protection_enabled,
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+ return true;
+}
+
+const char* A2DP_VendorCodecNameAptxHd(
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+ return "aptX-HD";
+}
+
+bool A2DP_VendorCodecTypeEqualsAptxHd(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b) {
+ tA2DP_APTX_HD_CIE aptx_hd_cie_a;
+ tA2DP_APTX_HD_CIE aptx_hd_cie_b;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAptxHd(&aptx_hd_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_ParseInfoAptxHd(&aptx_hd_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_VendorCodecEqualsAptxHd(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b) {
+ tA2DP_APTX_HD_CIE aptx_hd_cie_a;
+ tA2DP_APTX_HD_CIE aptx_hd_cie_b;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAptxHd(&aptx_hd_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_ParseInfoAptxHd(&aptx_hd_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 (aptx_hd_cie_a.sampleRate == aptx_hd_cie_b.sampleRate) &&
+ (aptx_hd_cie_a.channelMode == aptx_hd_cie_b.channelMode);
+}
+
+int A2DP_VendorGetTrackSampleRateAptxHd(const uint8_t* p_codec_info) {
+ tA2DP_APTX_HD_CIE aptx_hd_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAptxHd(&aptx_hd_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;
+ }
+
+ if (aptx_hd_cie.sampleRate == A2DP_APTX_HD_SAMPLERATE_44100) return 44100;
+ if (aptx_hd_cie.sampleRate == A2DP_APTX_HD_SAMPLERATE_48000) return 48000;
+
+ return -1;
+}
+
+int A2DP_VendorGetTrackBitsPerSampleAptxHd(const uint8_t* p_codec_info) {
+ tA2DP_APTX_HD_CIE aptx_hd_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAptxHd(&aptx_hd_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 24; // For aptX-HD we always use 24 bits per audio sample
+}
+
+int A2DP_VendorGetTrackChannelCountAptxHd(const uint8_t* p_codec_info) {
+ tA2DP_APTX_HD_CIE aptx_hd_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status =
+ A2DP_ParseInfoAptxHd(&aptx_hd_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 (aptx_hd_cie.channelMode) {
+ case A2DP_APTX_HD_CHANNELS_MONO:
+ return 1;
+ case A2DP_APTX_HD_CHANNELS_STEREO:
+ return 2;
+ }
+
+ return -1;
+}
+
+bool A2DP_VendorGetPacketTimestampAptxHd(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_VendorBuildCodecHeaderAptxHd(UNUSED_ATTR const uint8_t* p_codec_info,
+ UNUSED_ATTR BT_HDR* p_buf,
+ UNUSED_ATTR uint16_t frames_per_packet) {
+ // Nothing to do
+ return true;
+}
+
+void A2DP_VendorDumpCodecInfoAptxHd(const uint8_t* p_codec_info) {
+ tA2DP_STATUS a2dp_status;
+ tA2DP_APTX_HD_CIE aptx_hd_cie;
+
+ LOG_DEBUG(LOG_TAG, "%s", __func__);
+
+ a2dp_status = A2DP_ParseInfoAptxHd(&aptx_hd_cie, p_codec_info, true);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: A2DP_ParseInfoAptxHd fail:%d", __func__,
+ a2dp_status);
+ return;
+ }
+
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: 0x%x", aptx_hd_cie.sampleRate);
+ if (aptx_hd_cie.sampleRate == A2DP_APTX_HD_SAMPLERATE_44100) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (44100)");
+ }
+ if (aptx_hd_cie.sampleRate == A2DP_APTX_HD_SAMPLERATE_48000) {
+ LOG_DEBUG(LOG_TAG, "\tsamp_freq: (48000)");
+ }
+
+ LOG_DEBUG(LOG_TAG, "\tch_mode: 0x%x", aptx_hd_cie.channelMode);
+ if (aptx_hd_cie.channelMode == A2DP_APTX_HD_CHANNELS_MONO) {
+ LOG_DEBUG(LOG_TAG, "\tch_mode: (Mono)");
+ }
+ if (aptx_hd_cie.channelMode == A2DP_APTX_HD_CHANNELS_STEREO) {
+ LOG_DEBUG(LOG_TAG, "\tch_mode: (Stereo)");
+ }
+}
+
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceAptxHd(
+ const uint8_t* p_codec_info) {
+ if (!A2DP_IsVendorSourceCodecValidAptxHd(p_codec_info)) return NULL;
+
+ return &a2dp_encoder_interface_aptx_hd;
+}
+
+bool A2DP_VendorAdjustCodecAptxHd(uint8_t* p_codec_info) {
+ tA2DP_APTX_HD_CIE cfg_cie;
+
+ // Nothing to do: just verify the codec info is valid
+ if (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS)
+ return false;
+
+ return true;
+}
+
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexAptxHd(
+ const uint8_t* p_codec_info) {
+ return BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD;
+}
+
+const char* A2DP_VendorCodecIndexStrAptxHd(void) { return "aptX-HD"; }
+
+bool A2DP_VendorInitCodecConfigAptxHd(tAVDT_CFG* p_cfg) {
+ if (A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_hd_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;
+}
+
+A2dpCodecConfigAptxHd::A2dpCodecConfigAptxHd()
+ : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD, "aptX-HD") {}
+
+A2dpCodecConfigAptxHd::~A2dpCodecConfigAptxHd() {}
+
+bool A2dpCodecConfigAptxHd::init() {
+ if (!isValid()) return false;
+
+ // Load the encoder
+ if (!A2DP_VendorLoadEncoderAptxHd()) {
+ 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(uint8_t sampleRate,
+ tA2DP_APTX_HD_CIE* p_result,
+ btav_a2dp_codec_config_t* p_codec_config) {
+ if (sampleRate & A2DP_APTX_HD_SAMPLERATE_48000) {
+ p_result->sampleRate = A2DP_APTX_HD_SAMPLERATE_48000;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+ return true;
+ }
+ if (sampleRate & A2DP_APTX_HD_SAMPLERATE_44100) {
+ p_result->sampleRate = A2DP_APTX_HD_SAMPLERATE_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, uint8_t sampleRate,
+ tA2DP_APTX_HD_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_APTX_HD_SAMPLERATE_44100) {
+ p_result->sampleRate = A2DP_APTX_HD_SAMPLERATE_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_APTX_HD_SAMPLERATE_48000) {
+ p_result->sampleRate = A2DP_APTX_HD_SAMPLERATE_48000;
+ p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+ return true;
+ }
+ break;
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+ 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.
+// The result is stored in |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_bits_per_sample(
+ btav_a2dp_codec_config_t* p_codec_config) {
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+ return true;
+}
+
+//
+// Selects the audio bits per sample from |p_codec_audio_config|.
+// The result is stored in |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_config_t* p_codec_config) {
+ switch (p_codec_audio_config->bits_per_sample) {
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+ p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+ return true;
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+ 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_APTX_HD_CIE* p_result,
+ btav_a2dp_codec_config_t* p_codec_config) {
+ if (channelMode & A2DP_APTX_HD_CHANNELS_STEREO) {
+ p_result->channelMode = A2DP_APTX_HD_CHANNELS_STEREO;
+ p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+ return true;
+ }
+ if (channelMode & A2DP_APTX_HD_CHANNELS_MONO) {
+ p_result->channelMode = A2DP_APTX_HD_CHANNELS_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_APTX_HD_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_APTX_HD_CHANNELS_MONO) {
+ p_result->channelMode = A2DP_APTX_HD_CHANNELS_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_APTX_HD_CHANNELS_STEREO) {
+ p_result->channelMode = A2DP_APTX_HD_CHANNELS_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 A2dpCodecConfigAptxHd::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_APTX_HD_CIE sink_info_cie;
+ tA2DP_APTX_HD_CIE result_config_cie;
+ uint8_t sampleRate;
+ uint8_t channelMode;
+
+ // 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_ParseInfoAptxHd(&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));
+ result_config_cie.vendorId = a2dp_aptx_hd_caps.vendorId;
+ result_config_cie.codecId = a2dp_aptx_hd_caps.codecId;
+
+ //
+ // Select the sample frequency
+ //
+ sampleRate = a2dp_aptx_hd_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_APTX_HD_SAMPLERATE_44100) {
+ result_config_cie.sampleRate = A2DP_APTX_HD_SAMPLERATE_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_APTX_HD_SAMPLERATE_48000) {
+ result_config_cie.sampleRate = A2DP_APTX_HD_SAMPLERATE_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:
+ case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+ 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_APTX_HD_SAMPLERATE_44100)
+ codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+ if (sampleRate & A2DP_APTX_HD_SAMPLERATE_48000)
+ codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+
+ // 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_aptx_hd_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_aptx_hd_caps.sampleRate, sink_info_cie.sampleRate);
+ goto fail;
+ }
+
+ //
+ // Select the bits per sample
+ //
+ // NOTE: this information is NOT included in the aptX-HD A2DP codec
+ // description that is sent OTA.
+ 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_24:
+ 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_16:
+ case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+ case 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 = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+
+ // No user preference - try the codec audio config
+ if (select_audio_bits_per_sample(&codec_audio_config_, &codec_config_)) {
+ break;
+ }
+
+ // No user preference - try the default config
+ if (select_best_bits_per_sample(&codec_config_)) {
+ break;
+ }
+
+ // No user preference - use the best match
+ // NOTE: no-op - kept here for consistency
+ if (select_best_bits_per_sample(&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: user preference = 0x%x",
+ __func__, codec_user_config_.bits_per_sample);
+ goto fail;
+ }
+
+ //
+ // Select the channel mode
+ //
+ channelMode = a2dp_aptx_hd_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_APTX_HD_CHANNELS_MONO) {
+ result_config_cie.channelMode = A2DP_APTX_HD_CHANNELS_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_APTX_HD_CHANNELS_STEREO) {
+ result_config_cie.channelMode = A2DP_APTX_HD_CHANNELS_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_APTX_HD_CHANNELS_MONO)
+ codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+ if (channelMode & A2DP_APTX_HD_CHANNELS_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_aptx_hd_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_aptx_hd_caps.channelMode,
+ sink_info_cie.channelMode);
+ goto fail;
+ }
+
+ //
+ // Set the rest of the fields as bit-wise AND operation
+ //
+ result_config_cie.acl_sprint_reserved0 =
+ a2dp_aptx_hd_caps.acl_sprint_reserved0 &
+ sink_info_cie.acl_sprint_reserved0;
+ result_config_cie.acl_sprint_reserved1 =
+ a2dp_aptx_hd_caps.acl_sprint_reserved1 &
+ sink_info_cie.acl_sprint_reserved1;
+ result_config_cie.acl_sprint_reserved2 =
+ a2dp_aptx_hd_caps.acl_sprint_reserved2 &
+ sink_info_cie.acl_sprint_reserved2;
+ result_config_cie.acl_sprint_reserved3 =
+ a2dp_aptx_hd_caps.acl_sprint_reserved3 &
+ sink_info_cie.acl_sprint_reserved3;
+
+ if (A2DP_BuildInfoAptxHd(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/config, and the
+ // result codec config.
+ if (is_capability) {
+ status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+ ota_codec_peer_capability_);
+ } else {
+ status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+ ota_codec_peer_config_);
+ }
+ CHECK(status == A2DP_SUCCESS);
+ status = A2DP_BuildInfoAptxHd(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_vendor_aptx_hd_encoder.cc b/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc
new file mode 100644
index 0000000..0370302
--- /dev/null
+++ b/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc
@@ -0,0 +1,575 @@
+/*
+ * 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_vendor_aptx_hd_encoder"
+
+#include "a2dp_vendor_aptx_hd_encoder.h"
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx_hd.h"
+#include "bt_common.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+//
+// Encoder for aptX-HD Source Codec
+//
+
+//
+// The aptX-HD encoder shared library, and the functions to use
+//
+static const char* APTX_HD_ENCODER_LIB_NAME = "libaptXHD.so";
+static void* aptx_hd_encoder_lib_handle = NULL;
+
+static const char* APTX_HD_ENCODER_INIT_NAME = "aptxhdbtenc_init";
+typedef int (*tAPTX_HD_ENCODER_INIT)(void* state, short endian);
+
+static const char* APTX_HD_ENCODER_ENCODE_STEREO_NAME =
+ "aptxhdbtenc_encodestereo";
+typedef int (*tAPTX_HD_ENCODER_ENCODE_STEREO)(void* state, void* pcmL,
+ void* pcmR, void* buffer);
+
+static const char* APTX_HD_ENCODER_SIZEOF_PARAMS_NAME = "SizeofAptxhdbtenc";
+typedef int (*tAPTX_HD_ENCODER_SIZEOF_PARAMS)(void);
+
+static tAPTX_HD_ENCODER_INIT aptx_hd_encoder_init_func;
+static tAPTX_HD_ENCODER_ENCODE_STEREO aptx_hd_encoder_encode_stereo_func;
+static tAPTX_HD_ENCODER_SIZEOF_PARAMS aptx_hd_encoder_sizeof_params_func;
+
+// offset
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+#define A2DP_APTX_HD_OFFSET (AVDT_MEDIA_OFFSET + 1)
+#else
+#define A2DP_APTX_HD_OFFSET AVDT_MEDIA_OFFSET
+#endif
+
+#define A2DP_APTX_HD_MAX_PCM_BYTES_PER_READ 1024
+
+typedef struct {
+ uint64_t sleep_time_ns;
+ uint32_t pcm_reads;
+ uint32_t pcm_bytes_per_read;
+ uint32_t aptx_hd_bytes;
+ uint32_t frame_size_counter;
+} tAPTX_HD_FRAMING_PARAMS;
+
+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_aptx_hd_encoder_stats_t;
+
+typedef struct {
+ a2dp_source_read_callback_t read_callback;
+ a2dp_source_enqueue_callback_t enqueue_callback;
+
+ 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
+
+ tA2DP_FEEDING_PARAMS feeding_params;
+ tAPTX_HD_FRAMING_PARAMS framing_params;
+ void* aptx_hd_encoder_state;
+ a2dp_aptx_hd_encoder_stats_t stats;
+} tA2DP_APTX_HD_ENCODER_CB;
+
+static tA2DP_APTX_HD_ENCODER_CB a2dp_aptx_hd_encoder_cb;
+
+static void a2dp_vendor_aptx_hd_encoder_update(
+ uint16_t peer_mtu, A2dpCodecConfig* a2dp_codec_config,
+ bool* p_restart_input, bool* p_restart_output, bool* p_config_updated);
+static void aptx_hd_init_framing_params(
+ tAPTX_HD_FRAMING_PARAMS* framing_params);
+static void aptx_hd_update_framing_params(
+ tAPTX_HD_FRAMING_PARAMS* framing_params);
+static size_t aptx_hd_encode_24bit(tAPTX_HD_FRAMING_PARAMS* framing_params,
+ size_t* data_out_index, uint32_t* data32_in,
+ uint8_t* data_out);
+
+bool A2DP_VendorLoadEncoderAptxHd(void) {
+ if (aptx_hd_encoder_lib_handle != NULL) return true; // Already loaded
+
+ // Open the encoder library
+ aptx_hd_encoder_lib_handle = dlopen(APTX_HD_ENCODER_LIB_NAME, RTLD_NOW);
+ if (aptx_hd_encoder_lib_handle == NULL) {
+ LOG_ERROR(LOG_TAG, "%s: cannot open aptX-HD encoder library %s: %s",
+ __func__, APTX_HD_ENCODER_LIB_NAME, dlerror());
+ return false;
+ }
+
+ aptx_hd_encoder_init_func = (tAPTX_HD_ENCODER_INIT)dlsym(
+ aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_INIT_NAME);
+ if (aptx_hd_encoder_init_func == NULL) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot find function '%s' in the encoder library: %s",
+ __func__, APTX_HD_ENCODER_INIT_NAME, dlerror());
+ A2DP_VendorUnloadEncoderAptxHd();
+ return false;
+ }
+
+ aptx_hd_encoder_encode_stereo_func = (tAPTX_HD_ENCODER_ENCODE_STEREO)dlsym(
+ aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_ENCODE_STEREO_NAME);
+ if (aptx_hd_encoder_encode_stereo_func == NULL) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot find function '%s' in the encoder library: %s",
+ __func__, APTX_HD_ENCODER_ENCODE_STEREO_NAME, dlerror());
+ A2DP_VendorUnloadEncoderAptxHd();
+ return false;
+ }
+
+ aptx_hd_encoder_sizeof_params_func = (tAPTX_HD_ENCODER_SIZEOF_PARAMS)dlsym(
+ aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_SIZEOF_PARAMS_NAME);
+ if (aptx_hd_encoder_sizeof_params_func == NULL) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot find function '%s' in the encoder library: %s",
+ __func__, APTX_HD_ENCODER_SIZEOF_PARAMS_NAME, dlerror());
+ A2DP_VendorUnloadEncoderAptxHd();
+ return false;
+ }
+
+ return true;
+}
+
+void A2DP_VendorUnloadEncoderAptxHd(void) {
+ aptx_hd_encoder_init_func = NULL;
+ aptx_hd_encoder_encode_stereo_func = NULL;
+ aptx_hd_encoder_sizeof_params_func = NULL;
+
+ if (aptx_hd_encoder_lib_handle != NULL) {
+ dlclose(aptx_hd_encoder_lib_handle);
+ aptx_hd_encoder_lib_handle = NULL;
+ }
+}
+
+void a2dp_vendor_aptx_hd_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) {
+ memset(&a2dp_aptx_hd_encoder_cb, 0, sizeof(a2dp_aptx_hd_encoder_cb));
+
+ a2dp_aptx_hd_encoder_cb.stats.session_start_us = time_get_os_boottime_us();
+
+ a2dp_aptx_hd_encoder_cb.read_callback = read_callback;
+ a2dp_aptx_hd_encoder_cb.enqueue_callback = enqueue_callback;
+ a2dp_aptx_hd_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+ a2dp_aptx_hd_encoder_cb.peer_supports_3mbps =
+ p_peer_params->peer_supports_3mbps;
+ a2dp_aptx_hd_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+ a2dp_aptx_hd_encoder_cb.timestamp = 0;
+
+ /* aptX-HD encoder config */
+ a2dp_aptx_hd_encoder_cb.use_SCMS_T = false; // TODO: should be a parameter
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+ a2dp_aptx_hd_encoder_cb.use_SCMS_T = true;
+#endif
+
+ a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state =
+ osi_malloc(aptx_hd_encoder_sizeof_params_func());
+ if (a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state != NULL) {
+ aptx_hd_encoder_init_func(a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state, 0);
+ } else {
+ LOG_ERROR(LOG_TAG, "%s: Cannot allocate aptX-HD encoder state", __func__);
+ // TODO: Return an error?
+ }
+
+ // 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_vendor_aptx_hd_encoder_update(a2dp_aptx_hd_encoder_cb.peer_mtu,
+ a2dp_codec_config, &restart_input,
+ &restart_output, &config_updated);
+ aptx_hd_init_framing_params(&a2dp_aptx_hd_encoder_cb.framing_params);
+}
+
+bool A2dpCodecConfigAptxHd::updateEncoderUserConfig(
+ const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input,
+ bool* p_restart_output, bool* p_config_updated) {
+ a2dp_aptx_hd_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+ a2dp_aptx_hd_encoder_cb.peer_supports_3mbps =
+ p_peer_params->peer_supports_3mbps;
+ a2dp_aptx_hd_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+ a2dp_aptx_hd_encoder_cb.timestamp = 0;
+
+ if (a2dp_aptx_hd_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_vendor_aptx_hd_encoder_update(a2dp_aptx_hd_encoder_cb.peer_mtu, this,
+ p_restart_input, p_restart_output,
+ p_config_updated);
+ return true;
+}
+
+// Update the A2DP aptX-HD encoder.
+// |peer_mtu| is the peer MTU.
+// |a2dp_codec_config| is the A2DP codec to use for the update.
+static void a2dp_vendor_aptx_hd_encoder_update(
+ uint16_t peer_mtu, A2dpCodecConfig* a2dp_codec_config,
+ bool* p_restart_input, bool* p_restart_output, bool* p_config_updated) {
+ uint8_t codec_info[AVDT_CODEC_SIZE];
+
+ *p_restart_input = false;
+ *p_restart_output = false;
+ *p_config_updated = false;
+ 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
+ a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate =
+ A2DP_VendorGetTrackSampleRateAptxHd(p_codec_info);
+ a2dp_aptx_hd_encoder_cb.feeding_params.bits_per_sample =
+ A2DP_VendorGetTrackBitsPerSampleAptxHd(p_codec_info);
+ a2dp_aptx_hd_encoder_cb.feeding_params.channel_count =
+ A2DP_VendorGetTrackChannelCountAptxHd(p_codec_info);
+}
+
+void a2dp_vendor_aptx_hd_encoder_cleanup(void) {
+ osi_free(a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state);
+ memset(&a2dp_aptx_hd_encoder_cb, 0, sizeof(a2dp_aptx_hd_encoder_cb));
+}
+
+void a2dp_vendor_aptx_hd_feeding_init(
+ const tA2DP_FEEDING_PARAMS* p_feeding_params) {
+ LOG_DEBUG(LOG_TAG,
+ "%s: PCM feeding: sample_rate:%d bits_per_sample:%d"
+ "channel_count:%d",
+ __func__, p_feeding_params->sample_rate,
+ p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+
+ // Check the PCM feeding sample_rate
+ switch (p_feeding_params->sample_rate) {
+ case 44100:
+ case 48000:
+ break;
+ default:
+ LOG_WARN(LOG_TAG, "%s: feeding PCM sample_rate %u is not supported",
+ __func__, p_feeding_params->sample_rate);
+ return;
+ }
+
+ // Check the bits per sample
+ switch (p_feeding_params->bits_per_sample) {
+ case 24:
+ break;
+ default:
+ LOG_WARN(LOG_TAG, "%s: feeding PCM bits_per_sample %u is not supported",
+ __func__, p_feeding_params->bits_per_sample);
+ return;
+ }
+
+ // Check the number of channels
+ switch (p_feeding_params->channel_count) {
+ case 1: // Mono
+ case 2: // Stereo
+ break;
+ default:
+ LOG_WARN(LOG_TAG, "%s: feeding PCM channel_count %u is not supported",
+ __func__, p_feeding_params->channel_count);
+ return;
+ }
+
+ bool reconfig_needed = false;
+ if (a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate !=
+ p_feeding_params->sample_rate) {
+ LOG_DEBUG(LOG_TAG,
+ "%s: codec reconfiguration: feeding PCM sample_rate "
+ "from %u to %u",
+ __func__, a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate,
+ p_feeding_params->sample_rate);
+ reconfig_needed = true;
+ a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate =
+ p_feeding_params->sample_rate;
+ }
+ if (a2dp_aptx_hd_encoder_cb.feeding_params.bits_per_sample !=
+ p_feeding_params->bits_per_sample) {
+ LOG_DEBUG(LOG_TAG,
+ "%s: aptX-HD reconfiguration needed: "
+ "feeding PCM bits_per_sample from %u to %u",
+ __func__, a2dp_aptx_hd_encoder_cb.feeding_params.bits_per_sample,
+ p_feeding_params->bits_per_sample);
+ a2dp_aptx_hd_encoder_cb.feeding_params.bits_per_sample =
+ p_feeding_params->bits_per_sample;
+ reconfig_needed = true;
+ }
+ if (a2dp_aptx_hd_encoder_cb.feeding_params.channel_count !=
+ p_feeding_params->channel_count) {
+ LOG_DEBUG(LOG_TAG,
+ "%s: aptX-HD reconfiguration needed: "
+ "feeding PCM channel_count from %u to %u",
+ __func__, a2dp_aptx_hd_encoder_cb.feeding_params.channel_count,
+ p_feeding_params->channel_count);
+ a2dp_aptx_hd_encoder_cb.feeding_params.channel_count =
+ p_feeding_params->channel_count;
+ reconfig_needed = true;
+ }
+
+ if (reconfig_needed) {
+ 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);
+ aptx_hd_init_framing_params(&a2dp_aptx_hd_encoder_cb.framing_params);
+ } else {
+ LOG_DEBUG(LOG_TAG, "%s: no aptX-HD reconfiguration needed", __func__);
+ }
+}
+
+//
+// Initialize the framing parameters, and set those that don't change
+// while streaming (e.g., 'sleep_time_ns').
+//
+static void aptx_hd_init_framing_params(
+ tAPTX_HD_FRAMING_PARAMS* framing_params) {
+ framing_params->sleep_time_ns = 0;
+ framing_params->pcm_reads = 0;
+ framing_params->pcm_bytes_per_read = 0;
+ framing_params->aptx_hd_bytes = 0;
+ framing_params->frame_size_counter = 0;
+
+ framing_params->sleep_time_ns = 9000000;
+
+ LOG_DEBUG(LOG_TAG, "%s: sleep_time_ns = %" PRIu64, __func__,
+ framing_params->sleep_time_ns);
+}
+
+//
+// Set frame size and transmission interval needed to stream the required
+// sample rate using 2-DH5 packets for aptX and 2-DH3 packets for aptX-LL.
+// With SCMS-T enabled we need to reserve room for extra headers added later.
+// Packets are always sent at equals time intervals but to achieve the
+// required sample rate, the frame size needs to change on occasion.
+//
+// Also need to specify how many of the required PCM samples are read at a
+// time:
+// aptx_bytes = pcm_reads * pcm_bytes_per_read / 4
+// and
+// number of aptX samples produced = pcm_bytes_per_read / 16
+//
+static void aptx_hd_update_framing_params(
+ tAPTX_HD_FRAMING_PARAMS* framing_params) {
+ if (a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate == 48000) {
+ framing_params->aptx_hd_bytes = 648;
+ framing_params->pcm_bytes_per_read = 24;
+ framing_params->pcm_reads = 108;
+ } else {
+ // Assume the sample rate is 44100
+
+ //
+ // Total of 80 iterations:
+ // - Iteration 80: packet size 648, with 108 reads of 24 PCM bytes
+ // - Iterations 20, 40, 60: packet size 612, with 102 reads of 24 PCM bytes
+ // - All other iterations: packet size 594, with 99 reads of 24 PCM bytes
+ //
+ if (framing_params->frame_size_counter + 1 == 80) {
+ framing_params->aptx_hd_bytes = 648;
+ framing_params->pcm_bytes_per_read = 24;
+ framing_params->pcm_reads = 108;
+ } else if (((framing_params->frame_size_counter + 1) % 20) == 0) {
+ framing_params->aptx_hd_bytes = 612;
+ framing_params->pcm_bytes_per_read = 24;
+ framing_params->pcm_reads = 102;
+ } else {
+ framing_params->aptx_hd_bytes = 594;
+ framing_params->pcm_bytes_per_read = 24;
+ framing_params->pcm_reads = 99;
+ }
+ framing_params->frame_size_counter++;
+ if (framing_params->frame_size_counter == 80)
+ framing_params->frame_size_counter = 0;
+ }
+
+ LOG_DEBUG(LOG_TAG,
+ "%s: sleep_time_ns = %" PRIu64
+ " aptx_hd_bytes = %u "
+ "pcm_bytes_per_read = %u pcm_reads = %u frame_size_counter = %u",
+ __func__, framing_params->sleep_time_ns,
+ framing_params->aptx_hd_bytes, framing_params->pcm_bytes_per_read,
+ framing_params->pcm_reads, framing_params->frame_size_counter);
+}
+
+void a2dp_vendor_aptx_hd_feeding_reset(void) {
+ aptx_hd_init_framing_params(&a2dp_aptx_hd_encoder_cb.framing_params);
+}
+
+void a2dp_vendor_aptx_hd_feeding_flush(void) {
+ aptx_hd_init_framing_params(&a2dp_aptx_hd_encoder_cb.framing_params);
+}
+
+period_ms_t a2dp_vendor_aptx_hd_get_encoder_interval_ms(void) {
+ return a2dp_aptx_hd_encoder_cb.framing_params.sleep_time_ns / (1000 * 1000);
+}
+
+void a2dp_vendor_aptx_hd_send_frames(uint64_t timestamp_us) {
+ tAPTX_HD_FRAMING_PARAMS* framing_params =
+ &a2dp_aptx_hd_encoder_cb.framing_params;
+
+ // Prepare the packet to send
+ BT_HDR* p_buf = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
+ p_buf->offset = A2DP_APTX_HD_OFFSET;
+ p_buf->len = 0;
+ p_buf->layer_specific = 0;
+
+ uint8_t* encoded_ptr = (uint8_t*)(p_buf + 1);
+ encoded_ptr += p_buf->offset;
+
+ aptx_hd_update_framing_params(framing_params);
+
+ //
+ // Read the PCM data and encode it
+ //
+ LOG_DEBUG(LOG_TAG, "%s: %u PCM reads of size %u", __func__,
+ framing_params->pcm_reads, framing_params->pcm_bytes_per_read);
+ size_t encoded_ptr_index = 0;
+ size_t pcm_bytes_encoded = 0;
+ a2dp_aptx_hd_encoder_cb.stats.media_read_total_expected_packets++;
+ a2dp_aptx_hd_encoder_cb.stats.media_read_total_expected_reads_count +=
+ framing_params->pcm_reads;
+ a2dp_aptx_hd_encoder_cb.stats.media_read_total_expected_read_bytes +=
+ framing_params->pcm_reads * framing_params->pcm_bytes_per_read;
+ for (size_t reads = 0; reads < framing_params->pcm_reads; reads++) {
+ uint32_t
+ read_buffer32[A2DP_APTX_HD_MAX_PCM_BYTES_PER_READ / sizeof(uint32_t)];
+ size_t pcm_bytes_read = a2dp_aptx_hd_encoder_cb.read_callback(
+ (uint8_t*)read_buffer32, framing_params->pcm_bytes_per_read);
+ a2dp_aptx_hd_encoder_cb.stats.media_read_total_actual_read_bytes +=
+ pcm_bytes_read;
+ if (pcm_bytes_read < framing_params->pcm_bytes_per_read) {
+ LOG_WARN(LOG_TAG,
+ "%s: underflow at PCM reading iteration %zu: read %zu "
+ "instead of %d",
+ __func__, reads, pcm_bytes_read,
+ framing_params->pcm_bytes_per_read);
+ break;
+ }
+ a2dp_aptx_hd_encoder_cb.stats.media_read_total_actual_reads_count++;
+ pcm_bytes_encoded += aptx_hd_encode_24bit(
+ framing_params, &encoded_ptr_index, read_buffer32, encoded_ptr);
+ }
+
+ // Compute the number of encoded bytes
+ const int COMPRESSION_RATIO = 4;
+ size_t encoded_bytes = pcm_bytes_encoded / COMPRESSION_RATIO;
+ p_buf->len += encoded_bytes;
+ LOG_DEBUG(LOG_TAG, "%s: encoded %zu PCM bytes to %zu", __func__,
+ pcm_bytes_encoded, encoded_bytes);
+
+ // Update the RTP timestamp
+ *((uint32_t*)(p_buf + 1)) = a2dp_aptx_hd_encoder_cb.timestamp;
+ const uint8_t BYTES_PER_FRAME = 3;
+ uint32_t rtp_timestamp =
+ (pcm_bytes_encoded /
+ a2dp_aptx_hd_encoder_cb.feeding_params.channel_count) /
+ BYTES_PER_FRAME;
+ a2dp_aptx_hd_encoder_cb.timestamp += rtp_timestamp;
+
+ if (p_buf->len > 0) {
+ a2dp_aptx_hd_encoder_cb.enqueue_callback(p_buf, 1);
+ } else {
+ a2dp_aptx_hd_encoder_cb.stats.media_read_total_dropped_packets++;
+ osi_free(p_buf);
+ }
+}
+
+static size_t aptx_hd_encode_24bit(tAPTX_HD_FRAMING_PARAMS* framing_params,
+ size_t* data_out_index, uint32_t* data32_in,
+ uint8_t* data_out) {
+ size_t pcm_bytes_encoded = 0;
+ const uint8_t* p = (const uint8_t*)(data32_in);
+
+ for (size_t aptx_hd_samples = 0;
+ aptx_hd_samples < framing_params->pcm_bytes_per_read / 24;
+ aptx_hd_samples++) {
+ uint32_t pcmL[4];
+ uint32_t pcmR[4];
+ uint32_t encoded_sample[2];
+
+ // Expand from AUDIO_FORMAT_PCM_24_BIT_PACKED data (3 bytes per sample)
+ // into AUDIO_FORMAT_PCM_8_24_BIT (4 bytes per sample).
+ for (size_t i = 0; i < 4; i++) {
+ pcmL[i] = ((p[0] << 0) | (p[1] << 8) | (((int8_t)p[2]) << 16));
+ p += 3;
+ pcmR[i] = ((p[0] << 0) | (p[1] << 8) | (((int8_t)p[2]) << 16));
+ p += 3;
+ }
+
+ aptx_hd_encoder_encode_stereo_func(
+ a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state, &pcmL, &pcmR,
+ &encoded_sample);
+
+ uint8_t* encoded_ptr = (uint8_t*)&encoded_sample[0];
+ data_out[*data_out_index + 0] = *(encoded_ptr + 2);
+ data_out[*data_out_index + 1] = *(encoded_ptr + 1);
+ data_out[*data_out_index + 2] = *(encoded_ptr + 0);
+ data_out[*data_out_index + 3] = *(encoded_ptr + 6);
+ data_out[*data_out_index + 4] = *(encoded_ptr + 5);
+ data_out[*data_out_index + 5] = *(encoded_ptr + 4);
+
+ pcm_bytes_encoded += 24;
+ *data_out_index += 6;
+ }
+
+ return pcm_bytes_encoded;
+}
+
+void a2dp_vendor_aptx_hd_debug_codec_dump(int fd) {
+ a2dp_aptx_hd_encoder_stats_t* stats = &a2dp_aptx_hd_encoder_cb.stats;
+
+ dprintf(fd, "\nA2DP aptX-HD 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/include/a2dp_vendor_aptx.h b/stack/include/a2dp_vendor_aptx.h
new file mode 100644
index 0000000..ce9ab9c
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx.h
@@ -0,0 +1,148 @@
+/*
+ * 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 aptX
+//
+
+#ifndef A2DP_VENDOR_APTX_H
+#define A2DP_VENDOR_APTX_H
+
+#include "a2dp_codec_api.h"
+#include "a2dp_vendor_aptx_constants.h"
+#include "avdt_api.h"
+
+class A2dpCodecConfigAptx : public A2dpCodecConfig {
+ public:
+ A2dpCodecConfigAptx();
+ virtual ~A2dpCodecConfigAptx();
+
+ 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 aptX Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid aptX
+// codec, otherwise false.
+bool A2DP_IsVendorSourceCodecValidAptx(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP aptX Sink
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid aptX
+// codec, otherwise false.
+bool A2DP_IsVendorPeerSinkCodecValidAptx(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_VendorUsesRtpHeaderAptx(bool content_protection_enabled,
+ const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX codec name for a given |p_codec_info|.
+const char* A2DP_VendorCodecNameAptx(const uint8_t* p_codec_info);
+
+// Checks whether two A2DP aptX 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_VendorCodecTypeEqualsAptx(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b);
+
+// Checks whether two A2DP aptX 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 aptX, the return value is false.
+bool A2DP_VendorCodecEqualsAptx(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b);
+
+// Gets the track sample rate value for the A2DP aptX codec.
+// |p_codec_info| is a pointer to the aptX codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackSampleRateAptx(const uint8_t* p_codec_info);
+
+// Gets the bits per audio sample for the A2DP aptX codec.
+// |p_codec_info| is a pointer to the aptX codec_info to decode.
+// Returns the bits per audio sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSampleAptx(const uint8_t* p_codec_info);
+
+// Gets the channel count for the A2DP aptX codec.
+// |p_codec_info| is a pointer to the aptX codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackChannelCountAptx(const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX 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_VendorGetPacketTimestampAptx(const uint8_t* p_codec_info,
+ const uint8_t* p_data,
+ uint32_t* p_timestamp);
+
+// Builds A2DP aptX 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_VendorBuildCodecHeaderAptx(const uint8_t* p_codec_info, BT_HDR* p_buf,
+ uint16_t frames_per_packet);
+
+// Decodes and displays aptX codec info (for debugging).
+// |p_codec_info| is a pointer to the aptX codec_info to decode and display.
+void A2DP_VendorDumpCodecInfoAptx(const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX 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 aptX encoder interface if the |p_codec_info| is valid and
+// supported, otherwise NULL.
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceAptx(
+ const uint8_t* p_codec_info);
+
+// Adjusts the A2DP aptX 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_VendorAdjustCodecAptx(uint8_t* p_codec_info);
+
+// Gets the A2DP aptX 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_VendorSourceCodecIndexAptx(
+ const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX Source codec name.
+const char* A2DP_VendorCodecIndexStrAptx(void);
+
+// Initializes A2DP aptX Source codec information into |tAVDT_CFG|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_VendorInitCodecConfigAptx(tAVDT_CFG* p_cfg);
+
+#endif // A2DP_VENDOR_APTX_H
diff --git a/stack/include/a2dp_vendor_aptx_constants.h b/stack/include/a2dp_vendor_aptx_constants.h
new file mode 100644
index 0000000..0f2c7c6
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_constants.h
@@ -0,0 +1,36 @@
+/*
+ * 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 aptX codec
+//
+
+#ifndef A2DP_VENDOR_APTX_CONSTANTS_H
+#define A2DP_VENDOR_APTX_CONSTANTS_H
+
+/* aptX codec specific settings */
+#define A2DP_APTX_CODEC_LEN 9
+
+#define A2DP_APTX_VENDOR_ID 0x0000004F
+#define A2DP_APTX_CODEC_ID_BLUETOOTH 0x0001
+#define A2DP_APTX_SAMPLERATE_44100 0x20
+#define A2DP_APTX_SAMPLERATE_48000 0x10
+#define A2DP_APTX_CHANNELS_STEREO 0x02
+#define A2DP_APTX_CHANNELS_MONO 0x01
+#define A2DP_APTX_FUTURE_1 0x00
+#define A2DP_APTX_FUTURE_2 0x00
+
+#endif // A2DP_VENDOR_APTX_CONSTANTS_H
diff --git a/stack/include/a2dp_vendor_aptx_encoder.h b/stack/include/a2dp_vendor_aptx_encoder.h
new file mode 100644
index 0000000..d444b63
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_encoder.h
@@ -0,0 +1,71 @@
+/*
+ * 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 aptX Encoder
+//
+
+#ifndef A2DP_VENDOR_APTX_ENCODER_H
+#define A2DP_VENDOR_APTX_ENCODER_H
+
+#include "a2dp_codec_api.h"
+#include "osi/include/time.h"
+
+// Loads the A2DP aptX encoder.
+// Return true on success, otherwise false.
+bool A2DP_VendorLoadEncoderAptx(void);
+
+// Unloads the A2DP aptX encoder.
+void A2DP_VendorUnloadEncoderAptx(void);
+
+// Initialize the A2DP aptX 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_vendor_aptx_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 aptX encoder.
+void a2dp_vendor_aptx_encoder_cleanup(void);
+
+// Initialize the feeding for the A2DP aptX encoder.
+// The feeding initialization parameters are in |p_feeding_params|.
+void a2dp_vendor_aptx_feeding_init(
+ const tA2DP_FEEDING_PARAMS* p_feeding_params);
+
+// Reset the feeding for the A2DP aptX encoder.
+void a2dp_vendor_aptx_feeding_reset(void);
+
+// Flush the feeding for the A2DP aptX encoder.
+void a2dp_vendor_aptx_feeding_flush(void);
+
+// Get the A2DP aptX encoder interval (in milliseconds).
+period_ms_t a2dp_vendor_aptx_get_encoder_interval_ms(void);
+
+// Prepare and send A2DP aptX encoded frames.
+// |timestamp_us| is the current timestamp (in microseconds).
+void a2dp_vendor_aptx_send_frames(uint64_t timestamp_us);
+
+// Dump aptX codec-related statistics.
+// |fd| is the file descriptor to use to dump the statistics information
+// in user-friendly test format.
+void a2dp_vendor_aptx_debug_codec_dump(int fd);
+
+#endif // A2DP_VENDOR_APTX_ENCODER_H
diff --git a/stack/include/a2dp_vendor_aptx_hd.h b/stack/include/a2dp_vendor_aptx_hd.h
new file mode 100644
index 0000000..443ea90
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_hd.h
@@ -0,0 +1,149 @@
+/*
+ * 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 aptX-HD
+//
+
+#ifndef A2DP_VENDOR_APTX_HD_H
+#define A2DP_VENDOR_APTX_HD_H
+
+#include "a2dp_codec_api.h"
+#include "a2dp_vendor_aptx_hd_constants.h"
+#include "avdt_api.h"
+
+class A2dpCodecConfigAptxHd : public A2dpCodecConfig {
+ public:
+ A2dpCodecConfigAptxHd();
+ virtual ~A2dpCodecConfigAptxHd();
+
+ 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 aptX-HD Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid aptX-HD
+// codec, otherwise false.
+bool A2DP_IsVendorSourceCodecValidAptxHd(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP aptX-HD Sink
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid aptX-HD
+// codec, otherwise false.
+bool A2DP_IsVendorPeerSinkCodecValidAptxHd(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_VendorUsesRtpHeaderAptxHd(bool content_protection_enabled,
+ const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD codec name for a given |p_codec_info|.
+const char* A2DP_VendorCodecNameAptxHd(const uint8_t* p_codec_info);
+
+// Checks whether two A2DP aptX-HD 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_VendorCodecTypeEqualsAptxHd(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b);
+
+// Checks whether two A2DP aptX-HD 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 aptX-HD, the return value is false.
+bool A2DP_VendorCodecEqualsAptxHd(const uint8_t* p_codec_info_a,
+ const uint8_t* p_codec_info_b);
+
+// Gets the track sample rate value for the A2DP aptX-HD codec.
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackSampleRateAptxHd(const uint8_t* p_codec_info);
+
+// Gets the bits per audio sample for the A2DP aptX-HD codec.
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode.
+// Returns the bits per audio sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSampleAptxHd(const uint8_t* p_codec_info);
+
+// Gets the channel count for the A2DP aptX-HD codec.
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackChannelCountAptxHd(const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD 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_VendorGetPacketTimestampAptxHd(const uint8_t* p_codec_info,
+ const uint8_t* p_data,
+ uint32_t* p_timestamp);
+
+// Builds A2DP aptX-HD 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_VendorBuildCodecHeaderAptxHd(const uint8_t* p_codec_info,
+ BT_HDR* p_buf,
+ uint16_t frames_per_packet);
+
+// Decodes and displays aptX-HD codec info (for debugging).
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode and display.
+void A2DP_VendorDumpCodecInfoAptxHd(const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD 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 aptX-HD encoder interface if the |p_codec_info| is valid
+// and supported, otherwise NULL.
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceAptxHd(
+ const uint8_t* p_codec_info);
+
+// Adjusts the A2DP aptX-HD 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_VendorAdjustCodecAptxHd(uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD 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_VendorSourceCodecIndexAptxHd(
+ const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD Source codec name.
+const char* A2DP_VendorCodecIndexStrAptxHd(void);
+
+// Initializes A2DP aptX-HD Source codec information into |tAVDT_CFG|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_VendorInitCodecConfigAptxHd(tAVDT_CFG* p_cfg);
+
+#endif // A2DP_VENDOR_APTX_HD_H
diff --git a/stack/include/a2dp_vendor_aptx_hd_constants.h b/stack/include/a2dp_vendor_aptx_hd_constants.h
new file mode 100644
index 0000000..40ac8b5
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_hd_constants.h
@@ -0,0 +1,38 @@
+/*
+ * 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 aptX-HD codec
+//
+
+#ifndef A2DP_VENDOR_APTX_HD_CONSTANTS_H
+#define A2DP_VENDOR_APTX_HD_CONSTANTS_H
+
+/* aptX-HD codec specific settings */
+#define A2DP_APTX_HD_CODEC_LEN 13
+
+#define A2DP_APTX_HD_VENDOR_ID 0x000000D7
+#define A2DP_APTX_HD_CODEC_ID_BLUETOOTH 0x0024
+#define A2DP_APTX_HD_SAMPLERATE_44100 0x20
+#define A2DP_APTX_HD_SAMPLERATE_48000 0x10
+#define A2DP_APTX_HD_CHANNELS_STEREO 0x02
+#define A2DP_APTX_HD_CHANNELS_MONO 0x01
+#define A2DP_APTX_HD_ACL_SPRINT_RESERVED0 0x00
+#define A2DP_APTX_HD_ACL_SPRINT_RESERVED1 0x00
+#define A2DP_APTX_HD_ACL_SPRINT_RESERVED2 0x00
+#define A2DP_APTX_HD_ACL_SPRINT_RESERVED3 0x00
+
+#endif // A2DP_VENDOR_APTX_HD_CONSTANTS_H
diff --git a/stack/include/a2dp_vendor_aptx_hd_encoder.h b/stack/include/a2dp_vendor_aptx_hd_encoder.h
new file mode 100644
index 0000000..032d933
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_hd_encoder.h
@@ -0,0 +1,71 @@
+/*
+ * 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 aptX-HD Encoder
+//
+
+#ifndef A2DP_VENDOR_APTX_HD_ENCODER_H
+#define A2DP_VENDOR_APTX_HD_ENCODER_H
+
+#include "a2dp_codec_api.h"
+#include "osi/include/time.h"
+
+// Loads the A2DP aptX-HD encoder.
+// Return true on success, otherwise false.
+bool A2DP_VendorLoadEncoderAptxHd(void);
+
+// Unloads the A2DP aptX-HD encoder.
+void A2DP_VendorUnloadEncoderAptxHd(void);
+
+// Initialize the A2DP aptX-HD 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_vendor_aptx_hd_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 aptX-HD encoder.
+void a2dp_vendor_aptx_hd_encoder_cleanup(void);
+
+// Initialize the feeding for the A2DP aptX-HD encoder.
+// The feeding initialization parameters are in |p_feeding_params|.
+void a2dp_vendor_aptx_hd_feeding_init(
+ const tA2DP_FEEDING_PARAMS* p_feeding_params);
+
+// Reset the feeding for the A2DP aptX-HD encoder.
+void a2dp_vendor_aptx_hd_feeding_reset(void);
+
+// Flush the feeding for the A2DP aptX-HD encoder.
+void a2dp_vendor_aptx_hd_feeding_flush(void);
+
+// Get the A2DP aptX-HD encoder interval (in milliseconds).
+period_ms_t a2dp_vendor_aptx_hd_get_encoder_interval_ms(void);
+
+// Prepare and send A2DP aptX-HD encoded frames.
+// |timestamp_us| is the current timestamp (in microseconds).
+void a2dp_vendor_aptx_hd_send_frames(uint64_t timestamp_us);
+
+// Dump aptX-HD codec-related statistics.
+// |fd| is the file descriptor to use to dump the statistics information
+// in user-friendly test format.
+void a2dp_vendor_aptx_hd_debug_codec_dump(int fd);
+
+#endif // A2DP_VENDOR_APTX_HD_ENCODER_H