// Get the audio channel count for the A2DP Sink module.
tA2DP_CHANNEL_COUNT btif_a2dp_sink_get_channel_count(void);
+// Get the audio bits per sample for the A2DP Sink module.
+tA2DP_BITS_PER_SAMPLE btif_a2dp_sink_get_bits_per_sample(void);
+
// Update the decoder for the A2DP Sink module.
// |p_codec_info| contains the new codec information.
void btif_a2dp_sink_update_decoder(const uint8_t* p_codec_info);
* should eventually be
* deleted using BtifAvrcpAudioTrackDelete (see below).
*/
-void* BtifAvrcpAudioTrackCreate(int trackFreq, int channelType);
+void* BtifAvrcpAudioTrackCreate(int trackFreq, int bits_per_sample,
+ int channelType);
/**
* Starts the audio track.
bool rx_flush; /* discards any incoming data when true */
alarm_t* decode_alarm;
tA2DP_SAMPLE_RATE sample_rate;
+ tA2DP_BITS_PER_SAMPLE bits_per_sample;
tA2DP_CHANNEL_COUNT channel_count;
btif_a2dp_sink_focus_state_t rx_focus_state; /* audio focus state */
void* audio_track;
return btif_a2dp_sink_cb.sample_rate;
}
+tA2DP_BITS_PER_SAMPLE btif_a2dp_sink_get_bits_per_sample() {
+ LockGuard lock(g_mutex);
+ return btif_a2dp_sink_cb.bits_per_sample;
+}
+
tA2DP_CHANNEL_COUNT btif_a2dp_sink_get_channel_count() {
LockGuard lock(g_mutex);
return btif_a2dp_sink_cb.channel_count;
LOG_ERROR(LOG_TAG, "%s: cannot get the track frequency", __func__);
return;
}
+ int bits_per_sample = A2DP_GetTrackBitsPerSample(p_buf->codec_info);
+ if (bits_per_sample == -1) {
+ LOG_ERROR(LOG_TAG, "%s: cannot get the bits per sample", __func__);
+ return;
+ }
int channel_count = A2DP_GetTrackChannelCount(p_buf->codec_info);
if (channel_count == -1) {
LOG_ERROR(LOG_TAG, "%s: cannot get the channel count", __func__);
return;
}
btif_a2dp_sink_cb.sample_rate = sample_rate;
+ btif_a2dp_sink_cb.bits_per_sample = bits_per_sample;
btif_a2dp_sink_cb.channel_count = channel_count;
btif_a2dp_sink_cb.rx_flush = false;
APPL_TRACE_DEBUG("%s: create audio track", __func__);
btif_a2dp_sink_cb.audio_track =
#ifndef OS_GENERIC
- BtifAvrcpAudioTrackCreate(sample_rate, channel_type);
+ BtifAvrcpAudioTrackCreate(sample_rate, bits_per_sample, channel_type);
#else
NULL;
#endif
char outputFilename[50] = "/data/misc/bluedroid/output_sample.pcm";
#endif
-void* BtifAvrcpAudioTrackCreate(int trackFreq, int channelType) {
- LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btCreateTrack freq %d channel %d ",
- __func__, trackFreq, channelType);
+void* BtifAvrcpAudioTrackCreate(int trackFreq, int bits_per_sample,
+ int channelType) {
+ audio_format_t format;
+ switch (bits_per_sample) {
+ default:
+ case 16:
+ format = AUDIO_FORMAT_PCM_16_BIT;
+ break;
+ case 24:
+ format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+ break;
+ case 32:
+ format = AUDIO_FORMAT_PCM_32_BIT;
+ break;
+ }
+ LOG_VERBOSE(LOG_TAG,
+ "%s Track.cpp: btCreateTrack freq %d format 0x%x channel %d ",
+ __func__, trackFreq, format, channelType);
sp<android::AudioTrack> track = new android::AudioTrack(
- AUDIO_STREAM_MUSIC, trackFreq, AUDIO_FORMAT_PCM_16_BIT, channelType,
+ AUDIO_STREAM_MUSIC, trackFreq, format, channelType,
(size_t)0 /*frameCount*/, (audio_output_flags_t)AUDIO_OUTPUT_FLAG_FAST,
NULL /*callback_t*/, NULL /*void* user*/, 0 /*notificationFrames*/,
AUDIO_SESSION_ALLOCATE, android::AudioTrack::TRANSFER_SYNC);
// Add an entry for each sink codec here
BTAV_A2DP_CODEC_INDEX_SINK_SBC = BTAV_A2DP_CODEC_INDEX_SINK_MIN,
BTAV_A2DP_CODEC_INDEX_SINK_AAC,
+ BTAV_A2DP_CODEC_INDEX_SINK_LDAC,
BTAV_A2DP_CODEC_INDEX_SINK_MAX,
case BTAV_A2DP_CODEC_INDEX_SINK_AAC:
codec_name_str = "AAC (Sink)";
break;
+ case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
+ codec_name_str = "LDAC (Sink)";
+ break;
case BTAV_A2DP_CODEC_INDEX_MAX:
codec_name_str = "Unknown(CODEC_INDEX_MAX)";
break;
"a2dp/a2dp_vendor_aptx_hd_encoder.cc",
"a2dp/a2dp_vendor_ldac.cc",
"a2dp/a2dp_vendor_ldac_abr.cc",
+ "a2dp/a2dp_vendor_ldac_decoder.cc",
"a2dp/a2dp_vendor_ldac_encoder.cc",
"avct/avct_api.cc",
"avct/avct_bcb_act.cc",
return -1;
}
+int A2DP_GetTrackBitsPerSampleAac(const uint8_t* p_codec_info) {
+ tA2DP_AAC_CIE aac_cie;
+
+ // Check whether the codec info contains valid data
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ // NOTE: The bits per sample never changes for AAC
+ return 16;
+}
+
int A2DP_GetTrackChannelCountAac(const uint8_t* p_codec_info) {
tA2DP_AAC_CIE aac_cie;
sizeof(ota_codec_peer_config_));
return false;
}
+
bool A2dpCodecConfigAacBase::setPeerCodecCapabilities(
const uint8_t* p_peer_codec_capabilities) {
std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
codec_config = new A2dpCodecConfigAptxHd(codec_priority);
break;
case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
- codec_config = new A2dpCodecConfigLdac(codec_priority);
+ codec_config = new A2dpCodecConfigLdacSource(codec_priority);
+ break;
+ case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
+ codec_config = new A2dpCodecConfigLdacSink(codec_priority);
break;
case BTAV_A2DP_CODEC_INDEX_MAX:
break;
return -1;
}
+int A2DP_GetTrackBitsPerSample(const uint8_t* p_codec_info) {
+ tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+ LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+ switch (codec_type) {
+ case A2DP_MEDIA_CT_SBC:
+ return A2DP_GetTrackBitsPerSampleSbc(p_codec_info);
+ case A2DP_MEDIA_CT_AAC:
+ return A2DP_GetTrackBitsPerSampleAac(p_codec_info);
+ case A2DP_MEDIA_CT_NON_A2DP:
+ return A2DP_VendorGetTrackBitsPerSample(p_codec_info);
+ default:
+ break;
+ }
+
+ LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+ return -1;
+}
+
int A2DP_GetTrackChannelCount(const uint8_t* p_codec_info) {
tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
return -1;
}
+int A2DP_GetTrackBitsPerSampleSbc(const uint8_t* p_codec_info) {
+ tA2DP_SBC_CIE sbc_cie;
+
+ tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
+ if (a2dp_status != A2DP_SUCCESS) {
+ LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+ a2dp_status);
+ return -1;
+ }
+
+ // NOTE: The bits per sample never changes for SBC
+ return 16;
+}
+
int A2DP_GetTrackChannelCountSbc(const uint8_t* p_codec_info) {
tA2DP_SBC_CIE sbc_cie;
A2dpCodecConfigSbcSource::A2dpCodecConfigSbcSource(
btav_a2dp_codec_priority_t codec_priority)
- : A2dpCodecConfigSbcBase(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC, "SBC",
- codec_priority, true) {
+ : A2dpCodecConfigSbcBase(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,
+ A2DP_CodecIndexStrSbc(), codec_priority, true) {
// Compute the local capability
if (a2dp_sbc_source_caps.samp_freq & A2DP_SBC_IE_SAMP_FREQ_44) {
codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
A2dpCodecConfigSbcSink::A2dpCodecConfigSbcSink(
btav_a2dp_codec_priority_t codec_priority)
- : A2dpCodecConfigSbcBase(BTAV_A2DP_CODEC_INDEX_SINK_SBC, "SBC(Sink)",
- codec_priority, false) {}
+ : A2dpCodecConfigSbcBase(BTAV_A2DP_CODEC_INDEX_SINK_SBC,
+ A2DP_CodecIndexStrSbcSink(), codec_priority,
+ false) {}
A2dpCodecConfigSbcSink::~A2dpCodecConfigSbcSink() {}
return false;
}
-bool A2DP_IsVendorSinkCodecValid(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_IsVendorSinkCodecValid(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
// Add checks based on <vendor_id, codec_id>
// NOTE: Should be done only for local Sink codecs.
+ // Check for LDAC
+ if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) {
+ return A2DP_IsVendorSinkCodecValidLdac(p_codec_info);
+ }
+
return false;
}
-bool A2DP_IsVendorPeerSourceCodecValid(
- 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_IsVendorPeerSourceCodecValid(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
// Add checks based on <vendor_id, codec_id>
// NOTE: Should be done only for local Sink codecs.
+ // Check for LDAC
+ if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) {
+ return A2DP_IsVendorPeerSourceCodecValidLdac(p_codec_info);
+ }
+
return false;
}
return false;
}
-bool A2DP_IsVendorSinkCodecSupported(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_IsVendorSinkCodecSupported(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
// Add checks based on <vendor_id, codec_id>
// NOTE: Should be done only for local Sink codecs.
+ // Check for LDAC
+ if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) {
+ return A2DP_IsVendorSinkCodecSupportedLdac(p_codec_info);
+ }
+
return false;
}
-bool A2DP_IsVendorPeerSourceCodecSupported(
- 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_IsVendorPeerSourceCodecSupported(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
// Add checks based on <vendor_id, codec_id> and peer codec capabilities
// NOTE: Should be done only for local Sink codecs.
+ // Check for LDAC
+ if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) {
+ return A2DP_IsPeerSourceCodecSupportedLdac(p_codec_info);
+ }
+
return false;
}
return true;
}
-const char* A2DP_VendorCodecName(UNUSED_ATTR const uint8_t* p_codec_info) {
+const char* A2DP_VendorCodecName(const uint8_t* p_codec_info) {
uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
return -1;
}
+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);
+ }
+
+ // 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;
+}
+
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);
return -1;
}
-int A2DP_VendorGetSinkTrackChannelType(
- 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_VendorGetSinkTrackChannelType(const uint8_t* p_codec_info) {
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
// Add checks based on <vendor_id, codec_id>
// NOTE: Should be done only for local Sink codecs.
+ // Check for LDAC
+ if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) {
+ return A2DP_VendorGetSinkTrackChannelTypeLdac(p_codec_info);
+ }
+
return -1;
}
const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterface(
const uint8_t* p_codec_info) {
- // We do not support vendor codecs for decoding right now.
+ uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+ uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+ // Add checks based on <vendor_id, codec_id>
+ // NOTE: Should be done only for local Sink codecs.
+
+ // Check for LDAC
+ if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) {
+ return A2DP_VendorGetDecoderInterfaceLdac(p_codec_info);
+ }
+
return NULL;
}
}
btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndex(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);
// Add checks based on <vendor_id, codec_id>
+ // NOTE: Should be done only for local Sink codecs.
+
+ // Check for LDAC
+ if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) {
+ return A2DP_VendorSinkCodecIndexLdac(p_codec_info);
+ }
return BTAV_A2DP_CODEC_INDEX_MAX;
}
return A2DP_VendorCodecIndexStrAptxHd();
case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
return A2DP_VendorCodecIndexStrLdac();
+ case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
+ return A2DP_VendorCodecIndexStrLdacSink();
// Add a switch statement for each vendor-specific codec
case BTAV_A2DP_CODEC_INDEX_MAX:
break;
return A2DP_VendorInitCodecConfigAptxHd(p_cfg);
case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
return A2DP_VendorInitCodecConfigLdac(p_cfg);
+ case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
+ return A2DP_VendorInitCodecConfigLdacSink(p_cfg);
// Add a switch statement for each vendor-specific codec
case BTAV_A2DP_CODEC_INDEX_MAX:
break;
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;
+ }
+
+ // NOTE: The bits per sample never changes for aptX
+ return 16;
+}
+
int A2DP_VendorGetTrackChannelCountAptx(const uint8_t* p_codec_info) {
tA2DP_APTX_CIE aptx_cie;
A2dpCodecConfigAptx::A2dpCodecConfigAptx(
btav_a2dp_codec_priority_t codec_priority)
- : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, "aptX",
- codec_priority) {
+ : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX,
+ A2DP_VendorCodecIndexStrAptx(), codec_priority) {
// Compute the local capability
if (a2dp_aptx_source_caps.sampleRate & A2DP_APTX_SAMPLERATE_44100) {
codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
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;
+ }
+
+ // NOTE: The bits per sample never changes for aptX-HD
+ return 24;
+}
+
int A2DP_VendorGetTrackChannelCountAptxHd(const uint8_t* p_codec_info) {
tA2DP_APTX_HD_CIE aptx_hd_cie;
A2dpCodecConfigAptxHd::A2dpCodecConfigAptxHd(
btav_a2dp_codec_priority_t codec_priority)
- : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD, "aptX-HD",
- codec_priority) {
+ : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD,
+ A2DP_VendorCodecIndexStrAptxHd(), codec_priority) {
// Compute the local capability
if (a2dp_aptx_hd_source_caps.sampleRate & A2DP_APTX_HD_SAMPLERATE_44100) {
codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
#include <base/logging.h>
#include "a2dp_vendor.h"
+#include "a2dp_vendor_ldac_decoder.h"
#include "a2dp_vendor_ldac_encoder.h"
#include "bt_utils.h"
#include "btif_av_co.h"
(BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 | BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24 |
BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32)};
+/* LDAC Sink codec capabilities */
+static const tA2DP_LDAC_CIE a2dp_ldac_sink_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_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_vendor_ldac_send_frames,
a2dp_vendor_ldac_set_transmit_queue_length};
+static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_ldac = {
+ a2dp_vendor_ldac_decoder_init,
+ a2dp_vendor_ldac_decoder_cleanup,
+ a2dp_vendor_ldac_decoder_decode_packet,
+};
+
UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityLdac(
const tA2DP_LDAC_CIE* p_cap, const uint8_t* p_codec_info,
bool is_peer_codec_info);
(A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
}
+bool A2DP_IsVendorSinkCodecValidLdac(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_IsVendorPeerSourceCodecValidLdac(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;
(A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
}
+bool A2DP_IsVendorSinkCodecSupportedLdac(const uint8_t* p_codec_info) {
+ return A2DP_CodecInfoMatchesCapabilityLdac(&a2dp_ldac_sink_caps, p_codec_info,
+ false) == A2DP_SUCCESS;
+}
+bool A2DP_IsPeerSourceCodecSupportedLdac(const uint8_t* p_codec_info) {
+ return A2DP_CodecInfoMatchesCapabilityLdac(&a2dp_ldac_sink_caps, 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.
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;
+ }
+
+#if 1
+ return 32;
+#else
+ // TODO : Implement proc to care about bit per sample in A2DP_ParseInfoLdac()
+
+ switch (ldac_cie.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:
+ return -1;
+ }
+#endif
+}
+
int A2DP_VendorGetTrackChannelCountLdac(const uint8_t* p_codec_info) {
tA2DP_LDAC_CIE ldac_cie;
return -1;
}
+int A2DP_VendorGetSinkTrackChannelTypeLdac(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 3;
+ case A2DP_LDAC_CHANNEL_MODE_STEREO:
+ return 3;
+ }
+
+ return -1;
+}
+
int A2DP_VendorGetChannelModeCodeLdac(const uint8_t* p_codec_info) {
tA2DP_LDAC_CIE ldac_cie;
return &a2dp_encoder_interface_ldac;
}
+const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceLdac(
+ const uint8_t* p_codec_info) {
+ if (!A2DP_IsVendorSinkCodecValidLdac(p_codec_info)) return NULL;
+
+ return &a2dp_decoder_interface_ldac;
+}
+
bool A2DP_VendorAdjustCodecLdac(uint8_t* p_codec_info) {
tA2DP_LDAC_CIE cfg_cie;
return BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC;
}
+btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndexLdac(
+ UNUSED_ATTR const uint8_t* p_codec_info) {
+ return BTAV_A2DP_CODEC_INDEX_SINK_LDAC;
+}
+
const char* A2DP_VendorCodecIndexStrLdac(void) { return "LDAC"; }
+const char* A2DP_VendorCodecIndexStrLdacSink(void) { return "LDAC SINK"; }
+
bool A2DP_VendorInitCodecConfigLdac(AvdtpSepConfig* p_cfg) {
if (A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_ldac_source_caps,
p_cfg->codec_info) != A2DP_SUCCESS) {
return true;
}
+bool A2DP_VendorInitCodecConfigLdacSink(AvdtpSepConfig* p_cfg) {
+ return A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_ldac_sink_caps,
+ p_cfg->codec_info) == A2DP_SUCCESS;
+}
+
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)
}
}
-A2dpCodecConfigLdac::A2dpCodecConfigLdac(
+A2dpCodecConfigLdacSource::A2dpCodecConfigLdacSource(
btav_a2dp_codec_priority_t codec_priority)
- : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, "LDAC",
- codec_priority) {
+ : A2dpCodecConfigLdacBase(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC,
+ A2DP_VendorCodecIndexStrLdac(), codec_priority,
+ true) {
// Compute the local capability
if (a2dp_ldac_source_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_44100) {
codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
}
}
-A2dpCodecConfigLdac::~A2dpCodecConfigLdac() {}
+A2dpCodecConfigLdacSource::~A2dpCodecConfigLdacSource() {}
-bool A2dpCodecConfigLdac::init() {
+bool A2dpCodecConfigLdacSource::init() {
if (!isValid()) return false;
// Load the encoder
return true;
}
-bool A2dpCodecConfigLdac::useRtpHeaderMarkerBit() const { return false; }
+bool A2dpCodecConfigLdacSource::useRtpHeaderMarkerBit() const { return false; }
//
// Selects the best sample rate from |sampleRate|.
return false;
}
-bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info,
- bool is_capability,
- uint8_t* p_result_codec_config) {
+bool A2dpCodecConfigLdacBase::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 peer_info_cie;
tA2DP_LDAC_CIE result_config_cie;
uint8_t channelMode;
uint8_t sampleRate;
btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+ const tA2DP_LDAC_CIE* p_a2dp_ldac_caps =
+ (is_source_) ? &a2dp_ldac_source_caps : &a2dp_ldac_sink_caps;
// Save the internal state
btav_a2dp_codec_config_t saved_codec_config = codec_config_;
// Build the preferred configuration
//
memset(&result_config_cie, 0, sizeof(result_config_cie));
- result_config_cie.vendorId = a2dp_ldac_source_caps.vendorId;
- result_config_cie.codecId = a2dp_ldac_source_caps.codecId;
+ result_config_cie.vendorId = p_a2dp_ldac_caps->vendorId;
+ result_config_cie.codecId = p_a2dp_ldac_caps->codecId;
//
// Select the sample frequency
//
- sampleRate = a2dp_ldac_source_caps.sampleRate & peer_info_cie.sampleRate;
+ sampleRate = p_a2dp_ldac_caps->sampleRate & peer_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:
LOG_ERROR(LOG_TAG,
"%s: cannot match sample frequency: local caps = 0x%x "
"peer info = 0x%x",
- __func__, a2dp_ldac_source_caps.sampleRate,
- peer_info_cie.sampleRate);
+ __func__, p_a2dp_ldac_caps->sampleRate, peer_info_cie.sampleRate);
goto fail;
}
//
// NOTE: this information is NOT included in the LDAC A2DP codec description
// that is sent OTA.
- bits_per_sample = a2dp_ldac_source_caps.bits_per_sample;
+ bits_per_sample = p_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:
do {
// Compute the selectable capability
codec_selectable_capability_.bits_per_sample =
- a2dp_ldac_source_caps.bits_per_sample;
+ p_a2dp_ldac_caps->bits_per_sample;
if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE)
break;
// No user preference - the the codec audio config
if (select_audio_bits_per_sample(&codec_audio_config_,
- a2dp_ldac_source_caps.bits_per_sample,
+ p_a2dp_ldac_caps->bits_per_sample,
&result_config_cie, &codec_config_)) {
break;
}
}
// No user preference - use the best match
- if (select_best_bits_per_sample(a2dp_ldac_source_caps.bits_per_sample,
+ if (select_best_bits_per_sample(p_a2dp_ldac_caps->bits_per_sample,
&result_config_cie, &codec_config_)) {
break;
}
//
// Select the channel mode
//
- channelMode = a2dp_ldac_source_caps.channelMode & peer_info_cie.channelMode;
+ channelMode = p_a2dp_ldac_caps->channelMode & peer_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:
LOG_ERROR(LOG_TAG,
"%s: cannot match channel mode: local caps = 0x%x "
"peer info = 0x%x",
- __func__, a2dp_ldac_source_caps.channelMode,
+ __func__, p_a2dp_ldac_caps->channelMode,
peer_info_cie.channelMode);
goto fail;
}
return false;
}
-bool A2dpCodecConfigLdac::setPeerCodecCapabilities(
+bool A2dpCodecConfigLdacBase::setPeerCodecCapabilities(
const uint8_t* p_peer_codec_capabilities) {
std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
tA2DP_LDAC_CIE peer_info_cie;
uint8_t channelMode;
uint8_t sampleRate;
+ const tA2DP_LDAC_CIE* p_a2dp_ldac_caps =
+ (is_source_) ? &a2dp_ldac_source_caps : &a2dp_ldac_sink_caps;
// Save the internal state
btav_a2dp_codec_config_t saved_codec_selectable_capability =
}
// Compute the selectable capability - sample rate
- sampleRate = a2dp_ldac_source_caps.sampleRate & peer_info_cie.sampleRate;
+ sampleRate = p_a2dp_ldac_caps->sampleRate & peer_info_cie.sampleRate;
if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_44100) {
codec_selectable_capability_.sample_rate |=
BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
// Compute the selectable capability - bits per sample
codec_selectable_capability_.bits_per_sample =
- a2dp_ldac_source_caps.bits_per_sample;
+ p_a2dp_ldac_caps->bits_per_sample;
// Compute the selectable capability - channel mode
- channelMode = a2dp_ldac_source_caps.channelMode & peer_info_cie.channelMode;
+ channelMode = p_a2dp_ldac_caps->channelMode & peer_info_cie.channelMode;
if (channelMode & A2DP_LDAC_CHANNEL_MODE_MONO) {
codec_selectable_capability_.channel_mode |=
BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
sizeof(ota_codec_peer_capability_));
return false;
}
+
+A2dpCodecConfigLdacSink::A2dpCodecConfigLdacSink(
+ btav_a2dp_codec_priority_t codec_priority)
+ : A2dpCodecConfigLdacBase(BTAV_A2DP_CODEC_INDEX_SINK_LDAC,
+ A2DP_VendorCodecIndexStrLdacSink(),
+ codec_priority, false) {}
+
+A2dpCodecConfigLdacSink::~A2dpCodecConfigLdacSink() {}
+
+bool A2dpCodecConfigLdacSink::init() {
+ if (!isValid()) return false;
+
+ // Load the decoder
+ if (!A2DP_VendorLoadDecoderLdac()) {
+ LOG_ERROR(LOG_TAG, "%s: cannot load the decoder", __func__);
+ return false;
+ }
+
+ return true;
+}
+
+uint64_t A2dpCodecConfigLdacSink::encoderIntervalMs() const {
+ // TODO: This method applies only to Source codecs
+ return 0;
+}
+
+int A2dpCodecConfigLdacSink::getEffectiveMtu() const {
+ // TODO: This method applies only to Source codecs
+ return 0;
+}
+
+bool A2dpCodecConfigLdacSink::useRtpHeaderMarkerBit() const {
+ // TODO: This method applies only to Source codecs
+ return false;
+}
+
+bool A2dpCodecConfigLdacSink::updateEncoderUserConfig(
+ UNUSED_ATTR const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+ UNUSED_ATTR bool* p_restart_input, UNUSED_ATTR bool* p_restart_output,
+ UNUSED_ATTR bool* p_config_updated) {
+ // TODO: This method applies only to Source codecs
+ return false;
+}
--- /dev/null
+/*
+ * Copyright 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_decoder"
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include "a2dp_vendor_ldac_decoder.h"
+
+#ifndef OS_GENERIC
+#include <cutils/trace.h>
+#endif
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <ldacBT.h>
+
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_ldac.h"
+#include "bt_common.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+//
+// Decoder for LDAC Source Codec
+//
+
+//
+// The LDAC decoder shared library, and the functions to use
+//
+static const char* LDAC_DECODER_LIB_NAME = "libldacBT_dec.so";
+static void* ldac_decoder_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_DECODE_NAME = "ldacBT_init_handle_decode";
+typedef int (*tLDAC_INIT_HANDLE_DECODE)(HANDLE_LDAC_BT hLdacParam, int cm,
+ int sf, int var0, int var1, int var2);
+
+static const char* LDAC_DECODE_NAME = "ldacBT_decode";
+typedef int (*tLDAC_DECODE)(HANDLE_LDAC_BT hLdacBt, unsigned char* p_bs,
+ unsigned char* p_pcm, LDACBT_SMPL_FMT_T fmt,
+ int bs_bytes, int* used_bytes, int* wrote_bytes);
+
+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_DECODE ldac_init_handle_decode_func;
+static tLDAC_DECODE ldac_decode_func;
+static tLDAC_GET_ERROR_CODE ldac_get_error_code_func;
+
+// 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 pcm_wlength;
+ LDACBT_SMPL_FMT_T pcm_fmt;
+} tA2DP_LDAC_DECODER_PARAMS;
+
+typedef struct {
+ 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
+ unsigned char* decode_buf;
+ decoded_data_callback_t decode_callback;
+} tA2DP_LDAC_DECODER_CB;
+
+static tA2DP_LDAC_DECODER_CB a2dp_ldac_decoder_cb;
+
+static void* load_func(const char* func_name) {
+ void* func_ptr = dlsym(ldac_decoder_lib_handle, func_name);
+ if (func_ptr == NULL) {
+ LOG_ERROR(LOG_TAG,
+ "%s: cannot find function '%s' in the decoder library: %s",
+ __func__, func_name, dlerror());
+ A2DP_VendorUnloadDecoderLdac();
+ return NULL;
+ }
+ return func_ptr;
+}
+
+bool A2DP_VendorLoadDecoderLdac(void) {
+ if (ldac_decoder_lib_handle != NULL) return true; // Already loaded
+
+ // Initialize the control block
+ memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
+
+ // Open the decoder library
+ ldac_decoder_lib_handle = dlopen(LDAC_DECODER_LIB_NAME, RTLD_NOW);
+ if (ldac_decoder_lib_handle == NULL) {
+ LOG_ERROR(LOG_TAG, "%s: cannot open LDAC decoder library %s: %s", __func__,
+ LDAC_DECODER_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_decode_func =
+ (tLDAC_INIT_HANDLE_DECODE)load_func(LDAC_INIT_HANDLE_DECODE_NAME);
+ if (ldac_init_handle_decode_func == NULL) return false;
+ ldac_decode_func = (tLDAC_DECODE)load_func(LDAC_DECODE_NAME);
+ if (ldac_decode_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_VendorUnloadDecoderLdac(void) {
+ // Cleanup any LDAC-related state
+ if (a2dp_ldac_decoder_cb.has_ldac_handle && ldac_free_handle_func != NULL)
+ ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
+ memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_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_decode_func = NULL;
+ ldac_decode_func = NULL;
+ ldac_get_error_code_func = NULL;
+
+ if (ldac_decoder_lib_handle != NULL) {
+ dlclose(ldac_decoder_lib_handle);
+ ldac_decoder_lib_handle = NULL;
+ }
+}
+
+bool a2dp_vendor_ldac_decoder_init(decoded_data_callback_t decode_callback) {
+ if (a2dp_ldac_decoder_cb.has_ldac_handle)
+ ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
+ memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
+
+ a2dp_vendor_ldac_decoder_cleanup();
+
+ a2dp_ldac_decoder_cb.ldac_handle = ldac_get_handle_func();
+ a2dp_ldac_decoder_cb.has_ldac_handle = true;
+ a2dp_ldac_decoder_cb.decode_buf = static_cast<unsigned char*>(
+ osi_malloc(sizeof(a2dp_ldac_decoder_cb.decode_buf[0]) * LDACBT_MAX_LSU *
+ LDAC_PRCNCH * sizeof(int)));
+ a2dp_ldac_decoder_cb.decode_callback = decode_callback;
+
+ // initialize
+ ldac_init_handle_decode_func(a2dp_ldac_decoder_cb.ldac_handle,
+ LDACBT_CHANNEL_MODE_STEREO, 96000, 0, 0, 0);
+ return true;
+}
+
+void a2dp_vendor_ldac_decoder_cleanup(void) {
+ if (a2dp_ldac_decoder_cb.has_ldac_handle)
+ ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
+ memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
+}
+
+bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf) {
+ unsigned char* pBuffer =
+ reinterpret_cast<unsigned char*>(p_buf->data + p_buf->offset);
+ // unsigned int bufferSize = p_buf->len;
+ unsigned int bytesValid = p_buf->len;
+ int err;
+ LDACBT_SMPL_FMT_T fmt;
+ int bs_bytes, used_bytes, wrote_bytes, frame_number;
+
+ fmt = LDACBT_SMPL_FMT_S32;
+ frame_number = (int)pBuffer[0];
+ pBuffer++;
+ bs_bytes = (int)bytesValid - 1;
+ bytesValid -= 1;
+ LOG_VERBOSE(LOG_TAG, "%s:INPUT size : %d, frame : %d", __func__, bs_bytes,
+ frame_number);
+
+ while (bytesValid > 0) {
+#if 0
+ err = ldacDecoder_Fill(a2dp_ldac_decoder_cb.ldac_handle,
+ &pBuffer, &bufferSize, &bytesValid);
+ if (err != LDACBT_ERR_NONE) {
+ LOG_ERROR(LOG_TAG, "%s: ldacDecoder_Fill failed: 0x%x", __func__,
+ static_cast<unsigned>(err));
+ return false;
+ }
+#endif
+ while (true) {
+ // Todo : implement LDAC Buffer Control Operation instead of
+ // ldac_decode_func().
+ err = ldac_decode_func(a2dp_ldac_decoder_cb.ldac_handle, pBuffer,
+ a2dp_ldac_decoder_cb.decode_buf, fmt, bs_bytes,
+ &used_bytes, &wrote_bytes);
+ // if (err == LDAC_DEC_NOT_ENOUGH_FRAMES) {
+ // break;
+ // }
+ if (LDACBT_ERROR(err)) {
+ err = ldac_get_error_code_func(a2dp_ldac_decoder_cb.ldac_handle);
+ LOG_ERROR(LOG_TAG, "%s: ldacDecoder_DecodeFrame failed: %d:%d:%d",
+ __func__, LDACBT_API_ERR(err), LDACBT_HANDLE_ERR(err),
+ LDACBT_BLOCK_ERR(err));
+ if (LDACBT_FATAL(err)) {
+ break;
+ }
+ }
+
+ if (wrote_bytes > 0) {
+ size_t frame_len = (size_t)wrote_bytes;
+ a2dp_ldac_decoder_cb.decode_callback(
+ reinterpret_cast<uint8_t*>(a2dp_ldac_decoder_cb.decode_buf),
+ frame_len);
+ }
+ pBuffer += used_bytes;
+ bs_bytes -= used_bytes;
+ if (bs_bytes <= 1) {
+ bytesValid = 0;
+ break;
+ }
+ }
+ }
+
+ return true;
+}
&restart_output, &config_updated);
}
-bool A2dpCodecConfigLdac::updateEncoderUserConfig(
+bool A2dpCodecConfigLdacSource::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.TxQueueLength = transmit_queue_length;
}
-uint64_t A2dpCodecConfigLdac::encoderIntervalMs() const {
+uint64_t A2dpCodecConfigLdacSource::encoderIntervalMs() const {
return a2dp_vendor_ldac_get_encoder_interval_ms();
}
-int A2dpCodecConfigLdac::getEffectiveMtu() const {
+int A2dpCodecConfigLdacSource::getEffectiveMtu() const {
return a2dp_ldac_encoder_cb.TxAaMtuSize;
}
-void A2dpCodecConfigLdac::debug_codec_dump(int fd) {
+void A2dpCodecConfigLdacSource::debug_codec_dump(int fd) {
a2dp_ldac_encoder_stats_t* stats = &a2dp_ldac_encoder_cb.stats;
tA2DP_LDAC_ENCODER_PARAMS* p_encoder_params =
&a2dp_ldac_encoder_cb.ldac_encoder_params;
// contains invalid codec information.
int A2DP_GetTrackSampleRateAac(const uint8_t* p_codec_info);
+// Gets the track bits per sample value for the A2DP AAC codec.
+// |p_codec_info| is a pointer to the AAC codec_info to decode.
+// Returns the track bits per sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackBitsPerSampleAac(const uint8_t* p_codec_info);
+
// Gets the channel count for the A2DP AAC codec.
// |p_codec_info| is a pointer to the AAC codec_info to decode.
// Returns the channel count on success, or -1 if |p_codec_info|
// contains invalid codec information.
int A2DP_GetTrackSampleRate(const uint8_t* p_codec_info);
+// Gets the track bits per sample value for the A2DP codec.
+// |p_codec_info| is a pointer to the codec_info to decode.
+// Returns the track bits per sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackBitsPerSample(const uint8_t* p_codec_info);
+
// Gets the channel count for the A2DP codec.
// |p_codec_info| is a pointer to the codec_info to decode.
// Returns the channel count on success, or -1 if |p_codec_info|
// contains invalid codec information.
int A2DP_GetTrackSampleRateSbc(const uint8_t* p_codec_info);
+// Gets the track bits per sample value for the A2DP SBC codec.
+// |p_codec_info| is a pointer to the SBC codec_info to decode.
+// Returns the track bits per sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackBitsPerSampleSbc(const uint8_t* p_codec_info);
+
// Gets the channel count for the A2DP SBC codec.
// |p_codec_info| is a pointer to the SBC codec_info to decode.
// Returns the channel count on success, or -1 if |p_codec_info|
// contains invalid codec information.
int A2DP_VendorGetTrackSampleRate(const uint8_t* p_codec_info);
+// Gets the track bits per sample value for the A2DP vendor-specific codec.
+// |p_codec_info| is a pointer to the vendor-specific codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSample(const uint8_t* p_codec_info);
+
// Gets the channel count for the A2DP vendor-specific codec.
// |p_codec_info| is a pointer to the vendor-specific codec_info to decode.
// Returns the channel count on success, or -1 if |p_codec_info|
// contains invalid codec information.
int A2DP_VendorGetTrackSampleRateAptx(const uint8_t* p_codec_info);
+// Gets the track bits per sample value for the A2DP aptX codec.
+// |p_codec_info| is a pointer to the aptX codec_info to decode.
+// Returns the track bits per sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSampleAptx(const uint8_t* p_codec_info);
+
// Gets the track bitrate 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_VendorGetTrackSampleRateAptxHd(const uint8_t* p_codec_info);
+// Gets the track bits per sample value for the A2DP aptX-HD codec.
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode.
+// Returns the track bits per sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSampleAptxHd(const uint8_t* p_codec_info);
+
// Gets the track bitrate 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|
#include "a2dp_vendor_ldac_constants.h"
#include "avdt_api.h"
-class A2dpCodecConfigLdac : public A2dpCodecConfig {
+class A2dpCodecConfigLdacBase : public A2dpCodecConfig {
+ protected:
+ A2dpCodecConfigLdacBase(btav_a2dp_codec_index_t codec_index,
+ const std::string& name,
+ btav_a2dp_codec_priority_t codec_priority,
+ bool is_source)
+ : A2dpCodecConfig(codec_index, name, codec_priority),
+ is_source_(is_source) {}
+ bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
+ uint8_t* p_result_codec_config) override;
+ bool setPeerCodecCapabilities(
+ const uint8_t* p_peer_codec_capabilities) override;
+
+ private:
+ bool is_source_; // True if local is Source
+};
+
+class A2dpCodecConfigLdacSource : public A2dpCodecConfigLdacBase {
public:
- A2dpCodecConfigLdac(btav_a2dp_codec_priority_t codec_priority);
- virtual ~A2dpCodecConfigLdac();
+ A2dpCodecConfigLdacSource(btav_a2dp_codec_priority_t codec_priority);
+ virtual ~A2dpCodecConfigLdacSource();
bool init() override;
uint64_t encoderIntervalMs() const override;
int getEffectiveMtu() const override;
- bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
- uint8_t* p_result_codec_config) override;
- bool setPeerCodecCapabilities(
- const uint8_t* p_peer_codec_capabilities) override;
private:
bool useRtpHeaderMarkerBit() const override;
void debug_codec_dump(int fd) override;
};
+class A2dpCodecConfigLdacSink : public A2dpCodecConfigLdacBase {
+ public:
+ A2dpCodecConfigLdacSink(btav_a2dp_codec_priority_t codec_priority);
+ virtual ~A2dpCodecConfigLdacSink();
+
+ bool init() override;
+ uint64_t encoderIntervalMs() const override;
+ int getEffectiveMtu() const override;
+
+ private:
+ bool useRtpHeaderMarkerBit() const override;
+ 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.
// codec, otherwise false.
bool A2DP_IsVendorSourceCodecValidLdac(const uint8_t* p_codec_info);
+// Checks whether the codec capabilities contain a valid 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_IsVendorSinkCodecValidLdac(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.
// codec, otherwise false.
bool A2DP_IsVendorPeerSinkCodecValidLdac(const uint8_t* p_codec_info);
+// Checks whether the codec capabilities contain a valid peer 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_IsVendorPeerSourceCodecValidLdac(const uint8_t* p_codec_info);
+
+// Checks whether A2DP LDAC Sink codec is supported.
+// |p_codec_info| contains information about the codec capabilities.
+// Returns true if the A2DP LDAC Sink codec is supported, otherwise false.
+bool A2DP_IsVendorSinkCodecSupportedLdac(const uint8_t* p_codec_info);
+
+// Checks whether an A2DP LDAC Source codec for a peer Source device is
+// supported.
+// |p_codec_info| contains information about the codec capabilities of the
+// peer device.
+// Returns true if the A2DP LDAC Source codec for a peer Source device is
+// supported, otherwise false.
+bool A2DP_IsPeerSourceCodecSupportedLdac(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.
// contains invalid codec information.
int A2DP_VendorGetTrackSampleRateLdac(const uint8_t* p_codec_info);
+// Gets the track bits per sample value for the A2DP LDAC codec.
+// |p_codec_info| is a pointer to the LDAC codec_info to decode.
+// Returns the track bits per sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSampleLdac(const uint8_t* p_codec_info);
+
// Gets the track bitrate 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_VendorGetTrackChannelCountLdac(const uint8_t* p_codec_info);
+// Gets the channel type for the A2DP LDAC codec.
+// 1 for mono, or 3 for dual channel/stereo.
+// |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_VendorGetSinkTrackChannelTypeLdac(const uint8_t* p_codec_info);
+
// Gets the channel mode code for the A2DP LDAC codec.
// The actual value is codec-specific - see |A2DP_LDAC_CHANNEL_MODE_*|.
// |p_codec_info| is a pointer to the LDAC codec_info to decode.
const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceLdac(
const uint8_t* p_codec_info);
+// Gets the current A2DP LDAC decoder interface that can be used to decode
+// received A2DP packets - see |tA2DP_DECODER_INTERFACE|.
+// |p_codec_info| contains the codec information.
+// Returns the A2DP LDAC decoder interface if the |p_codec_info| is valid and
+// supported, otherwise NULL.
+const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceLdac(
+ 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.
btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexLdac(
const uint8_t* p_codec_info);
+// Gets the A2DP LDAC Sink 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_VendorSinkCodecIndexLdac(
+ const uint8_t* p_codec_info);
+
// Gets the A2DP LDAC Source codec name.
const char* A2DP_VendorCodecIndexStrLdac(void);
+// Gets the A2DP LDAC Sink codec name.
+const char* A2DP_VendorCodecIndexStrLdacSink(void);
+
// Initializes A2DP LDAC Source codec information into |AvdtpSepConfig|
// configuration entry pointed by |p_cfg|.
bool A2DP_VendorInitCodecConfigLdac(AvdtpSepConfig* p_cfg);
+// Initializes A2DP LDAC Sink codec information into |AvdtpSepConfig|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_VendorInitCodecConfigLdacSink(AvdtpSepConfig* p_cfg);
+
#endif // A2DP_VENDOR_LDAC_H
--- /dev/null
+/*
+ * Copyright 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 Decoder
+//
+
+#ifndef A2DP_VENDOR_LDAC_DECODER_H
+#define A2DP_VENDOR_LDAC_DECODER_H
+
+#include "a2dp_codec_api.h"
+
+// Loads the A2DP LDAC decoder.
+// Return true on success, otherwise false.
+bool A2DP_VendorLoadDecoderLdac(void);
+
+// Unloads the A2DP LDAC decoder.
+void A2DP_VendorUnloadDecoderLdac(void);
+
+// Initialize the A2DP LDAC decoder.
+bool a2dp_vendor_ldac_decoder_init(decoded_data_callback_t decode_callback);
+
+// Cleanup the A2DP LDAC decoder.
+void a2dp_vendor_ldac_decoder_cleanup(void);
+
+// Decodes |p_buf|. Calls |decode_callback| passed into
+// |a2dp_vendor_ldac_decoder_init| if decoded frames are available.
+bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf);
+
+#endif // A2DP_VENDOR_LDAC_DECODER_H
static const char* APTX_ENCODER_LIB_NAME = "libaptX_encoder.so";
static const char* APTX_HD_ENCODER_LIB_NAME = "libaptXHD_encoder.so";
static const char* LDAC_ENCODER_LIB_NAME = "libldacBT_enc.so";
+static const char* LDAC_DECODER_LIB_NAME = "libldacBT_dec.so";
static bool has_shared_library(const char* name) {
void* lib_handle = dlopen(name, RTLD_NOW);
case BTAV_A2DP_CODEC_INDEX_SINK_AAC:
supported = true;
break;
+ case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
+ // Codec LDAC is supported only if the device has the corresponding
+ // shared library installed.
+ supported = has_shared_library(LDAC_DECODER_LIB_NAME);
+ break;
case BTAV_A2DP_CODEC_INDEX_MAX:
// Needed to avoid using "default:" case so we can capture when
// a new codec is added, and it can be included here.