diff --git a/system/include/bt_target.h b/system/include/bt_target.h index fa2d6aa5a83dc8c66274ba0b90ea290623f87ec1..eef12f67314500015c621cc114d33431cd97c694 100644 --- a/system/include/bt_target.h +++ b/system/include/bt_target.h @@ -1100,7 +1100,7 @@ /* Number of simultaneous stream endpoints. */ #ifndef AVDT_NUM_SEPS -#define AVDT_NUM_SEPS 3 +#define AVDT_NUM_SEPS 6 #endif /* Number of transport channels setup by AVDT for all media streams */ diff --git a/system/stack/Android.mk b/system/stack/Android.mk index 0b1615f5fd9880cb6dfaa121b52243fdcdbad07c..146c4b460ff4c89c84b6f430aee6088d7bec2b71 100644 --- a/system/stack/Android.mk +++ b/system/stack/Android.mk @@ -43,6 +43,8 @@ LOCAL_SRC_FILES := \ ./a2dp/a2dp_vendor_aptx_hd.cc \ ./a2dp/a2dp_vendor_aptx_encoder.cc \ ./a2dp/a2dp_vendor_aptx_hd_encoder.cc \ + ./a2dp/a2dp_vendor_ldac.cc \ + ./a2dp/a2dp_vendor_ldac_encoder.cc \ ./avct/avct_api.cc \ ./avct/avct_bcb_act.cc \ ./avct/avct_ccb.cc \ diff --git a/system/stack/a2dp/a2dp_codec_config.cc b/system/stack/a2dp/a2dp_codec_config.cc index a0182c37929accdf2af249d90edb4d490463b831..69659ced7cfc8d161b6cb4081ec91bfe8dfc8cd3 100644 --- a/system/stack/a2dp/a2dp_codec_config.cc +++ b/system/stack/a2dp/a2dp_codec_config.cc @@ -28,6 +28,7 @@ #include "a2dp_vendor.h" #include "a2dp_vendor_aptx.h" #include "a2dp_vendor_aptx_hd.h" +#include "a2dp_vendor_ldac.h" #include "osi/include/log.h" /* The Media Type offset within the codec info byte array */ @@ -90,6 +91,10 @@ A2dpCodecConfig* A2dpCodecConfig::createCodec( case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: codec_config = new A2dpCodecConfigAptxHd(); break; + case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: + codec_config = new A2dpCodecConfigLdac(); + break; + // Add a switch statement for each vendor-specific codec case BTAV_A2DP_CODEC_INDEX_MAX: break; } diff --git a/system/stack/a2dp/a2dp_vendor.cc b/system/stack/a2dp/a2dp_vendor.cc index 722414001718a6ed7dc79e3af31a2a62c748489b..a49d76f30f8b79468c696c96b84203540f0dd9d9 100644 --- a/system/stack/a2dp/a2dp_vendor.cc +++ b/system/stack/a2dp/a2dp_vendor.cc @@ -23,6 +23,7 @@ #include "a2dp_vendor.h" #include "a2dp_vendor_aptx.h" #include "a2dp_vendor_aptx_hd.h" +#include "a2dp_vendor_ldac.h" #include "bt_target.h" #include "osi/include/log.h" #include "osi/include/osi.h" @@ -43,6 +44,11 @@ bool A2DP_IsVendorSourceCodecValid(const uint8_t* p_codec_info) { return A2DP_IsVendorSourceCodecValidAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_IsVendorSourceCodecValidLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -85,6 +91,11 @@ bool A2DP_IsVendorPeerSinkCodecValid(const uint8_t* p_codec_info) { return A2DP_IsVendorPeerSinkCodecValidAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_IsVendorPeerSinkCodecValidLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -159,6 +170,12 @@ bool A2DP_VendorUsesRtpHeader(bool content_protection_enabled, p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorUsesRtpHeaderLdac(content_protection_enabled, + p_codec_info); + } + // Add checks based on <content_protection_enabled, vendor_id, codec_id> return true; @@ -180,6 +197,11 @@ const char* A2DP_VendorCodecName(UNUSED_ATTR const uint8_t* p_codec_info) { return A2DP_VendorCodecNameAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorCodecNameLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return "UNKNOWN VENDOR CODEC"; @@ -214,6 +236,11 @@ bool A2DP_VendorCodecTypeEquals(const uint8_t* p_codec_info_a, return A2DP_VendorCodecTypeEqualsAptxHd(p_codec_info_a, p_codec_info_b); } + // Check for LDAC + if (vendor_id_a == A2DP_LDAC_VENDOR_ID && codec_id_a == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorCodecTypeEqualsLdac(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". @@ -249,6 +276,11 @@ bool A2DP_VendorCodecEquals(const uint8_t* p_codec_info_a, return A2DP_VendorCodecEqualsAptxHd(p_codec_info_a, p_codec_info_b); } + // Check for LDAC + if (vendor_id_a == A2DP_LDAC_VENDOR_ID && codec_id_a == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorCodecEqualsLdac(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". @@ -271,6 +303,11 @@ int A2DP_VendorGetTrackSampleRate(const uint8_t* p_codec_info) { return A2DP_VendorGetTrackSampleRateAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorGetTrackSampleRateLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return -1; @@ -292,6 +329,11 @@ int A2DP_VendorGetTrackBitsPerSample(const uint8_t* p_codec_info) { return A2DP_VendorGetTrackBitsPerSampleAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorGetTrackBitsPerSampleLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return -1; @@ -313,6 +355,11 @@ int A2DP_VendorGetTrackChannelCount(const uint8_t* p_codec_info) { return A2DP_VendorGetTrackChannelCountAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorGetTrackChannelCountLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return -1; @@ -360,6 +407,11 @@ bool A2DP_VendorGetPacketTimestamp(const uint8_t* p_codec_info, p_timestamp); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorGetPacketTimestampLdac(p_codec_info, p_data, p_timestamp); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -384,6 +436,12 @@ bool A2DP_VendorBuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf, frames_per_packet); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorBuildCodecHeaderLdac(p_codec_info, p_buf, + frames_per_packet); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -406,6 +464,11 @@ const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterface( return A2DP_VendorGetEncoderInterfaceAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorGetEncoderInterfaceLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return NULL; @@ -427,6 +490,11 @@ bool A2DP_VendorAdjustCodec(uint8_t* p_codec_info) { return A2DP_VendorAdjustCodecAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorAdjustCodecLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -449,6 +517,11 @@ btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndex( return A2DP_VendorSourceCodecIndexAptxHd(p_codec_info); } + // Check for LDAC + if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { + return A2DP_VendorSourceCodecIndexLdac(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return BTAV_A2DP_CODEC_INDEX_MAX; @@ -464,6 +537,8 @@ const char* A2DP_VendorCodecIndexStr(btav_a2dp_codec_index_t codec_index) { return A2DP_VendorCodecIndexStrAptx(); case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: return A2DP_VendorCodecIndexStrAptxHd(); + case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: + return A2DP_VendorCodecIndexStrLdac(); // Add a switch statement for each vendor-specific codec case BTAV_A2DP_CODEC_INDEX_MAX: break; @@ -483,6 +558,8 @@ bool A2DP_VendorInitCodecConfig(btav_a2dp_codec_index_t codec_index, return A2DP_VendorInitCodecConfigAptx(p_cfg); case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: return A2DP_VendorInitCodecConfigAptxHd(p_cfg); + case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: + return A2DP_VendorInitCodecConfigLdac(p_cfg); // Add a switch statement for each vendor-specific codec case BTAV_A2DP_CODEC_INDEX_MAX: break; diff --git a/system/stack/a2dp/a2dp_vendor_ldac.cc b/system/stack/a2dp/a2dp_vendor_ldac.cc new file mode 100644 index 0000000000000000000000000000000000000000..2f496ba3564899501059cd6a72f12be97e4e8649 --- /dev/null +++ b/system/stack/a2dp/a2dp_vendor_ldac.cc @@ -0,0 +1,1102 @@ +/* + * 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 LDAC Codec Information + * Element and Media Payload. + * + ******************************************************************************/ + +#define LOG_TAG "a2dp_vendor_ldac" + +#include "bt_target.h" + +#include "a2dp_vendor_ldac.h" + +#include <string.h> + +#include <base/logging.h> +#include "a2dp_vendor.h" +#include "a2dp_vendor_ldac_encoder.h" +#include "bt_utils.h" +#include "osi/include/log.h" +#include "osi/include/osi.h" + +// data type for the LDAC Codec Information Element */ +// NOTE: bits_per_sample is only needs for LDAC encoder initialization. +typedef struct { + uint32_t vendorId; + uint16_t codecId; /* Codec ID for LDAC */ + uint8_t sampleRate; /* Sampling Frequency */ + uint8_t channelMode; /* STEREO/DUAL/MONO */ + btav_a2dp_codec_bits_per_sample_t bits_per_sample; +} tA2DP_LDAC_CIE; + +/* LDAC Source codec capabilities */ +static const tA2DP_LDAC_CIE a2dp_ldac_caps = { + A2DP_LDAC_VENDOR_ID, // vendorId + A2DP_LDAC_CODEC_ID, // codecId + // sampleRate + (A2DP_LDAC_SAMPLING_FREQ_44100 | A2DP_LDAC_SAMPLING_FREQ_48000 | + A2DP_LDAC_SAMPLING_FREQ_88200 | A2DP_LDAC_SAMPLING_FREQ_96000), + // channelMode + (A2DP_LDAC_CHANNEL_MODE_MONO | A2DP_LDAC_CHANNEL_MODE_DUAL | + A2DP_LDAC_CHANNEL_MODE_STEREO), + // bits_per_sample + (BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 | BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24 | + BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32)}; + +/* Default LDAC codec configuration */ +static const tA2DP_LDAC_CIE a2dp_ldac_default_config = { + A2DP_LDAC_VENDOR_ID, // vendorId + A2DP_LDAC_CODEC_ID, // codecId + A2DP_LDAC_SAMPLING_FREQ_96000, // sampleRate + A2DP_LDAC_CHANNEL_MODE_STEREO, // channelMode + BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32 // bits_per_sample +}; + +static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_ldac = { + a2dp_vendor_ldac_encoder_init, a2dp_vendor_ldac_encoder_cleanup, + a2dp_vendor_ldac_feeding_init, a2dp_vendor_ldac_feeding_reset, + a2dp_vendor_ldac_feeding_flush, a2dp_vendor_ldac_get_encoder_interval_ms, + a2dp_vendor_ldac_send_frames, a2dp_vendor_ldac_debug_codec_dump}; + +UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityLdac( + const tA2DP_LDAC_CIE* p_cap, const uint8_t* p_codec_info, + bool is_peer_codec_info); + +// Builds the LDAC 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 LDAC 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_BuildInfoLdac(uint8_t media_type, + const tA2DP_LDAC_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_LDAC_CODEC_LEN; + *p_result++ = media_type; + *p_result++ = A2DP_MEDIA_CT_NON_A2DP; + + // Vendor ID and Codec ID + *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); + + // Sampling Frequency + *p_result = (uint8_t)(p_ie->sampleRate & A2DP_LDAC_SAMPLING_FREQ_MASK); + if (*p_result == 0) return A2DP_INVALID_PARAMS; + p_result++; + + // Channel Mode + *p_result = (uint8_t)(p_ie->channelMode & A2DP_LDAC_CHANNEL_MODE_MASK); + if (*p_result == 0) return A2DP_INVALID_PARAMS; + } + return status; +} + +// Parses the LDAC 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_ParseInfoLdac(tA2DP_LDAC_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_LDAC_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_LDAC_VENDOR_ID || + p_ie->codecId != A2DP_LDAC_CODEC_ID) { + return A2DP_WRONG_CODEC; + } + + p_ie->sampleRate = *p_codec_info++ & A2DP_LDAC_SAMPLING_FREQ_MASK; + p_ie->channelMode = *p_codec_info++ & A2DP_LDAC_CHANNEL_MODE_MASK; + + 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; +} + +// Build the LDAC Media Payload Header. +// |p_dst| points to the location where the header should be written to. +// If |frag| is true, the media payload frame is fragmented. +// |start| is true for the first packet of a fragmented frame. +// |last| is true for the last packet of a fragmented frame. +// If |frag| is false, |num| is the number of number of frames in the packet, +// otherwise is the number of remaining fragments (including this one). +static void A2DP_BuildMediaPayloadHeaderLdac(uint8_t* p_dst, bool frag, + bool start, bool last, + uint8_t num) { + if (p_dst == NULL) return; + + *p_dst = 0; + if (frag) *p_dst |= A2DP_LDAC_HDR_F_MSK; + if (start) *p_dst |= A2DP_LDAC_HDR_S_MSK; + if (last) *p_dst |= A2DP_LDAC_HDR_L_MSK; + *p_dst |= (A2DP_LDAC_HDR_NUM_MSK & num); +} + +bool A2DP_IsVendorSourceCodecValidLdac(const uint8_t* p_codec_info) { + tA2DP_LDAC_CIE cfg_cie; + + /* Use a liberal check when parsing the codec info */ + return (A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) || + (A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS); +} + +bool A2DP_IsVendorPeerSinkCodecValidLdac(const uint8_t* p_codec_info) { + tA2DP_LDAC_CIE cfg_cie; + + /* Use a liberal check when parsing the codec info */ + return (A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) || + (A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS); +} + +// Checks whether A2DP LDAC codec configuration matches with a device's codec +// capabilities. |p_cap| is the LDAC 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_CodecInfoMatchesCapabilityLdac( + const tA2DP_LDAC_CIE* p_cap, const uint8_t* p_codec_info, + bool is_capability) { + tA2DP_STATUS status; + tA2DP_LDAC_CIE cfg_cie; + + /* parse configuration */ + status = A2DP_ParseInfoLdac(&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_VendorUsesRtpHeaderLdac(UNUSED_ATTR bool content_protection_enabled, + UNUSED_ATTR const uint8_t* p_codec_info) { + // TODO: Is this correct? The RTP header is always included? + return true; +} + +const char* A2DP_VendorCodecNameLdac(UNUSED_ATTR const uint8_t* p_codec_info) { + return "LDAC"; +} + +bool A2DP_VendorCodecTypeEqualsLdac(const uint8_t* p_codec_info_a, + const uint8_t* p_codec_info_b) { + tA2DP_LDAC_CIE ldac_cie_a; + tA2DP_LDAC_CIE ldac_cie_b; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = + A2DP_ParseInfoLdac(&ldac_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_ParseInfoLdac(&ldac_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_VendorCodecEqualsLdac(const uint8_t* p_codec_info_a, + const uint8_t* p_codec_info_b) { + tA2DP_LDAC_CIE ldac_cie_a; + tA2DP_LDAC_CIE ldac_cie_b; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = + A2DP_ParseInfoLdac(&ldac_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_ParseInfoLdac(&ldac_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 (ldac_cie_a.sampleRate == ldac_cie_b.sampleRate) && + (ldac_cie_a.channelMode == ldac_cie_b.channelMode); +} + +int A2DP_VendorGetTrackSampleRateLdac(const uint8_t* p_codec_info) { + tA2DP_LDAC_CIE ldac_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_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 (ldac_cie.sampleRate) { + case A2DP_LDAC_SAMPLING_FREQ_44100: + return 44100; + case A2DP_LDAC_SAMPLING_FREQ_48000: + return 48000; + case A2DP_LDAC_SAMPLING_FREQ_88200: + return 88200; + case A2DP_LDAC_SAMPLING_FREQ_96000: + return 96000; + case A2DP_LDAC_SAMPLING_FREQ_176400: + return 176400; + case A2DP_LDAC_SAMPLING_FREQ_192000: + return 192000; + } + + return -1; +} + +int A2DP_VendorGetTrackBitsPerSampleLdac(const uint8_t* p_codec_info) { + tA2DP_LDAC_CIE ldac_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_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 (a2dp_ldac_caps.bits_per_sample) { + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16: + return 16; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24: + return 24; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32: + return 32; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE: + break; + } + return -1; +} + +int A2DP_VendorGetTrackChannelCountLdac(const uint8_t* p_codec_info) { + tA2DP_LDAC_CIE ldac_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_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 (ldac_cie.channelMode) { + case A2DP_LDAC_CHANNEL_MODE_MONO: + return 1; + case A2DP_LDAC_CHANNEL_MODE_DUAL: + return 2; + case A2DP_LDAC_CHANNEL_MODE_STEREO: + return 2; + } + + return -1; +} + +int A2DP_VendorGetChannelModeCodeLdac(const uint8_t* p_codec_info) { + tA2DP_LDAC_CIE ldac_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_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 (ldac_cie.channelMode) { + case A2DP_LDAC_CHANNEL_MODE_MONO: + case A2DP_LDAC_CHANNEL_MODE_DUAL: + case A2DP_LDAC_CHANNEL_MODE_STEREO: + return ldac_cie.channelMode; + default: + break; + } + + return -1; +} + +bool A2DP_VendorGetPacketTimestampLdac(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_VendorBuildCodecHeaderLdac(UNUSED_ATTR const uint8_t* p_codec_info, + BT_HDR* p_buf, + uint16_t frames_per_packet) { + uint8_t* p; + + p_buf->offset -= A2DP_LDAC_MPL_HDR_LEN; + p = (uint8_t*)(p_buf + 1) + p_buf->offset; + p_buf->len += A2DP_LDAC_MPL_HDR_LEN; + A2DP_BuildMediaPayloadHeaderLdac(p, false, false, false, + (uint8_t)frames_per_packet); + + return true; +} + +void A2DP_VendorDumpCodecInfoLdac(const uint8_t* p_codec_info) { + tA2DP_STATUS a2dp_status; + tA2DP_LDAC_CIE ldac_cie; + + LOG_DEBUG(LOG_TAG, "%s", __func__); + + a2dp_status = A2DP_ParseInfoLdac(&ldac_cie, p_codec_info, true); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR(LOG_TAG, "%s: A2DP_ParseInfoLdac fail:%d", __func__, a2dp_status); + return; + } + + LOG_DEBUG(LOG_TAG, "\tsamp_freq: 0x%x", ldac_cie.sampleRate); + if (ldac_cie.sampleRate == A2DP_LDAC_SAMPLING_FREQ_44100) { + LOG_DEBUG(LOG_TAG, "\tsamp_freq: (44100)"); + } + if (ldac_cie.sampleRate == A2DP_LDAC_SAMPLING_FREQ_48000) { + LOG_DEBUG(LOG_TAG, "\tsamp_freq: (48000)"); + } + if (ldac_cie.sampleRate == A2DP_LDAC_SAMPLING_FREQ_88200) { + LOG_DEBUG(LOG_TAG, "\tsamp_freq: (88200)"); + } + if (ldac_cie.sampleRate == A2DP_LDAC_SAMPLING_FREQ_96000) { + LOG_DEBUG(LOG_TAG, "\tsamp_freq: (96000)"); + } + if (ldac_cie.sampleRate == A2DP_LDAC_SAMPLING_FREQ_176400) { + LOG_DEBUG(LOG_TAG, "\tsamp_freq: (176400)"); + } + if (ldac_cie.sampleRate == A2DP_LDAC_SAMPLING_FREQ_192000) { + LOG_DEBUG(LOG_TAG, "\tsamp_freq: (192000)"); + } + + LOG_DEBUG(LOG_TAG, "\tch_mode: 0x%x", ldac_cie.channelMode); + if (ldac_cie.channelMode == A2DP_LDAC_CHANNEL_MODE_MONO) { + LOG_DEBUG(LOG_TAG, "\tch_mode: (Mono)"); + } + if (ldac_cie.channelMode == A2DP_LDAC_CHANNEL_MODE_DUAL) { + LOG_DEBUG(LOG_TAG, "\tch_mode: (Dual)"); + } + if (ldac_cie.channelMode == A2DP_LDAC_CHANNEL_MODE_STEREO) { + LOG_DEBUG(LOG_TAG, "\tch_mode: (Stereo)"); + } +} + +const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceLdac( + const uint8_t* p_codec_info) { + if (!A2DP_IsVendorSourceCodecValidLdac(p_codec_info)) return NULL; + + return &a2dp_encoder_interface_ldac; +} + +bool A2DP_VendorAdjustCodecLdac(uint8_t* p_codec_info) { + tA2DP_LDAC_CIE cfg_cie; + + // Nothing to do: just verify the codec info is valid + if (A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS) + return false; + + return true; +} + +btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexLdac( + UNUSED_ATTR const uint8_t* p_codec_info) { + return BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC; +} + +const char* A2DP_VendorCodecIndexStrLdac(void) { return "LDAC"; } + +bool A2DP_VendorInitCodecConfigLdac(tAVDT_CFG* p_cfg) { + if (A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_ldac_caps, + p_cfg->codec_info) != A2DP_SUCCESS) { + return false; + } + +#if (BTA_AV_CO_CP_SCMS_T == TRUE) + /* Content protection info - support SCMS-T */ + uint8_t* p = p_cfg->protect_info; + *p++ = AVDT_CP_LOSC; + UINT16_TO_STREAM(p, AVDT_CP_SCMS_T_ID); + p_cfg->num_protect = 1; +#endif + + return true; +} + +UNUSED_ATTR static void build_codec_config(const tA2DP_LDAC_CIE& config_cie, + btav_a2dp_codec_config_t* result) { + if (config_cie.sampleRate & A2DP_LDAC_SAMPLING_FREQ_44100) + result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + if (config_cie.sampleRate & A2DP_LDAC_SAMPLING_FREQ_48000) + result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + if (config_cie.sampleRate & A2DP_LDAC_SAMPLING_FREQ_88200) + result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_88200; + if (config_cie.sampleRate & A2DP_LDAC_SAMPLING_FREQ_96000) + result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_96000; + + result->bits_per_sample = config_cie.bits_per_sample; + + if (config_cie.channelMode & A2DP_LDAC_CHANNEL_MODE_MONO) + result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + if (config_cie.channelMode & + (A2DP_LDAC_CHANNEL_MODE_DUAL | A2DP_LDAC_CHANNEL_MODE_STEREO)) { + result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } +} + +A2dpCodecConfigLdac::A2dpCodecConfigLdac() + : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, "LDAC") {} + +A2dpCodecConfigLdac::~A2dpCodecConfigLdac() {} + +bool A2dpCodecConfigLdac::init() { + if (!isValid()) return false; + + // Load the encoder + if (!A2DP_VendorLoadEncoderLdac()) { + 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_LDAC_CIE* p_result, + btav_a2dp_codec_config_t* p_codec_config) { + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_192000) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_192000; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_192000; + return true; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_176400) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_176400; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_176400; + return true; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_96000) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_96000; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_96000; + return true; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_88200) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_88200; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_88200; + return true; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_48000) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_48000; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + return true; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_44100) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_44100; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + return true; + } + return false; +} + +// +// Selects the audio sample rate from |p_codec_audio_config|. +// |sampleRate| contains the capability. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_audio_sample_rate( + const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t sampleRate, + tA2DP_LDAC_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_LDAC_SAMPLING_FREQ_44100) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_44100; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + return true; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_48000: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_48000) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_48000; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + return true; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_88200: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_88200) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_88200; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_88200; + return true; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_96000: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_96000) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_96000; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_96000; + return true; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_176400: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_176400) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_176400; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_176400; + return true; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_192000: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_192000) { + p_result->sampleRate = A2DP_LDAC_SAMPLING_FREQ_192000; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_192000; + return true; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE: + break; + } + return false; +} + +// +// Selects the best bits per sample from |bits_per_sample|. +// |bits_per_sample| contains the capability. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_best_bits_per_sample( + btav_a2dp_codec_bits_per_sample_t bits_per_sample, tA2DP_LDAC_CIE* p_result, + btav_a2dp_codec_config_t* p_codec_config) { + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32; + return true; + } + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24; + return true; + } + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + return true; + } + return false; +} + +// +// Selects the audio bits per sample from |p_codec_audio_config|. +// |bits_per_sample| contains the capability. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_audio_bits_per_sample( + const btav_a2dp_codec_config_t* p_codec_audio_config, + btav_a2dp_codec_bits_per_sample_t bits_per_sample, tA2DP_LDAC_CIE* p_result, + btav_a2dp_codec_config_t* p_codec_config) { + switch (p_codec_audio_config->bits_per_sample) { + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + return true; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24; + return true; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32; + return true; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE: + break; + } + return false; +} + +// +// Selects the best channel mode from |channelMode|. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_best_channel_mode(uint8_t channelMode, + tA2DP_LDAC_CIE* p_result, + btav_a2dp_codec_config_t* p_codec_config) { + if (channelMode & A2DP_LDAC_CHANNEL_MODE_STEREO) { + p_result->channelMode = A2DP_LDAC_CHANNEL_MODE_STEREO; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + return true; + } + if (channelMode & A2DP_LDAC_CHANNEL_MODE_DUAL) { + p_result->channelMode = A2DP_LDAC_CHANNEL_MODE_DUAL; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + return true; + } + if (channelMode & A2DP_LDAC_CHANNEL_MODE_MONO) { + p_result->channelMode = A2DP_LDAC_CHANNEL_MODE_MONO; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + return true; + } + return false; +} + +// +// Selects the audio channel mode from |p_codec_audio_config|. +// |channelMode| contains the capability. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_audio_channel_mode( + const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t channelMode, + tA2DP_LDAC_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_LDAC_CHANNEL_MODE_MONO) { + p_result->channelMode = A2DP_LDAC_CHANNEL_MODE_MONO; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + return true; + } + break; + case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO: + if (channelMode & A2DP_LDAC_CHANNEL_MODE_STEREO) { + p_result->channelMode = A2DP_LDAC_CHANNEL_MODE_STEREO; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + return true; + } + if (channelMode & A2DP_LDAC_CHANNEL_MODE_DUAL) { + p_result->channelMode = A2DP_LDAC_CHANNEL_MODE_DUAL; + 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 A2dpCodecConfigLdac::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_LDAC_CIE sink_info_cie; + tA2DP_LDAC_CIE result_config_cie; + uint8_t channelMode; + uint8_t sampleRate; + btav_a2dp_codec_bits_per_sample_t bits_per_sample; + + // Save the internal state + btav_a2dp_codec_config_t saved_codec_config = codec_config_; + btav_a2dp_codec_config_t saved_codec_capability = codec_capability_; + btav_a2dp_codec_config_t saved_codec_user_config = codec_user_config_; + btav_a2dp_codec_config_t saved_codec_audio_config = codec_audio_config_; + uint8_t saved_ota_codec_config[AVDT_CODEC_SIZE]; + uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE]; + uint8_t saved_ota_codec_peer_config[AVDT_CODEC_SIZE]; + memcpy(saved_ota_codec_config, ota_codec_config_, sizeof(ota_codec_config_)); + memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_, + sizeof(ota_codec_peer_capability_)); + memcpy(saved_ota_codec_peer_config, ota_codec_peer_config_, + sizeof(ota_codec_peer_config_)); + + tA2DP_STATUS status = + A2DP_ParseInfoLdac(&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_ldac_caps.vendorId; + result_config_cie.codecId = a2dp_ldac_caps.codecId; + + // + // Select the sample frequency + // + sampleRate = a2dp_ldac_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_LDAC_SAMPLING_FREQ_44100) { + result_config_cie.sampleRate = A2DP_LDAC_SAMPLING_FREQ_44100; + codec_capability_.sample_rate = codec_user_config_.sample_rate; + codec_config_.sample_rate = codec_user_config_.sample_rate; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_48000: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_48000) { + result_config_cie.sampleRate = A2DP_LDAC_SAMPLING_FREQ_48000; + codec_capability_.sample_rate = codec_user_config_.sample_rate; + codec_config_.sample_rate = codec_user_config_.sample_rate; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_88200: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_88200) { + result_config_cie.sampleRate = A2DP_LDAC_SAMPLING_FREQ_88200; + codec_capability_.sample_rate = codec_user_config_.sample_rate; + codec_config_.sample_rate = codec_user_config_.sample_rate; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_96000: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_96000) { + result_config_cie.sampleRate = A2DP_LDAC_SAMPLING_FREQ_96000; + codec_capability_.sample_rate = codec_user_config_.sample_rate; + codec_config_.sample_rate = codec_user_config_.sample_rate; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_176400: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_176400) { + result_config_cie.sampleRate = A2DP_LDAC_SAMPLING_FREQ_176400; + 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_192000: + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_192000) { + result_config_cie.sampleRate = A2DP_LDAC_SAMPLING_FREQ_192000; + codec_capability_.sample_rate = codec_user_config_.sample_rate; + codec_config_.sample_rate = codec_user_config_.sample_rate; + } + 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_LDAC_SAMPLING_FREQ_44100) + codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_48000) + codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_88200) + codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_88200; + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_96000) + codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_96000; + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_176400) + codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_176400; + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_192000) + codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_192000; + + // 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_ldac_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_ldac_caps.sampleRate, sink_info_cie.sampleRate); + goto fail; + } + + // + // Select the bits per sample + // + // NOTE: this information is NOT included in the LDAC A2DP codec description + // that is sent OTA. + bits_per_sample = a2dp_ldac_caps.bits_per_sample; + codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE; + switch (codec_user_config_.bits_per_sample) { + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) { + result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample; + codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample; + codec_config_.bits_per_sample = codec_user_config_.bits_per_sample; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) { + result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample; + codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample; + codec_config_.bits_per_sample = codec_user_config_.bits_per_sample; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) { + result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample; + codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample; + codec_config_.bits_per_sample = codec_user_config_.bits_per_sample; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE: + result_config_cie.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE; + codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE; + codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE; + break; + } + + // Select the bits per sample if there is no user preference + do { + if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) + break; + + // Compute the common capability + codec_capability_.bits_per_sample = bits_per_sample; + + // No user preference - the the codec audio config + if (select_audio_bits_per_sample(&codec_audio_config_, + a2dp_ldac_caps.bits_per_sample, + &result_config_cie, &codec_config_)) { + break; + } + + // No user preference - try the default config + if (select_best_bits_per_sample(a2dp_ldac_default_config.bits_per_sample, + &result_config_cie, &codec_config_)) { + break; + } + + // No user preference - use the best match + if (select_best_bits_per_sample(a2dp_ldac_caps.bits_per_sample, + &result_config_cie, &codec_config_)) { + break; + } + } while (false); + if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) { + LOG_ERROR(LOG_TAG, + "%s: cannot match bits per sample: default = 0x%x " + "user preference = 0x%x", + __func__, a2dp_ldac_default_config.bits_per_sample, + codec_user_config_.bits_per_sample); + goto fail; + } + + // + // Select the channel mode + // + channelMode = a2dp_ldac_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_LDAC_CHANNEL_MODE_MONO) { + result_config_cie.channelMode = A2DP_LDAC_CHANNEL_MODE_MONO; + codec_capability_.channel_mode = codec_user_config_.channel_mode; + codec_config_.channel_mode = codec_user_config_.channel_mode; + } + break; + case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO: + if (channelMode & A2DP_LDAC_CHANNEL_MODE_STEREO) { + result_config_cie.channelMode = A2DP_LDAC_CHANNEL_MODE_STEREO; + codec_capability_.channel_mode = codec_user_config_.channel_mode; + codec_config_.channel_mode = codec_user_config_.channel_mode; + break; + } + if (channelMode & A2DP_LDAC_CHANNEL_MODE_DUAL) { + result_config_cie.channelMode = A2DP_LDAC_CHANNEL_MODE_DUAL; + codec_capability_.channel_mode = codec_user_config_.channel_mode; + codec_config_.channel_mode = codec_user_config_.channel_mode; + break; + } + 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_LDAC_CHANNEL_MODE_MONO) + codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + if (channelMode & + (A2DP_LDAC_CHANNEL_MODE_STEREO | A2DP_LDAC_CHANNEL_MODE_DUAL)) { + 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_ldac_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_ldac_caps.channelMode, sink_info_cie.channelMode); + goto fail; + } + + if (A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie, + p_result_codec_config) != A2DP_SUCCESS) { + goto fail; + } + + // + // Copy the codec-specific fields if they are not zero + // + if (codec_user_config_.codec_specific_1 != 0) + codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1; + if (codec_user_config_.codec_specific_2 != 0) + codec_config_.codec_specific_2 = codec_user_config_.codec_specific_2; + if (codec_user_config_.codec_specific_3 != 0) + codec_config_.codec_specific_3 = codec_user_config_.codec_specific_3; + if (codec_user_config_.codec_specific_4 != 0) + codec_config_.codec_specific_4 = codec_user_config_.codec_specific_4; + + // Create a local copy of the peer codec capability, and the + // result codec config. + if (is_capability) { + status = A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie, + ota_codec_peer_capability_); + } else { + status = A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie, + ota_codec_peer_config_); + } + CHECK(status == A2DP_SUCCESS); + status = A2DP_BuildInfoLdac(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/system/stack/a2dp/a2dp_vendor_ldac_encoder.cc b/system/stack/a2dp/a2dp_vendor_ldac_encoder.cc new file mode 100644 index 0000000000000000000000000000000000000000..70456b490e74cd07f50584d6ebaa402a27191839 --- /dev/null +++ b/system/stack/a2dp/a2dp_vendor_ldac_encoder.cc @@ -0,0 +1,807 @@ +/* + * 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_ldac_encoder" + +#include "a2dp_vendor_ldac_encoder.h" + +#include <dlfcn.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> + +#include "a2dp_vendor.h" +#include "a2dp_vendor_ldac.h" +#include "bt_common.h" +#include "osi/include/log.h" +#include "osi/include/osi.h" + +// +// Encoder for LDAC Source Codec +// + +// +// TODO: Local typedefs and defines to avoid including the LDAC encoder +// header file. +// +typedef void* HANDLE_LDAC_BT; +typedef enum { + LDACBT_SMPL_FMT_S16 = 0x2, + LDACBT_SMPL_FMT_S24 = 0x3, + LDACBT_SMPL_FMT_S32 = 0x4, + LDACBT_SMPL_FMT_F32 = 0x5, +} LDACBT_SMPL_FMT_T; + +enum { + LDACBT_EQMID_HQ = 0, + LDACBT_EQMID_SQ, + LDACBT_EQMID_MQ, +}; + +#define LDACBT_ENC_LSU 128 +#define LDACBT_MAX_LSU 512 +#define LDACBT_API_ERR(err) ((err >> 20) & 0x0FFF) +#define LDACBT_HANDLE_ERR(err) ((err >> 10) & 0x03FF) +#define LDACBT_BLOCK_ERR(err) (err & 0x03FF) + +// +// The LDAC encoder shared library, and the functions to use +// +static const char* LDAC_ENCODER_LIB_NAME = "libldacBT_enc.so"; +static void* ldac_encoder_lib_handle = NULL; + +static const char* LDAC_GET_HANDLE_NAME = "ldacBT_get_handle"; +typedef HANDLE_LDAC_BT (*tLDAC_GET_HANDLE)(void); + +static const char* LDAC_FREE_HANDLE_NAME = "ldacBT_free_handle"; +typedef void (*tLDAC_FREE_HANDLE)(HANDLE_LDAC_BT hLdacParam); + +static const char* LDAC_CLOSE_HANDLE_NAME = "ldacBT_close_handle"; +typedef void (*tLDAC_CLOSE_HANDLE)(HANDLE_LDAC_BT hLdacParam); + +static const char* LDAC_GET_VERSION_NAME = "ldacBT_get_version"; +typedef int (*tLDAC_GET_VERSION)(void); + +static const char* LDAC_GET_BITRATE_NAME = "ldacBT_get_bitrate"; +typedef int (*tLDAC_GET_BITRATE)(HANDLE_LDAC_BT hLdacParam); + +static const char* LDAC_GET_SAMPLING_FREQ_NAME = "ldacBT_get_sampling_freq"; +typedef int (*tLDAC_GET_SAMPLING_FREQ)(HANDLE_LDAC_BT hLdacParam); + +static const char* LDAC_INIT_HANDLE_ENCODE_NAME = "ldacBT_init_handle_encode"; +typedef int (*tLDAC_INIT_HANDLE_ENCODE)(HANDLE_LDAC_BT hLdacParam, int mtu, + int eqmid, int channel_mode, + LDACBT_SMPL_FMT_T fmt, + int sampling_freq); + +static const char* LDAC_ENCODE_NAME = "ldacBT_encode"; +typedef int (*tLDAC_ENCODE)(HANDLE_LDAC_BT hLdacParam, void* p_pcm, + int* p_pcm_encoded_byte, unsigned char* p_stream, + int* pframe_length_wrote, int* pframe_num); + +static const char* LDAC_SET_EQMID_NAME = "ldacBT_set_eqmid"; +typedef int (*tLDAC_SET_EQMID)(HANDLE_LDAC_BT hLdacParam, int eqmid); + +static const char* LDAC_ALTER_EQMID_PRIORITY_NAME = + "ldacBT_alter_eqmid_priority"; +typedef int (*tLDAC_ALTER_EQMID_PRIORITY)(HANDLE_LDAC_BT hLdacParam, + int priority); + +static const char* LDAC_GET_EQMID_NAME = "ldacBT_get_eqmid"; +typedef int (*tLDAC_GET_EQMID)(HANDLE_LDAC_BT hLdacParam); + +static const char* LDAC_GET_ERROR_CODE_NAME = "ldacBT_get_error_code"; +typedef int (*tLDAC_GET_ERROR_CODE)(HANDLE_LDAC_BT hLdacParam); + +static tLDAC_GET_HANDLE ldac_get_handle_func; +static tLDAC_FREE_HANDLE ldac_free_handle_func; +static tLDAC_CLOSE_HANDLE ldac_close_handle_func; +static tLDAC_GET_VERSION ldac_get_version_func; +static tLDAC_GET_BITRATE ldac_get_bitrate_func; +static tLDAC_GET_SAMPLING_FREQ ldac_get_sampling_freq_func; +static tLDAC_INIT_HANDLE_ENCODE ldac_init_handle_encode_func; +static tLDAC_ENCODE ldac_encode_func; +static tLDAC_SET_EQMID ldac_set_eqmid_func; +static tLDAC_ALTER_EQMID_PRIORITY ldac_alter_eqmid_priority_func; +static tLDAC_GET_EQMID ldac_get_eqmid_func; +static tLDAC_GET_ERROR_CODE ldac_get_error_code_func; + +#define STATS_UPDATE_MAX(current_value_storage, new_value) \ + do { \ + if ((new_value) > (current_value_storage)) \ + (current_value_storage) = (new_value); \ + } while (0) + +// A2DP LDAC encoder interval in milliseconds +#define A2DP_LDAC_ENCODER_INTERVAL_MS 20 +#define A2DP_LDAC_MEDIA_BYTES_PER_FRAME 128 + +// offset +#if (BTA_AV_CO_CP_SCMS_T == TRUE) +#define A2DP_LDAC_OFFSET (AVDT_MEDIA_OFFSET + A2DP_LDAC_MPL_HDR_LEN + 1) +#else +#define A2DP_LDAC_OFFSET (AVDT_MEDIA_OFFSET + A2DP_LDAC_MPL_HDR_LEN) +#endif + +typedef struct { + uint32_t sample_rate; + uint8_t channel_mode; + uint8_t bits_per_sample; + int quality_mode_index; + int pcm_wlength; + LDACBT_SMPL_FMT_T pcm_fmt; +} tA2DP_LDAC_ENCODER_PARAMS; + +typedef struct { + uint32_t counter; + uint32_t bytes_per_tick; /* pcm bytes read each media task tick */ + uint64_t last_frame_us; +} tA2DP_LDAC_FEEDING_STATE; + +typedef struct { + uint64_t session_start_us; + + size_t media_read_total_expected_packets; + size_t media_read_total_expected_reads_count; + size_t media_read_total_expected_read_bytes; + + size_t media_read_total_dropped_packets; + size_t media_read_total_actual_reads_count; + size_t media_read_total_actual_read_bytes; +} a2dp_ldac_encoder_stats_t; + +typedef struct { + a2dp_source_read_callback_t read_callback; + a2dp_source_enqueue_callback_t enqueue_callback; + uint16_t TxAaMtuSize; + + bool use_SCMS_T; + bool is_peer_edr; // True if the peer device supports EDR + bool peer_supports_3mbps; // True if the peer device supports 3Mbps EDR + uint16_t peer_mtu; // MTU of the A2DP peer + uint32_t timestamp; // Timestamp for the A2DP frames + + HANDLE_LDAC_BT ldac_handle; + bool has_ldac_handle; // True if ldac_handle is valid + + tA2DP_FEEDING_PARAMS feeding_params; + tA2DP_LDAC_ENCODER_PARAMS ldac_encoder_params; + tA2DP_LDAC_FEEDING_STATE ldac_feeding_state; + + a2dp_ldac_encoder_stats_t stats; +} tA2DP_LDAC_ENCODER_CB; + +static tA2DP_LDAC_ENCODER_CB a2dp_ldac_encoder_cb; + +static void a2dp_vendor_ldac_encoder_update(uint16_t peer_mtu, + A2dpCodecConfig* a2dp_codec_config, + bool* p_restart_input, + bool* p_restart_output, + bool* p_config_updated); +static void a2dp_ldac_get_num_frame_iteration(uint8_t* num_of_iterations, + uint8_t* num_of_frames, + uint64_t timestamp_us); +static void a2dp_ldac_encode_frames(uint8_t nb_frame); +static bool a2dp_ldac_read_feeding(uint8_t* read_buffer); + +static void* load_func(const char* func_name) { + void* func_ptr = dlsym(ldac_encoder_lib_handle, func_name); + if (func_ptr == NULL) { + LOG_ERROR(LOG_TAG, + "%s: cannot find function '%s' in the encoder library: %s", + __func__, func_name, dlerror()); + A2DP_VendorUnloadEncoderLdac(); + return NULL; + } + return func_ptr; +} + +bool A2DP_VendorLoadEncoderLdac(void) { + if (ldac_encoder_lib_handle != NULL) return true; // Already loaded + + // Initialize the control block + memset(&a2dp_ldac_encoder_cb, 0, sizeof(a2dp_ldac_encoder_cb)); + + // Open the encoder library + ldac_encoder_lib_handle = dlopen(LDAC_ENCODER_LIB_NAME, RTLD_NOW); + if (ldac_encoder_lib_handle == NULL) { + LOG_ERROR(LOG_TAG, "%s: cannot open LDAC encoder library %s: %s", __func__, + LDAC_ENCODER_LIB_NAME, dlerror()); + return false; + } + + // Load all functions + ldac_get_handle_func = (tLDAC_GET_HANDLE)load_func(LDAC_GET_HANDLE_NAME); + if (ldac_get_handle_func == NULL) return false; + ldac_free_handle_func = (tLDAC_FREE_HANDLE)load_func(LDAC_FREE_HANDLE_NAME); + if (ldac_free_handle_func == NULL) return false; + ldac_close_handle_func = + (tLDAC_CLOSE_HANDLE)load_func(LDAC_CLOSE_HANDLE_NAME); + if (ldac_close_handle_func == NULL) return false; + ldac_get_version_func = (tLDAC_GET_VERSION)load_func(LDAC_GET_VERSION_NAME); + if (ldac_get_version_func == NULL) return false; + ldac_get_bitrate_func = (tLDAC_GET_BITRATE)load_func(LDAC_GET_BITRATE_NAME); + if (ldac_get_bitrate_func == NULL) return false; + ldac_get_sampling_freq_func = + (tLDAC_GET_SAMPLING_FREQ)load_func(LDAC_GET_SAMPLING_FREQ_NAME); + if (ldac_get_sampling_freq_func == NULL) return false; + ldac_init_handle_encode_func = + (tLDAC_INIT_HANDLE_ENCODE)load_func(LDAC_INIT_HANDLE_ENCODE_NAME); + if (ldac_init_handle_encode_func == NULL) return false; + ldac_encode_func = (tLDAC_ENCODE)load_func(LDAC_ENCODE_NAME); + if (ldac_encode_func == NULL) return false; + ldac_set_eqmid_func = (tLDAC_SET_EQMID)load_func(LDAC_SET_EQMID_NAME); + if (ldac_set_eqmid_func == NULL) return false; + ldac_alter_eqmid_priority_func = + (tLDAC_ALTER_EQMID_PRIORITY)load_func(LDAC_ALTER_EQMID_PRIORITY_NAME); + if (ldac_alter_eqmid_priority_func == NULL) return false; + ldac_get_eqmid_func = (tLDAC_GET_EQMID)load_func(LDAC_GET_EQMID_NAME); + if (ldac_get_eqmid_func == NULL) return false; + ldac_get_error_code_func = + (tLDAC_GET_ERROR_CODE)load_func(LDAC_GET_ERROR_CODE_NAME); + if (ldac_get_error_code_func == NULL) return false; + + return true; +} + +void A2DP_VendorUnloadEncoderLdac(void) { + // Cleanup any LDAC-related state + if (a2dp_ldac_encoder_cb.has_ldac_handle && ldac_free_handle_func != NULL) + ldac_free_handle_func(a2dp_ldac_encoder_cb.ldac_handle); + memset(&a2dp_ldac_encoder_cb, 0, sizeof(a2dp_ldac_encoder_cb)); + + ldac_get_handle_func = NULL; + ldac_free_handle_func = NULL; + ldac_close_handle_func = NULL; + ldac_get_version_func = NULL; + ldac_get_bitrate_func = NULL; + ldac_get_sampling_freq_func = NULL; + ldac_init_handle_encode_func = NULL; + ldac_encode_func = NULL; + ldac_set_eqmid_func = NULL; + ldac_alter_eqmid_priority_func = NULL; + ldac_get_eqmid_func = NULL; + ldac_get_error_code_func = NULL; + + if (ldac_encoder_lib_handle != NULL) { + dlclose(ldac_encoder_lib_handle); + ldac_encoder_lib_handle = NULL; + } +} + +void a2dp_vendor_ldac_encoder_init( + const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, + A2dpCodecConfig* a2dp_codec_config, + a2dp_source_read_callback_t read_callback, + a2dp_source_enqueue_callback_t enqueue_callback) { + if (a2dp_ldac_encoder_cb.has_ldac_handle) + ldac_free_handle_func(a2dp_ldac_encoder_cb.ldac_handle); + memset(&a2dp_ldac_encoder_cb, 0, sizeof(a2dp_ldac_encoder_cb)); + + a2dp_ldac_encoder_cb.stats.session_start_us = time_get_os_boottime_us(); + + a2dp_ldac_encoder_cb.read_callback = read_callback; + a2dp_ldac_encoder_cb.enqueue_callback = enqueue_callback; + a2dp_ldac_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr; + a2dp_ldac_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps; + a2dp_ldac_encoder_cb.peer_mtu = p_peer_params->peer_mtu; + a2dp_ldac_encoder_cb.timestamp = 0; + + a2dp_ldac_encoder_cb.use_SCMS_T = false; // TODO: should be a parameter +#if (BTA_AV_CO_CP_SCMS_T == TRUE) + a2dp_ldac_encoder_cb.use_SCMS_T = true; +#endif + + a2dp_ldac_encoder_cb.ldac_handle = ldac_get_handle_func(); + if (a2dp_ldac_encoder_cb.ldac_handle == NULL) { + LOG_ERROR(LOG_TAG, "%s: Cannot get LDAC encoder handle", __func__); + return; // TODO: Return an error? + } + a2dp_ldac_encoder_cb.has_ldac_handle = true; + + // 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_ldac_encoder_update(a2dp_ldac_encoder_cb.peer_mtu, + a2dp_codec_config, &restart_input, + &restart_output, &config_updated); +} + +bool A2dpCodecConfigLdac::updateEncoderUserConfig( + const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input, + bool* p_restart_output, bool* p_config_updated) { + a2dp_ldac_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr; + a2dp_ldac_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps; + a2dp_ldac_encoder_cb.peer_mtu = p_peer_params->peer_mtu; + a2dp_ldac_encoder_cb.timestamp = 0; + + if (a2dp_ldac_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_ldac_encoder_update(a2dp_ldac_encoder_cb.peer_mtu, this, + p_restart_input, p_restart_output, + p_config_updated); + return true; +} + +// Update the A2DP LDAC encoder. +// |peer_mtu| is the peer MTU. +// |a2dp_codec_config| is the A2DP codec to use for the update. +static void a2dp_vendor_ldac_encoder_update(uint16_t peer_mtu, + A2dpCodecConfig* a2dp_codec_config, + bool* p_restart_input, + bool* p_restart_output, + bool* p_config_updated) { + tA2DP_LDAC_ENCODER_PARAMS* p_encoder_params = + &a2dp_ldac_encoder_cb.ldac_encoder_params; + 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; + btav_a2dp_codec_config_t codec_config = a2dp_codec_config->getCodecConfig(); + + // The feeding parameters + a2dp_ldac_encoder_cb.feeding_params.sample_rate = + A2DP_VendorGetTrackSampleRateLdac(p_codec_info); + a2dp_ldac_encoder_cb.feeding_params.bits_per_sample = + a2dp_codec_config->getAudioBitsPerSample(); + a2dp_ldac_encoder_cb.feeding_params.channel_count = + A2DP_VendorGetTrackChannelCountLdac(p_codec_info); + + // The codec parameters + p_encoder_params->sample_rate = + a2dp_ldac_encoder_cb.feeding_params.sample_rate; + p_encoder_params->channel_mode = + A2DP_VendorGetChannelModeCodeLdac(p_codec_info); + + uint16_t mtu_size = + BT_DEFAULT_BUFFER_SIZE - A2DP_LDAC_OFFSET - sizeof(BT_HDR); + if (mtu_size < peer_mtu) { + a2dp_ldac_encoder_cb.TxAaMtuSize = mtu_size; + } else { + a2dp_ldac_encoder_cb.TxAaMtuSize = peer_mtu; + } + + // Set the quality mode index + int old_quality_mode_index = p_encoder_params->quality_mode_index; + if (codec_config.codec_specific_1 != 0) { + p_encoder_params->quality_mode_index = codec_config.codec_specific_1 % 10; + LOG_DEBUG(LOG_TAG, "%s: setting quality index to %d", __func__, + p_encoder_params->quality_mode_index); + } else { + p_encoder_params->quality_mode_index = LDACBT_EQMID_HQ; + LOG_DEBUG(LOG_TAG, "%s: setting quality index to default value of %d", + __func__, p_encoder_params->quality_mode_index); + } + if (p_encoder_params->quality_mode_index != old_quality_mode_index) + *p_config_updated = true; + + p_encoder_params->pcm_wlength = + a2dp_ldac_encoder_cb.feeding_params.bits_per_sample >> 3; + // Set the Audio format from pcm_wlength + p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S16; + if (p_encoder_params->pcm_wlength == 2) + p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S16; + else if (p_encoder_params->pcm_wlength == 3) + p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S24; + else if (p_encoder_params->pcm_wlength == 4) + p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S32; + + LOG_DEBUG(LOG_TAG, "%s: MTU=%d, peer_mtu=%d", __func__, + a2dp_ldac_encoder_cb.TxAaMtuSize, peer_mtu); + LOG_DEBUG(LOG_TAG, + "%s: sample_rate: %d channel_mode: %d " + "quality_mode_index: %d pcm_wlength: %d pcm_fmt: %d", + __func__, p_encoder_params->sample_rate, + p_encoder_params->channel_mode, + p_encoder_params->quality_mode_index, p_encoder_params->pcm_wlength, + p_encoder_params->pcm_fmt); + + // Initialize the encoder. + // NOTE: MTU in the initialization must include the AVDT media header size. + int result = ldac_init_handle_encode_func( + a2dp_ldac_encoder_cb.ldac_handle, + a2dp_ldac_encoder_cb.TxAaMtuSize + AVDT_MEDIA_HDR_SIZE, + p_encoder_params->quality_mode_index, p_encoder_params->channel_mode, + p_encoder_params->pcm_fmt, p_encoder_params->sample_rate); + if (result != 0) { + LOG_ERROR(LOG_TAG, "%s: error initializing the LDAC encoder: %d", __func__, + result); + } +} + +void a2dp_vendor_ldac_encoder_cleanup(void) { + if (a2dp_ldac_encoder_cb.has_ldac_handle) + ldac_free_handle_func(a2dp_ldac_encoder_cb.ldac_handle); + memset(&a2dp_ldac_encoder_cb, 0, sizeof(a2dp_ldac_encoder_cb)); +} + +void a2dp_vendor_ldac_feeding_init( + const tA2DP_FEEDING_PARAMS* p_feeding_params) { + tA2DP_LDAC_ENCODER_PARAMS* p_encoder_params = + &a2dp_ldac_encoder_cb.ldac_encoder_params; + bool reconfig_needed = false; + + 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); + + /* Save the feeding information */ + memcpy(&a2dp_ldac_encoder_cb.feeding_params, p_feeding_params, + sizeof(tA2DP_FEEDING_PARAMS)); + + // Check the PCM feeding sample_rate + switch (p_feeding_params->sample_rate) { + case 44100: + case 48000: + case 88200: + case 96000: + case 176400: + case 192000: + break; + default: + LOG_WARN(LOG_TAG, "%s: feeding PCM sample_rate %u is not supported", + __func__, p_feeding_params->sample_rate); + return; + } + if (p_encoder_params->sample_rate != p_feeding_params->sample_rate) { + LOG_DEBUG(LOG_TAG, + "%s: codec reconfiguration: feeding PCM sample_rate " + "from %u to %u", + __func__, p_encoder_params->sample_rate, + p_feeding_params->sample_rate); + reconfig_needed = true; + p_encoder_params->sample_rate = p_feeding_params->sample_rate; + } + + // Check the bits per sample + switch (p_feeding_params->bits_per_sample) { + case 16: + case 24: + case 32: + break; + default: + LOG_WARN(LOG_TAG, "%s: feeding PCM bits_per_sample %u is not supported", + __func__, p_feeding_params->bits_per_sample); + return; + } + if (p_encoder_params->bits_per_sample != p_feeding_params->bits_per_sample) { + LOG_DEBUG(LOG_TAG, + "%s: LDAC reconfiguration needed: " + "feeding PCM bits_per_sample from %u to %u", + __func__, p_encoder_params->bits_per_sample, + p_feeding_params->bits_per_sample); + p_encoder_params->bits_per_sample = p_feeding_params->bits_per_sample; + // NOTE: A2DP reconfigure is not necessary in this case. Re-initializing + // just the LDAC encoder is sufficient. + reconfig_needed = true; + } + + // 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; + } + + if ((p_feeding_params->channel_count == 1) && + (p_encoder_params->channel_mode != A2DP_LDAC_CHANNEL_MODE_MONO)) { + LOG_DEBUG(LOG_TAG, + "%s: codec reconfiguration: feeding PCM channel_count " + "from 2 to %u", + __func__, p_feeding_params->channel_count); + reconfig_needed = true; + p_encoder_params->channel_mode = A2DP_LDAC_CHANNEL_MODE_MONO; + } + if ((p_feeding_params->channel_count == 2) && + (p_encoder_params->channel_mode != A2DP_LDAC_CHANNEL_MODE_STEREO) && + (p_encoder_params->channel_mode != A2DP_LDAC_CHANNEL_MODE_DUAL)) { + LOG_DEBUG(LOG_TAG, + "%s: codec reconfiguration: feeding PCM channel_count " + "from 1 to %u", + __func__, p_feeding_params->channel_count); + reconfig_needed = true; + p_encoder_params->channel_mode = A2DP_LDAC_CHANNEL_MODE_STEREO; + } + + p_encoder_params->pcm_wlength = p_encoder_params->bits_per_sample >> 3; + // Set the Audio format from pcm_wlength + p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S16; + if (p_encoder_params->pcm_wlength == 2) + p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S16; + else if (p_encoder_params->pcm_wlength == 3) + p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S24; + else if (p_encoder_params->pcm_wlength == 4) + p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S32; + + if (!reconfig_needed) { + LOG_DEBUG(LOG_TAG, "%s: no LDAC reconfiguration needed", __func__); + return; + } + + 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); + + if (a2dp_ldac_encoder_cb.has_ldac_handle) { + ldac_free_handle_func(a2dp_ldac_encoder_cb.ldac_handle); + a2dp_ldac_encoder_cb.has_ldac_handle = false; + } + a2dp_ldac_encoder_cb.ldac_handle = ldac_get_handle_func(); + if (a2dp_ldac_encoder_cb.ldac_handle == NULL) { + LOG_ERROR(LOG_TAG, "%s: Cannot get LDAC encoder handle", __func__); + return; // TODO: Return an error? + } + a2dp_ldac_encoder_cb.has_ldac_handle = true; + + LOG_DEBUG(LOG_TAG, + "%s: sample_rate: %d channel_mode: %d " + "quality_mode_index: %d pcm_wlength: %d pcm_fmt: %d", + __func__, p_encoder_params->sample_rate, + p_encoder_params->channel_mode, + p_encoder_params->quality_mode_index, p_encoder_params->pcm_wlength, + p_encoder_params->pcm_fmt); + + // Initialize the encoder. + // NOTE: MTU in the initialization must include the AVDT media header size. + int result = ldac_init_handle_encode_func( + a2dp_ldac_encoder_cb.ldac_handle, + a2dp_ldac_encoder_cb.TxAaMtuSize + AVDT_MEDIA_HDR_SIZE, + p_encoder_params->quality_mode_index, p_encoder_params->channel_mode, + p_encoder_params->pcm_fmt, p_encoder_params->sample_rate); + if (result != 0) { + LOG_ERROR(LOG_TAG, "%s: error initializing the LDAC encoder: %d", __func__, + result); + } +} + +void a2dp_vendor_ldac_feeding_reset(void) { + /* By default, just clear the entire state */ + memset(&a2dp_ldac_encoder_cb.ldac_feeding_state, 0, + sizeof(a2dp_ldac_encoder_cb.ldac_feeding_state)); + + a2dp_ldac_encoder_cb.ldac_feeding_state.bytes_per_tick = + (a2dp_ldac_encoder_cb.feeding_params.sample_rate * + a2dp_ldac_encoder_cb.feeding_params.bits_per_sample / 8 * + a2dp_ldac_encoder_cb.feeding_params.channel_count * + A2DP_LDAC_ENCODER_INTERVAL_MS) / + 1000; + + LOG_DEBUG(LOG_TAG, "%s: PCM bytes per tick %u", __func__, + a2dp_ldac_encoder_cb.ldac_feeding_state.bytes_per_tick); +} + +void a2dp_vendor_ldac_feeding_flush(void) { + a2dp_ldac_encoder_cb.ldac_feeding_state.counter = 0; +} + +period_ms_t a2dp_vendor_ldac_get_encoder_interval_ms(void) { + return A2DP_LDAC_ENCODER_INTERVAL_MS; +} + +void a2dp_vendor_ldac_send_frames(uint64_t timestamp_us) { + uint8_t nb_frame = 0; + uint8_t nb_iterations = 0; + + a2dp_ldac_get_num_frame_iteration(&nb_iterations, &nb_frame, timestamp_us); + LOG_VERBOSE(LOG_TAG, "%s: Sending %d frames per iteration, %d iterations", + __func__, nb_frame, nb_iterations); + if (nb_frame == 0) return; + + for (uint8_t counter = 0; counter < nb_iterations; counter++) { + // Transcode frame and enqueue + a2dp_ldac_encode_frames(nb_frame); + } +} + +// Obtains the number of frames to send and number of iterations +// to be used. |num_of_iterations| and |num_of_frames| parameters +// are used as output param for returning the respective values. +static void a2dp_ldac_get_num_frame_iteration(uint8_t* num_of_iterations, + uint8_t* num_of_frames, + uint64_t timestamp_us) { + uint32_t result = 0; + uint8_t nof = 0; + uint8_t noi = 1; + + uint32_t pcm_bytes_per_frame = + A2DP_LDAC_MEDIA_BYTES_PER_FRAME * + a2dp_ldac_encoder_cb.feeding_params.channel_count * + a2dp_ldac_encoder_cb.feeding_params.bits_per_sample / 8; + LOG_VERBOSE(LOG_TAG, "%s: pcm_bytes_per_frame %u", __func__, + pcm_bytes_per_frame); + + uint32_t us_this_tick = A2DP_LDAC_ENCODER_INTERVAL_MS * 1000; + uint64_t now_us = timestamp_us; + if (a2dp_ldac_encoder_cb.ldac_feeding_state.last_frame_us != 0) + us_this_tick = + (now_us - a2dp_ldac_encoder_cb.ldac_feeding_state.last_frame_us); + a2dp_ldac_encoder_cb.ldac_feeding_state.last_frame_us = now_us; + + a2dp_ldac_encoder_cb.ldac_feeding_state.counter += + a2dp_ldac_encoder_cb.ldac_feeding_state.bytes_per_tick * us_this_tick / + (A2DP_LDAC_ENCODER_INTERVAL_MS * 1000); + + result = + a2dp_ldac_encoder_cb.ldac_feeding_state.counter / pcm_bytes_per_frame; + a2dp_ldac_encoder_cb.ldac_feeding_state.counter -= + result * pcm_bytes_per_frame; + nof = result; + + LOG_VERBOSE(LOG_TAG, "%s: effective num of frames %u, iterations %u", + __func__, nof, noi); + + *num_of_frames = nof; + *num_of_iterations = noi; +} + +static void a2dp_ldac_encode_frames(uint8_t nb_frame) { + tA2DP_LDAC_ENCODER_PARAMS* p_encoder_params = + &a2dp_ldac_encoder_cb.ldac_encoder_params; + uint8_t remain_nb_frame = nb_frame; + uint16_t ldac_frame_size; + uint8_t read_buffer[LDACBT_MAX_LSU * 4 /* byte/sample */ * 2 /* ch */]; + + switch (p_encoder_params->sample_rate) { + case 176400: + case 192000: + ldac_frame_size = 512; // sample/ch + break; + case 88200: + case 96000: + ldac_frame_size = 256; // sample/ch + break; + case 44100: + case 48000: + default: + ldac_frame_size = 128; // sample/ch + break; + } + + uint32_t count; + int32_t encode_count = 0; + int32_t out_frames = 0; + int written = 0; + while (nb_frame) { + BT_HDR* p_buf = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE); + + /* Init buffer */ + p_buf->offset = A2DP_LDAC_OFFSET; + p_buf->len = 0; + p_buf->layer_specific = 0; + + count = 0; + do { + /* Read PCM data */ + if (a2dp_ldac_read_feeding(read_buffer)) { + uint8_t* packet = (uint8_t*)(p_buf + 1) + p_buf->offset + p_buf->len; + if (a2dp_ldac_encoder_cb.ldac_handle == NULL) { + LOG_ERROR(LOG_TAG, "%s: invalid LDAC handle", __func__); + osi_free(p_buf); + return; + } + int result = ldac_encode_func( + a2dp_ldac_encoder_cb.ldac_handle, read_buffer, (int*)&encode_count, + packet + count, (int*)&written, (int*)&out_frames); + if (result != 0) { + int err_code = + ldac_get_error_code_func(a2dp_ldac_encoder_cb.ldac_handle); + LOG_ERROR(LOG_TAG, + "%s: LDAC encoding error: %d api_error = %d " + "handle_error = %d block_error = %d", + __func__, result, LDACBT_API_ERR(err_code), + LDACBT_HANDLE_ERR(err_code), LDACBT_BLOCK_ERR(err_code)); + osi_free(p_buf); + return; + } + count += written; + p_buf->len += written; + nb_frame--; + p_buf->layer_specific += out_frames; // added a frame to the buffer + } else { + LOG_WARN(LOG_TAG, "%s: underflow %d", __func__, nb_frame); + a2dp_ldac_encoder_cb.ldac_feeding_state.counter += + nb_frame * LDACBT_ENC_LSU * + a2dp_ldac_encoder_cb.feeding_params.channel_count * + a2dp_ldac_encoder_cb.feeding_params.bits_per_sample / 8; + + // no more pcm to read + nb_frame = 0; + } + } while ((written == 0) && nb_frame); + + if (p_buf->len) { + /* + * Timestamp of the media packet header represent the TS of the + * first frame, i.e the timestamp before including this frame. + */ + *((uint32_t*)(p_buf + 1)) = a2dp_ldac_encoder_cb.timestamp; + + a2dp_ldac_encoder_cb.timestamp += p_buf->layer_specific * ldac_frame_size; + + uint8_t done_nb_frame = remain_nb_frame - nb_frame; + remain_nb_frame = nb_frame; + if (!a2dp_ldac_encoder_cb.enqueue_callback(p_buf, done_nb_frame)) return; + } else { + osi_free(p_buf); + } + } +} + +static bool a2dp_ldac_read_feeding(uint8_t* read_buffer) { + uint32_t read_size = LDACBT_ENC_LSU * + a2dp_ldac_encoder_cb.feeding_params.channel_count * + a2dp_ldac_encoder_cb.feeding_params.bits_per_sample / 8; + + /* Read Data from UIPC channel */ + uint32_t nb_byte_read = + a2dp_ldac_encoder_cb.read_callback(read_buffer, read_size); + + if (nb_byte_read < read_size) { + if (nb_byte_read == 0) return false; + + /* Fill the unfilled part of the read buffer with silence (0) */ + memset(((uint8_t*)read_buffer) + nb_byte_read, 0, read_size - nb_byte_read); + nb_byte_read = read_size; + } + return true; +} + +void a2dp_vendor_ldac_debug_codec_dump(int fd) { + a2dp_ldac_encoder_stats_t* stats = &a2dp_ldac_encoder_cb.stats; + + dprintf(fd, "\nA2DP LDAC 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/system/stack/include/a2dp_vendor_ldac.h b/system/stack/include/a2dp_vendor_ldac.h new file mode 100644 index 0000000000000000000000000000000000000000..4bb71c4bd47ba0ef8279dd09f035dd3442d5ff17 --- /dev/null +++ b/system/stack/include/a2dp_vendor_ldac.h @@ -0,0 +1,155 @@ +/* + * 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 LDAC +// + +#ifndef A2DP_VENDOR_LDAC_H +#define A2DP_VENDOR_LDAC_H + +#include "a2dp_codec_api.h" +#include "a2dp_vendor_ldac_constants.h" +#include "avdt_api.h" + +class A2dpCodecConfigLdac : public A2dpCodecConfig { + public: + A2dpCodecConfigLdac(); + virtual ~A2dpCodecConfigLdac(); + + 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 LDAC Source +// codec. +// NOTE: only codecs that are implemented are considered valid. +// Returns true if |p_codec_info| contains information about a valid LDAC +// codec, otherwise false. +bool A2DP_IsVendorSourceCodecValidLdac(const uint8_t* p_codec_info); + +// Checks whether the codec capabilities contain a valid peer A2DP LDAC Sink +// codec. +// NOTE: only codecs that are implemented are considered valid. +// Returns true if |p_codec_info| contains information about a valid LDAC +// codec, otherwise false. +bool A2DP_IsVendorPeerSinkCodecValidLdac(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_VendorUsesRtpHeaderLdac(bool content_protection_enabled, + const uint8_t* p_codec_info); + +// Gets the A2DP LDAC codec name for a given |p_codec_info|. +const char* A2DP_VendorCodecNameLdac(const uint8_t* p_codec_info); + +// Checks whether two A2DP LDAC 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_VendorCodecTypeEqualsLdac(const uint8_t* p_codec_info_a, + const uint8_t* p_codec_info_b); + +// Checks whether two A2DP LDAC 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 LDAC, the return value is false. +bool A2DP_VendorCodecEqualsLdac(const uint8_t* p_codec_info_a, + const uint8_t* p_codec_info_b); + +// Gets the track sample rate value for the A2DP LDAC codec. +// |p_codec_info| is a pointer to the LDAC codec_info to decode. +// Returns the track sample rate on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetTrackSampleRateLdac(const uint8_t* p_codec_info); + +// Gets the bits per audio sample for the A2DP LDAC codec. +// |p_codec_info| is a pointer to the LDAC codec_info to decode. +// Returns the bits per audio sample on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetTrackBitsPerSampleLdac(const uint8_t* p_codec_info); + +// Gets the channel count for the A2DP LDAC codec. +// |p_codec_info| is a pointer to the LDAC codec_info to decode. +// Returns the channel count on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetTrackChannelCountLdac(const uint8_t* p_codec_info); + +// Gets the channel mode code for the A2DP LDAC codec. +// The actual value is codec-specific. +// |p_codec_info| is a pointer to the LDAC codec_info to decode. +// Returns the channel mode code on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetChannelModeCodeLdac(const uint8_t* p_codec_info); + +// Gets the A2DP LDAC 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_VendorGetPacketTimestampLdac(const uint8_t* p_codec_info, + const uint8_t* p_data, + uint32_t* p_timestamp); + +// Builds A2DP LDAC 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_VendorBuildCodecHeaderLdac(const uint8_t* p_codec_info, BT_HDR* p_buf, + uint16_t frames_per_packet); + +// Decodes and displays LDAC codec info (for debugging). +// |p_codec_info| is a pointer to the LDAC codec_info to decode and display. +void A2DP_VendorDumpCodecInfoLdac(const uint8_t* p_codec_info); + +// Gets the A2DP LDAC 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 LDAC encoder interface if the |p_codec_info| is valid and +// supported, otherwise NULL. +const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceLdac( + const uint8_t* p_codec_info); + +// Adjusts the A2DP LDAC 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_VendorAdjustCodecLdac(uint8_t* p_codec_info); + +// Gets the A2DP LDAC 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_VendorSourceCodecIndexLdac( + const uint8_t* p_codec_info); + +// Gets the A2DP LDAC Source codec name. +const char* A2DP_VendorCodecIndexStrLdac(void); + +// Initializes A2DP LDAC Source codec information into |tAVDT_CFG| +// configuration entry pointed by |p_cfg|. +bool A2DP_VendorInitCodecConfigLdac(tAVDT_CFG* p_cfg); + +#endif // A2DP_VENDOR_LDAC_H diff --git a/system/stack/include/a2dp_vendor_ldac_constants.h b/system/stack/include/a2dp_vendor_ldac_constants.h new file mode 100644 index 0000000000000000000000000000000000000000..7d041fb1457317a1aad09bfc7ad3ee793c03cc08 --- /dev/null +++ b/system/stack/include/a2dp_vendor_ldac_constants.h @@ -0,0 +1,53 @@ +/* + * 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 LDAC codec +// + +#ifndef A2DP_VENDOR_LDAC_CONSTANTS_H +#define A2DP_VENDOR_LDAC_CONSTANTS_H + +// Length of the LDAC Media Payload header +#define A2DP_LDAC_MPL_HDR_LEN 1 + +// LDAC Media Payload Header +#define A2DP_LDAC_HDR_F_MSK 0x80 +#define A2DP_LDAC_HDR_S_MSK 0x40 +#define A2DP_LDAC_HDR_L_MSK 0x20 +#define A2DP_LDAC_HDR_NUM_MSK 0x0F + +// LDAC codec specific settings +#define A2DP_LDAC_CODEC_LEN 10 +// [Octet 0-3] Vendor ID +#define A2DP_LDAC_VENDOR_ID 0x0000012D +// [Octet 4-5] Vendor Specific Codec ID +#define A2DP_LDAC_CODEC_ID 0x00AA +// [Octet 6], [Bits 0-5] Sampling Frequency +#define A2DP_LDAC_SAMPLING_FREQ_MASK 0x3F +#define A2DP_LDAC_SAMPLING_FREQ_44100 0x20 +#define A2DP_LDAC_SAMPLING_FREQ_48000 0x10 +#define A2DP_LDAC_SAMPLING_FREQ_88200 0x08 +#define A2DP_LDAC_SAMPLING_FREQ_96000 0x04 +#define A2DP_LDAC_SAMPLING_FREQ_176400 0x02 +#define A2DP_LDAC_SAMPLING_FREQ_192000 0x01 +// [Octet 7], [Bits 0-2] Channel Mode +#define A2DP_LDAC_CHANNEL_MODE_MASK 0x07 +#define A2DP_LDAC_CHANNEL_MODE_MONO 0x04 +#define A2DP_LDAC_CHANNEL_MODE_DUAL 0x02 +#define A2DP_LDAC_CHANNEL_MODE_STEREO 0x01 + +#endif // A2DP_VENDOR_LDAC_CONSTANTS_H diff --git a/system/stack/include/a2dp_vendor_ldac_encoder.h b/system/stack/include/a2dp_vendor_ldac_encoder.h new file mode 100644 index 0000000000000000000000000000000000000000..393ba1b174a15820205e7743d2cef77ffad8ac17 --- /dev/null +++ b/system/stack/include/a2dp_vendor_ldac_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 LDAC Encoder +// + +#ifndef A2DP_VENDOR_LDAC_ENCODER_H +#define A2DP_VENDOR_LDAC_ENCODER_H + +#include "a2dp_codec_api.h" +#include "osi/include/time.h" + +// Loads the A2DP LDAC encoder. +// Return true on success, otherwise false. +bool A2DP_VendorLoadEncoderLdac(void); + +// Unloads the A2DP LDAC encoder. +void A2DP_VendorUnloadEncoderLdac(void); + +// Initialize the A2DP LDAC 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_ldac_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 LDAC encoder. +void a2dp_vendor_ldac_encoder_cleanup(void); + +// Initialize the feeding for the A2DP LDAC encoder. +// The feeding initialization parameters are in |p_feeding_params|. +void a2dp_vendor_ldac_feeding_init( + const tA2DP_FEEDING_PARAMS* p_feeding_params); + +// Reset the feeding for the A2DP LDAC encoder. +void a2dp_vendor_ldac_feeding_reset(void); + +// Flush the feeding for the A2DP LDAC encoder. +void a2dp_vendor_ldac_feeding_flush(void); + +// Get the A2DP LDAC encoder interval (in milliseconds). +period_ms_t a2dp_vendor_ldac_get_encoder_interval_ms(void); + +// Prepare and send A2DP LDAC encoded frames. +// |timestamp_us| is the current timestamp (in microseconds). +void a2dp_vendor_ldac_send_frames(uint64_t timestamp_us); + +// Dump LDAC codec-related statistics. +// |fd| is the file descriptor to use to dump the statistics information +// in user-friendly test format. +void a2dp_vendor_ldac_debug_codec_dump(int fd); + +#endif // A2DP_VENDOR_LDAC_ENCODER_H