From e86055ad44f7c38b87152e816c7bb28f60910b86 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov <pavlin@google.com> Date: Mon, 9 Apr 2018 18:01:41 -0700 Subject: [PATCH] Handle properly AVDTP SetConfig from the A2DP Sink device If the remote device is proactive and sends AVDTP SetConfig after re-connection before the local device gets the chance to do it, the internal codec setup state might be inconsistent. * Fix the internal logic when the local device is Acceptor inside file bta_av_co.cc, and simplify some of the code. * Fix the handling of p_scb->sep_info_idx inside bta_av_save_caps() when receiving capabilities from the remote device. * Add new A2dpCodecConfig::setPeerCodecCapabilities() method that is implemented by each codec. Bug: 77525584 Test: Manual - initiate connection by Momentum 2.0 Wireless Headset. Connect/disconnect/reconnect multiple headsets. Change-Id: I456df7c8a2fa0758c0908a4628c4dfae3259dbdb --- system/bta/av/bta_av_aact.cc | 24 +- system/btif/co/bta_av_co.cc | 368 ++++++++++++--------- system/stack/a2dp/a2dp_aac.cc | 72 +++- system/stack/a2dp/a2dp_codec_config.cc | 22 ++ system/stack/a2dp/a2dp_sbc.cc | 80 ++++- system/stack/a2dp/a2dp_vendor_aptx.cc | 120 +++++-- system/stack/a2dp/a2dp_vendor_aptx_hd.cc | 133 ++++++-- system/stack/a2dp/a2dp_vendor_ldac.cc | 150 +++++++-- system/stack/include/a2dp_aac.h | 2 + system/stack/include/a2dp_codec_api.h | 16 + system/stack/include/a2dp_sbc.h | 2 + system/stack/include/a2dp_vendor_aptx.h | 2 + system/stack/include/a2dp_vendor_aptx_hd.h | 2 + system/stack/include/a2dp_vendor_ldac.h | 2 + 14 files changed, 737 insertions(+), 258 deletions(-) diff --git a/system/bta/av/bta_av_aact.cc b/system/bta/av/bta_av_aact.cc index ba4bbdd0e6f..c339e88ac01 100644 --- a/system/bta/av/bta_av_aact.cc +++ b/system/bta/av/bta_av_aact.cc @@ -1523,24 +1523,30 @@ void bta_av_save_caps(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { cfg = p_scb->peer_cap; /* let application know the capability of the SNK */ - p_scb->p_cos->getcfg(p_scb->hndl, p_scb->peer_addr, cfg.codec_info, - &p_scb->sep_info_idx, p_info->seid, &cfg.num_protect, - cfg.protect_info); - - p_scb->sep_info_idx++; - APPL_TRACE_DEBUG("%s: result: sep_info_idx:%d", __func__, - p_scb->sep_info_idx); + if (p_scb->p_cos->getcfg(p_scb->hndl, p_scb->peer_addr, cfg.codec_info, + &p_scb->sep_info_idx, p_info->seid, &cfg.num_protect, + cfg.protect_info) != A2DP_SUCCESS) { + p_scb->sep_info_idx++; + APPL_TRACE_DEBUG("%s: result: next sep_info_idx:%d", __func__, + p_scb->sep_info_idx); + } else { + // All capabilities found + getcap_done = true; + APPL_TRACE_DEBUG("%s: result: done sep_info_idx:%d", __func__, + p_scb->sep_info_idx); + } APPL_TRACE_DEBUG("%s: codec: %s", __func__, A2DP_CodecInfoString(cfg.codec_info).c_str()); - if (p_scb->num_seps > p_scb->sep_info_idx) { + if (p_scb->num_seps > p_scb->sep_info_idx && !getcap_done) { /* Some devices have seps at the end of the discover list, which is not */ /* matching media type(video not audio). */ /* In this case, we are done with getcap without sending another */ /* request to AVDT. */ if (!bta_av_next_getcap(p_scb, p_data)) getcap_done = true; - } else + } else { getcap_done = true; + } if (getcap_done) { APPL_TRACE_DEBUG("%s: getcap_done: num_seps:%d sep_info_idx:%d wait:0x%x", diff --git a/system/btif/co/bta_av_co.cc b/system/btif/co/bta_av_co.cc index d01707cfc16..364991957e0 100644 --- a/system/btif/co/bta_av_co.cc +++ b/system/btif/co/bta_av_co.cc @@ -219,7 +219,7 @@ class BtaAvCo { /** * Process retrieved codec configuration and content protection from - * Peer Source SEP. + * Peer Sink SEP. * * @param bta_av_handle the BTA AV handle to identify the peer * @param peer_address the peer address @@ -478,6 +478,26 @@ class BtaAvCo { */ BtaAvCoPeer* FindPeer(const RawAddress& peer_address); + /** + * Find the peer Sink SEP entry for a given codec index. + * + * @param p_peer the peer to use + * @param codec_index the codec index to use + * @return the peer Sink SEP for the codec index if found, otherwise nullptr + */ + BtaAvCoSep* FindPeerSink(BtaAvCoPeer* p_peer, + btav_a2dp_codec_index_t codec_index); + + /** + * Find the peer Source SEP entry for a given codec index. + * + * @param p_peer the peer to use + * @param codec_config the codec index to use + * @return the peer Source SEP for the codec index if found, otherwise nullptr + */ + BtaAvCoSep* FindPeerSource(BtaAvCoPeer* p_peer, + btav_a2dp_codec_index_t codec_index); + private: /** * Reset the state. @@ -513,7 +533,7 @@ class BtaAvCo { * @return a pointer to the corresponding SEP Sink entry on success, * otherwise nullptr */ - BtaAvCoSep* SelectSourceCodec(BtaAvCoPeer* p_peer); + const BtaAvCoSep* SelectSourceCodec(BtaAvCoPeer* p_peer); /** * Select the Sink codec configuration based on peer codec support. @@ -525,7 +545,7 @@ class BtaAvCo { * @return a pointer to the corresponding SEP Source entry on success, * otherwise nullptr */ - BtaAvCoSep* SelectSinkCodec(BtaAvCoPeer* p_peer); + const BtaAvCoSep* SelectSinkCodec(BtaAvCoPeer* p_peer); /** * Save new codec configuration. @@ -561,6 +581,15 @@ class BtaAvCo { uint8_t num_protect, const uint8_t* p_protect_info, bool* p_restart_output); + /** + * Update all selectable Source codecs with the corresponding codec + * information from a Sink peer. + * + * @param p_peer the peer Sink SEP to use + * @return the number of codecs that have been updated + */ + size_t UpdateAllSelectableSourceCodecs(BtaAvCoPeer* p_peer); + /** * Update a selectable Source codec with the corresponding codec information * from a Sink peer. @@ -572,6 +601,15 @@ class BtaAvCo { bool UpdateSelectableSourceCodec(const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer); + /** + * Update all selectable Sink codecs with the corresponding codec + * information from a Source peer. + * + * @param p_peer the peer Source SEP to use + * @return the number of codecs that have been updated + */ + size_t UpdateAllSelectableSinkCodecs(BtaAvCoPeer* p_peer); + /** * Update a selectable Sink codec with the corresponding codec information * from a Source peer. @@ -591,8 +629,8 @@ class BtaAvCo { * @return a pointer to the corresponding SEP Sink entry on success, * otnerwise nullptr */ - BtaAvCoSep* AttemptSourceCodecSelection(const A2dpCodecConfig& codec_config, - BtaAvCoPeer* p_peer); + const BtaAvCoSep* AttemptSourceCodecSelection( + const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer); /** * Attempt to select Sink codec configuration for a Source peer. @@ -602,8 +640,8 @@ class BtaAvCo { * @return a pointer to the corresponding SEP Source entry on success, * otnerwise nullptr */ - BtaAvCoSep* AttemptSinkCodecSelection(const A2dpCodecConfig& codec_config, - BtaAvCoPeer* p_peer); + const BtaAvCoSep* AttemptSinkCodecSelection( + const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer); /** * Check if a peer SEP has content protection enabled. @@ -838,6 +876,7 @@ void BtaAvCo::ProcessDiscoveryResult(tBTA_AV_HNDL bta_av_handle, p_peer->num_rx_sinks = 0; p_peer->num_rx_sources = 0; p_peer->num_sup_sinks = 0; + p_peer->num_sup_sources = 0; if (uuid_local == UUID_SERVCLASS_AUDIO_SINK) { p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SOURCE; } else if (uuid_local == UUID_SERVCLASS_AUDIO_SOURCE) { @@ -898,13 +937,31 @@ tA2DP_STATUS BtaAvCo::ProcessSourceGetConfig( (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) { return A2DP_FAIL; } - APPL_TRACE_DEBUG("%s: last Sink reached", __func__); + APPL_TRACE_DEBUG("%s: last Sink codec reached for peer %s", __func__, + p_peer->addr.ToString().c_str()); // Select the Source codec - const BtaAvCoSep* p_sink = SelectSourceCodec(p_peer); - if (p_sink == nullptr) { - APPL_TRACE_ERROR("%s: cannot set up codec for the peer Sink", __func__); - return A2DP_FAIL; + const BtaAvCoSep* p_sink = nullptr; + if (p_peer->acceptor) { + UpdateAllSelectableSourceCodecs(p_peer); + if (p_peer->p_sink == nullptr) { + // Update the selected codec + p_peer->p_sink = + FindPeerSink(p_peer, A2DP_SourceCodecIndex(p_peer->codec_config)); + } + p_sink = p_peer->p_sink; + if (p_sink == nullptr) { + APPL_TRACE_ERROR("%s: cannot find the selected codec for peer %s", + __func__, p_peer->addr.ToString().c_str()); + return A2DP_FAIL; + } + } else { + p_sink = SelectSourceCodec(p_peer); + if (p_sink == nullptr) { + APPL_TRACE_ERROR("%s: cannot set up codec for peer %s", __func__, + p_peer->addr.ToString().c_str()); + return A2DP_FAIL; + } } // By default, no content protection @@ -915,18 +972,19 @@ tA2DP_STATUS BtaAvCo::ProcessSourceGetConfig( } // If acceptor -> reconfig otherwise reply for configuration + *p_sep_info_idx = p_sink->sep_info_idx; + APPL_TRACE_EVENT("%s: peer %s acceptor:%s reconfig_needed:%s", __func__, + p_peer->addr.ToString().c_str(), + (p_peer->acceptor) ? "true" : "false", + (p_peer->reconfig_needed) ? "true" : "false"); if (p_peer->acceptor) { - // Stop fetching caps once we retrieved a supported codec - APPL_TRACE_EVENT("%s: no need to fetch more SEPs", __func__); - *p_sep_info_idx = p_peer->num_seps; if (p_peer->reconfig_needed) { - APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__, - bta_av_handle); + APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x) for peer %s", __func__, + bta_av_handle, p_peer->addr.ToString().c_str()); BTA_AvReconfig(bta_av_handle, true, p_sink->sep_info_idx, p_peer->codec_config, *p_num_protect, bta_av_co_cp_scmst); } } else { - *p_sep_info_idx = p_sink->sep_info_idx; memcpy(p_codec_info, p_peer->codec_config, AVDT_CODEC_SIZE); } @@ -992,13 +1050,31 @@ tA2DP_STATUS BtaAvCo::ProcessSinkGetConfig(tBTA_AV_HNDL bta_av_handle, (p_peer->num_sup_sources != BTA_AV_CO_NUM_ELEMENTS(p_peer->sources))) { return A2DP_FAIL; } - APPL_TRACE_DEBUG("%s: last Source reached", __func__); + APPL_TRACE_DEBUG("%s: last Source codec reached for peer %s", __func__, + p_peer->addr.ToString().c_str()); // Select the Sink codec - const BtaAvCoSep* p_source = SelectSinkCodec(p_peer); - if (p_source == nullptr) { - APPL_TRACE_ERROR("%s: cannot set up codec for the peer Source", __func__); - return A2DP_FAIL; + const BtaAvCoSep* p_source = nullptr; + if (p_peer->acceptor) { + UpdateAllSelectableSinkCodecs(p_peer); + if (p_peer->p_source == nullptr) { + // Update the selected codec + p_peer->p_source = + FindPeerSource(p_peer, A2DP_SinkCodecIndex(p_peer->codec_config)); + } + p_source = p_peer->p_source; + if (p_source == nullptr) { + APPL_TRACE_ERROR("%s: cannot find the selected codec for peer %s", + __func__, p_peer->addr.ToString().c_str()); + return A2DP_FAIL; + } + } else { + p_source = SelectSinkCodec(p_peer); + if (p_source == nullptr) { + APPL_TRACE_ERROR("%s: cannot set up codec for the peer %s", __func__, + p_peer->addr.ToString().c_str()); + return A2DP_FAIL; + } } // By default, no content protection @@ -1009,18 +1085,19 @@ tA2DP_STATUS BtaAvCo::ProcessSinkGetConfig(tBTA_AV_HNDL bta_av_handle, } // If acceptor -> reconfig otherwise reply for configuration + *p_sep_info_idx = p_source->sep_info_idx; + APPL_TRACE_EVENT("%s: peer %s acceptor:%s reconfig_needed:%s", __func__, + p_peer->addr.ToString().c_str(), + (p_peer->acceptor) ? "true" : "false", + (p_peer->reconfig_needed) ? "true" : "false"); if (p_peer->acceptor) { - // Stop fetching caps once we retrieved a supported codec - APPL_TRACE_EVENT("%s: no need to fetch more SEPs", __func__); - *p_sep_info_idx = p_peer->num_seps; if (p_peer->reconfig_needed) { - APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__, - bta_av_handle); + APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x) for peer %s", __func__, + bta_av_handle, p_peer->addr.ToString().c_str()); BTA_AvReconfig(bta_av_handle, true, p_source->sep_info_idx, p_peer->codec_config, *p_num_protect, bta_av_co_cp_scmst); } } else { - *p_sep_info_idx = p_source->sep_info_idx; memcpy(p_codec_info, p_peer->codec_config, AVDT_CODEC_SIZE); } @@ -1314,6 +1391,8 @@ bool BtaAvCo::SetActivePeer(const RawAddress& peer_address) { active_peer_ = p_peer; memcpy(codec_config_, active_peer_->codec_config, AVDT_CODEC_SIZE); + APPL_TRACE_DEBUG("%s: codec = %s", __func__, + A2DP_CodecInfoString(codec_config_).c_str()); ReportSourceCodecState(active_peer_); return true; } @@ -1378,16 +1457,7 @@ bool BtaAvCo::SetCodecUserConfig( // Find the peer SEP codec to use if (codec_user_config.codec_type < BTAV_A2DP_CODEC_INDEX_MAX) { - for (size_t index = 0; index < p_peer->num_sup_sinks; index++) { - btav_a2dp_codec_index_t peer_codec_index = - A2DP_SourceCodecIndex(p_peer->sinks[index].codec_caps); - if (peer_codec_index != codec_user_config.codec_type) continue; - if (!AudioSepHasContentProtection(&p_peer->sinks[index])) { - continue; - } - p_sink = &p_peer->sinks[index]; - break; - } + p_sink = FindPeerSink(p_peer, codec_user_config.codec_type); } else { // Use the current sink codec p_sink = p_peer->p_sink; @@ -1430,6 +1500,7 @@ bool BtaAvCo::SetCodecUserConfig( goto done; } + p_peer->acceptor = false; APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__, p_peer->BtaAvHandle()); BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx, @@ -1495,6 +1566,7 @@ bool BtaAvCo::SetCodecAudioConfig( APPL_TRACE_WARNING("%s: not all peer's capabilities have been retrieved", __func__); } else { + p_peer->acceptor = false; APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__, p_peer->BtaAvHandle()); BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx, @@ -1529,6 +1601,9 @@ bool BtaAvCo::ReportSourceCodecState(BtaAvCoPeer* p_peer) { __func__, p_peer->addr.ToString().c_str()); return false; } + APPL_TRACE_DEBUG("%s: peer %s codec_config=%s", __func__, + p_peer->addr.ToString().c_str(), + codec_config.ToString().c_str()); do_in_jni_thread( FROM_HERE, base::Bind(&btif_av_report_source_codec_state, p_peer->addr, codec_config, @@ -1620,17 +1695,13 @@ bool BtaAvCo::AudioSepHasContentProtection(const BtaAvCoSep* p_sep) { return true; } -BtaAvCoSep* BtaAvCo::SelectSourceCodec(BtaAvCoPeer* p_peer) { - BtaAvCoSep* p_sink = nullptr; +const BtaAvCoSep* BtaAvCo::SelectSourceCodec(BtaAvCoPeer* p_peer) { + const BtaAvCoSep* p_sink = nullptr; // Update all selectable codecs. // This is needed to update the selectable parameters for each codec. // NOTE: The selectable codec info is used only for informational purpose. - for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) { - APPL_TRACE_DEBUG("%s: updating selectable codec %s", __func__, - iter->name().c_str()); - UpdateSelectableSourceCodec(*iter, p_peer); - } + UpdateAllSelectableSourceCodecs(p_peer); // Select the codec for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) { @@ -1650,17 +1721,13 @@ BtaAvCoSep* BtaAvCo::SelectSourceCodec(BtaAvCoPeer* p_peer) { return p_sink; } -BtaAvCoSep* BtaAvCo::SelectSinkCodec(BtaAvCoPeer* p_peer) { - BtaAvCoSep* p_source = nullptr; +const BtaAvCoSep* BtaAvCo::SelectSinkCodec(BtaAvCoPeer* p_peer) { + const BtaAvCoSep* p_source = nullptr; // Update all selectable codecs. // This is needed to update the selectable parameters for each codec. // NOTE: The selectable codec info is used only for informational purpose. - for (const auto& iter : p_peer->GetCodecs()->orderedSinkCodecs()) { - APPL_TRACE_DEBUG("%s: updating selectable codec %s", __func__, - iter->name().c_str()); - UpdateSelectableSinkCodec(*iter, p_peer); - } + UpdateAllSelectableSinkCodecs(p_peer); // Select the codec for (const auto& iter : p_peer->GetCodecs()->orderedSinkCodecs()) { @@ -1680,30 +1747,70 @@ BtaAvCoSep* BtaAvCo::SelectSinkCodec(BtaAvCoPeer* p_peer) { return p_source; } -BtaAvCoSep* BtaAvCo::AttemptSourceCodecSelection( - const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer) { - uint8_t new_codec_config[AVDT_CODEC_SIZE]; - - APPL_TRACE_DEBUG("%s", __func__); +BtaAvCoSep* BtaAvCo::FindPeerSink(BtaAvCoPeer* p_peer, + btav_a2dp_codec_index_t codec_index) { + if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) { + APPL_TRACE_WARNING("%s: invalid codec index for peer %s", __func__, + p_peer->addr.ToString().c_str()); + return nullptr; + } // Find the peer Sink for the codec - BtaAvCoSep* p_sink = nullptr; for (size_t index = 0; index < p_peer->num_sup_sinks; index++) { + BtaAvCoSep* p_sink = &p_peer->sinks[index]; btav_a2dp_codec_index_t peer_codec_index = - A2DP_SourceCodecIndex(p_peer->sinks[index].codec_caps); - if (peer_codec_index != codec_config.codecIndex()) { + A2DP_SourceCodecIndex(p_sink->codec_caps); + if (peer_codec_index != codec_index) { continue; } - if (!AudioSepHasContentProtection(&p_peer->sinks[index])) { + if (!AudioSepHasContentProtection(p_sink)) { APPL_TRACE_DEBUG( "%s: peer Sink for codec %s does not support " "Content Protection", - __func__, codec_config.name().c_str()); + __func__, A2DP_CodecIndexStr(codec_index)); + continue; + } + return p_sink; + } + return nullptr; +} + +BtaAvCoSep* BtaAvCo::FindPeerSource(BtaAvCoPeer* p_peer, + btav_a2dp_codec_index_t codec_index) { + if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) { + APPL_TRACE_WARNING("%s: invalid codec index for peer %s", __func__, + p_peer->addr.ToString().c_str()); + return nullptr; + } + + // Find the peer Source for the codec + for (size_t index = 0; index < p_peer->num_sup_sources; index++) { + BtaAvCoSep* p_source = &p_peer->sources[index]; + btav_a2dp_codec_index_t peer_codec_index = + A2DP_SinkCodecIndex(p_source->codec_caps); + if (peer_codec_index != codec_index) { + continue; + } + if (!AudioSepHasContentProtection(p_source)) { + APPL_TRACE_DEBUG( + "%s: peer Source for codec %s does not support " + "Content Protection", + __func__, A2DP_CodecIndexStr(codec_index)); continue; } - p_sink = &p_peer->sinks[index]; - break; + return p_source; } + return nullptr; +} + +const BtaAvCoSep* BtaAvCo::AttemptSourceCodecSelection( + const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer) { + uint8_t new_codec_config[AVDT_CODEC_SIZE]; + + APPL_TRACE_DEBUG("%s", __func__); + + // Find the peer Sink for the codec + BtaAvCoSep* p_sink = FindPeerSink(p_peer, codec_config.codecIndex()); if (p_sink == nullptr) { APPL_TRACE_DEBUG("%s: peer Sink for codec %s not found", __func__, codec_config.name().c_str()); @@ -1724,30 +1831,14 @@ BtaAvCoSep* BtaAvCo::AttemptSourceCodecSelection( return p_sink; } -BtaAvCoSep* BtaAvCo::AttemptSinkCodecSelection( +const BtaAvCoSep* BtaAvCo::AttemptSinkCodecSelection( const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer) { uint8_t new_codec_config[AVDT_CODEC_SIZE]; APPL_TRACE_DEBUG("%s", __func__); // Find the peer Source for the codec - BtaAvCoSep* p_source = nullptr; - for (size_t index = 0; index < p_peer->num_sup_sources; index++) { - btav_a2dp_codec_index_t peer_codec_index = - A2DP_SinkCodecIndex(p_peer->sources[index].codec_caps); - if (peer_codec_index != codec_config.codecIndex()) { - continue; - } - if (!AudioSepHasContentProtection(&p_peer->sources[index])) { - APPL_TRACE_DEBUG( - "%s: peer Source for codec %s does not support " - "Content Protection", - __func__, codec_config.name().c_str()); - continue; - } - p_source = &p_peer->sources[index]; - break; - } + BtaAvCoSep* p_source = FindPeerSource(p_peer, codec_config.codecIndex()); if (p_source == nullptr) { APPL_TRACE_DEBUG("%s: peer Source for codec %s not found", __func__, codec_config.name().c_str()); @@ -1768,77 +1859,69 @@ BtaAvCoSep* BtaAvCo::AttemptSinkCodecSelection( return p_source; } +size_t BtaAvCo::UpdateAllSelectableSourceCodecs(BtaAvCoPeer* p_peer) { + APPL_TRACE_DEBUG("%s: peer %s", __func__, p_peer->addr.ToString().c_str()); + + size_t updated_codecs = 0; + for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) { + APPL_TRACE_DEBUG("%s: updating selectable codec %s", __func__, + iter->name().c_str()); + if (UpdateSelectableSourceCodec(*iter, p_peer)) { + updated_codecs++; + } + } + return updated_codecs; +} + bool BtaAvCo::UpdateSelectableSourceCodec(const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer) { - uint8_t new_codec_config[AVDT_CODEC_SIZE]; - - APPL_TRACE_DEBUG("%s", __func__); + APPL_TRACE_DEBUG("%s: peer %s", __func__, p_peer->addr.ToString().c_str()); // Find the peer Sink for the codec - const BtaAvCoSep* p_sink = nullptr; - for (size_t index = 0; index < p_peer->num_sup_sinks; index++) { - btav_a2dp_codec_index_t peer_codec_index = - A2DP_SourceCodecIndex(p_peer->sinks[index].codec_caps); - if (peer_codec_index != codec_config.codecIndex()) { - continue; - } - if (!AudioSepHasContentProtection(&p_peer->sinks[index])) { - APPL_TRACE_DEBUG( - "%s: peer Sink for codec %s does not support " - "Content Protection", - __func__, codec_config.name().c_str()); - continue; - } - p_sink = &p_peer->sinks[index]; - break; - } + const BtaAvCoSep* p_sink = FindPeerSink(p_peer, codec_config.codecIndex()); if (p_sink == nullptr) { // The peer Sink device does not support this codec return false; } - if (!p_peer->GetCodecs()->setCodecConfig( - p_sink->codec_caps, true /* is_capability */, new_codec_config, - false /* select_current_codec */)) { - APPL_TRACE_DEBUG("%s: cannot update source codec %s", __func__, - codec_config.name().c_str()); + if (!p_peer->GetCodecs()->setPeerSinkCodecCapabilities(p_sink->codec_caps)) { + APPL_TRACE_WARNING("%s: cannot update peer %s codec capabilities for %s", + __func__, p_peer->addr.ToString().c_str(), + A2DP_CodecName(p_sink->codec_caps)); return false; } return true; } +size_t BtaAvCo::UpdateAllSelectableSinkCodecs(BtaAvCoPeer* p_peer) { + APPL_TRACE_DEBUG("%s: peer %s", __func__, p_peer->addr.ToString().c_str()); + + size_t updated_codecs = 0; + for (const auto& iter : p_peer->GetCodecs()->orderedSinkCodecs()) { + APPL_TRACE_DEBUG("%s: updating selectable codec %s", __func__, + iter->name().c_str()); + if (UpdateSelectableSinkCodec(*iter, p_peer)) { + updated_codecs++; + } + } + return updated_codecs; +} + bool BtaAvCo::UpdateSelectableSinkCodec(const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer) { - uint8_t new_codec_config[AVDT_CODEC_SIZE]; - - APPL_TRACE_DEBUG("%s", __func__); + APPL_TRACE_DEBUG("%s: peer %s", __func__, p_peer->addr.ToString().c_str()); // Find the peer Source for the codec - const BtaAvCoSep* p_source = nullptr; - for (size_t index = 0; index < p_peer->num_sup_sources; index++) { - btav_a2dp_codec_index_t peer_codec_index = - A2DP_SinkCodecIndex(p_peer->sources[index].codec_caps); - if (peer_codec_index != codec_config.codecIndex()) { - continue; - } - if (!AudioSepHasContentProtection(&p_peer->sources[index])) { - APPL_TRACE_DEBUG( - "%s: peer Source for codec %s does not support " - "Content Protection", - __func__, codec_config.name().c_str()); - continue; - } - p_source = &p_peer->sources[index]; - break; - } + const BtaAvCoSep* p_source = + FindPeerSource(p_peer, codec_config.codecIndex()); if (p_source == nullptr) { // The peer Source device does not support this codec return false; } - if (!p_peer->GetCodecs()->setSinkCodecConfig( - p_source->codec_caps, true /* is_capability */, new_codec_config, - false /* select_current_codec */)) { - APPL_TRACE_DEBUG("%s: cannot update source codec %s", __func__, - codec_config.name().c_str()); + if (!p_peer->GetCodecs()->setPeerSourceCodecCapabilities( + p_source->codec_caps)) { + APPL_TRACE_WARNING("%s: cannot update peer %s codec capabilities for %s", + __func__, p_peer->addr.ToString().c_str(), + A2DP_CodecName(p_source->codec_caps)); return false; } return true; @@ -1882,23 +1965,8 @@ bool BtaAvCo::SetCodecOtaConfig(BtaAvCoPeer* p_peer, *p_restart_output = false; // Find the peer SEP codec to use - btav_a2dp_codec_index_t ota_codec_index = - A2DP_SourceCodecIndex(p_ota_codec_config); - if (ota_codec_index == BTAV_A2DP_CODEC_INDEX_MAX) { - APPL_TRACE_WARNING("%s: invalid peer codec config", __func__); - return false; - } - const BtaAvCoSep* p_sink = nullptr; - for (size_t index = 0; index < p_peer->num_sup_sinks; index++) { - btav_a2dp_codec_index_t peer_codec_index = - A2DP_SourceCodecIndex(p_peer->sinks[index].codec_caps); - if (peer_codec_index != ota_codec_index) continue; - if (!AudioSepHasContentProtection(&p_peer->sinks[index])) { - continue; - } - p_sink = &p_peer->sinks[index]; - break; - } + const BtaAvCoSep* p_sink = + FindPeerSink(p_peer, A2DP_SourceCodecIndex(p_ota_codec_config)); if ((p_peer->num_sup_sinks > 0) && (p_sink == nullptr)) { // There are no peer SEPs if we didn't do the discovery procedure yet. // We have all the information we need from the peer, so we can diff --git a/system/stack/a2dp/a2dp_aac.cc b/system/stack/a2dp/a2dp_aac.cc index 4a29b78d8da..b9dbfbeaf1c 100644 --- a/system/stack/a2dp/a2dp_aac.cc +++ b/system/stack/a2dp/a2dp_aac.cc @@ -1295,7 +1295,7 @@ bool A2dpCodecConfigAacBase::setCodecConfig(const uint8_t* p_peer_codec_info, 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 + // Create a local copy of the peer codec capability/config, and the // result codec config. if (is_capability) { status = A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, @@ -1324,6 +1324,76 @@ fail: 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_); + tA2DP_AAC_CIE peer_info_cie; + uint8_t channelMode; + uint16_t sampleRate; + const tA2DP_AAC_CIE* p_a2dp_aac_caps = + (is_source_) ? &a2dp_aac_source_caps : &a2dp_aac_sink_caps; + + // Save the internal state + btav_a2dp_codec_config_t saved_codec_selectable_capability = + codec_selectable_capability_; + uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE]; + memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_, + sizeof(ota_codec_peer_capability_)); + + tA2DP_STATUS status = + A2DP_ParseInfoAac(&peer_info_cie, p_peer_codec_capabilities, true); + if (status != A2DP_SUCCESS) { + LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d", + __func__, status); + goto fail; + } + + // Compute the selectable capability - sample rate + sampleRate = p_a2dp_aac_caps->sampleRate & peer_info_cie.sampleRate; + if (sampleRate & A2DP_AAC_SAMPLING_FREQ_44100) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + } + if (sampleRate & A2DP_AAC_SAMPLING_FREQ_48000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + } + if (sampleRate & A2DP_AAC_SAMPLING_FREQ_88200) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_88200; + } + if (sampleRate & A2DP_AAC_SAMPLING_FREQ_96000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_96000; + } + + // Compute the selectable capability - bits per sample + codec_selectable_capability_.bits_per_sample = + p_a2dp_aac_caps->bits_per_sample; + + // Compute the selectable capability - channel mode + channelMode = p_a2dp_aac_caps->channelMode & peer_info_cie.channelMode; + if (channelMode & A2DP_AAC_CHANNEL_MODE_MONO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + } + if (channelMode & A2DP_AAC_CHANNEL_MODE_STEREO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + status = A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_capability_); + CHECK(status == A2DP_SUCCESS); + return true; + +fail: + // Restore the internal state + codec_selectable_capability_ = saved_codec_selectable_capability; + memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability, + sizeof(ota_codec_peer_capability_)); + return false; +} A2dpCodecConfigAacSink::A2dpCodecConfigAacSink( btav_a2dp_codec_priority_t codec_priority) diff --git a/system/stack/a2dp/a2dp_codec_config.cc b/system/stack/a2dp/a2dp_codec_config.cc index 21813cd479b..1279cebc393 100644 --- a/system/stack/a2dp/a2dp_codec_config.cc +++ b/system/stack/a2dp/a2dp_codec_config.cc @@ -908,6 +908,28 @@ fail: return false; } +bool A2dpCodecs::setPeerSinkCodecCapabilities( + const uint8_t* p_peer_codec_capabilities) { + std::lock_guard<std::recursive_mutex> lock(codec_mutex_); + + if (!A2DP_IsPeerSinkCodecValid(p_peer_codec_capabilities)) return false; + A2dpCodecConfig* a2dp_codec_config = + findSourceCodecConfig(p_peer_codec_capabilities); + if (a2dp_codec_config == nullptr) return false; + return a2dp_codec_config->setPeerCodecCapabilities(p_peer_codec_capabilities); +} + +bool A2dpCodecs::setPeerSourceCodecCapabilities( + const uint8_t* p_peer_codec_capabilities) { + std::lock_guard<std::recursive_mutex> lock(codec_mutex_); + + if (!A2DP_IsPeerSourceCodecValid(p_peer_codec_capabilities)) return false; + A2dpCodecConfig* a2dp_codec_config = + findSinkCodecConfig(p_peer_codec_capabilities); + if (a2dp_codec_config == nullptr) return false; + return a2dp_codec_config->setPeerCodecCapabilities(p_peer_codec_capabilities); +} + bool A2dpCodecs::getCodecConfigAndCapabilities( btav_a2dp_codec_config_t* p_codec_config, std::vector<btav_a2dp_codec_config_t>* p_codecs_local_capabilities, diff --git a/system/stack/a2dp/a2dp_sbc.cc b/system/stack/a2dp/a2dp_sbc.cc index 565a67de910..43f7625ba48 100644 --- a/system/stack/a2dp/a2dp_sbc.cc +++ b/system/stack/a2dp/a2dp_sbc.cc @@ -1425,12 +1425,13 @@ bool A2dpCodecConfigSbcBase::setCodecConfig(const uint8_t* p_peer_codec_info, // Create a local copy of the peer codec capability/config, and the // result codec config. if (is_capability) { - memcpy(ota_codec_peer_capability_, p_peer_codec_info, - sizeof(ota_codec_peer_capability_)); + status = A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_capability_); } else { - memcpy(ota_codec_peer_config_, p_peer_codec_info, - sizeof(ota_codec_peer_config_)); + status = A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_config_); } + CHECK(status == A2DP_SUCCESS); status = A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie, ota_codec_config_); CHECK(status == A2DP_SUCCESS); @@ -1451,6 +1452,77 @@ fail: return false; } +bool A2dpCodecConfigSbcBase::setPeerCodecCapabilities( + const uint8_t* p_peer_codec_capabilities) { + std::lock_guard<std::recursive_mutex> lock(codec_mutex_); + tA2DP_SBC_CIE peer_info_cie; + uint8_t samp_freq; + uint8_t ch_mode; + const tA2DP_SBC_CIE* p_a2dp_sbc_caps = + (is_source_) ? &a2dp_sbc_source_caps : &a2dp_sbc_sink_caps; + + // Save the internal state + btav_a2dp_codec_config_t saved_codec_selectable_capability = + codec_selectable_capability_; + uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE]; + memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_, + sizeof(ota_codec_peer_capability_)); + + tA2DP_STATUS status = + A2DP_ParseInfoSbc(&peer_info_cie, p_peer_codec_capabilities, true); + if (status != A2DP_SUCCESS) { + LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d", + __func__, status); + goto fail; + } + + // Compute the selectable capability - sample rate + samp_freq = p_a2dp_sbc_caps->samp_freq & peer_info_cie.samp_freq; + if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_44) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + } + if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_48) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + } + + // Compute the selectable capability - bits per sample + codec_selectable_capability_.bits_per_sample = + p_a2dp_sbc_caps->bits_per_sample; + + // Compute the selectable capability - channel mode + ch_mode = p_a2dp_sbc_caps->ch_mode & peer_info_cie.ch_mode; + if (ch_mode & A2DP_SBC_IE_CH_MD_MONO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + } + if (ch_mode & A2DP_SBC_IE_CH_MD_JOINT) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + if (ch_mode & A2DP_SBC_IE_CH_MD_STEREO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + if (ch_mode & A2DP_SBC_IE_CH_MD_DUAL) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + status = A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_capability_); + CHECK(status == A2DP_SUCCESS); + return true; + +fail: + // Restore the internal state + codec_selectable_capability_ = saved_codec_selectable_capability; + memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability, + sizeof(ota_codec_peer_capability_)); + return false; +} + A2dpCodecConfigSbcSink::A2dpCodecConfigSbcSink( btav_a2dp_codec_priority_t codec_priority) : A2dpCodecConfigSbcBase(BTAV_A2DP_CODEC_INDEX_SINK_SBC, "SBC(Sink)", diff --git a/system/stack/a2dp/a2dp_vendor_aptx.cc b/system/stack/a2dp/a2dp_vendor_aptx.cc index 48e9b967bba..7017f06635b 100644 --- a/system/stack/a2dp/a2dp_vendor_aptx.cc +++ b/system/stack/a2dp/a2dp_vendor_aptx.cc @@ -49,7 +49,7 @@ typedef struct { } tA2DP_APTX_CIE; /* aptX Source codec capabilities */ -static const tA2DP_APTX_CIE a2dp_aptx_caps = { +static const tA2DP_APTX_CIE a2dp_aptx_source_caps = { A2DP_APTX_VENDOR_ID, /* vendorId */ A2DP_APTX_CODEC_ID_BLUETOOTH, /* codecId */ (A2DP_APTX_SAMPLERATE_44100 | A2DP_APTX_SAMPLERATE_48000), /* sampleRate */ @@ -401,7 +401,7 @@ btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexAptx( const char* A2DP_VendorCodecIndexStrAptx(void) { return "aptX"; } bool A2DP_VendorInitCodecConfigAptx(AvdtpSepConfig* p_cfg) { - if (A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_caps, + if (A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_source_caps, p_cfg->codec_info) != A2DP_SUCCESS) { return false; } @@ -422,17 +422,18 @@ A2dpCodecConfigAptx::A2dpCodecConfigAptx( : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, "aptX", codec_priority) { // Compute the local capability - if (a2dp_aptx_caps.sampleRate & A2DP_APTX_SAMPLERATE_44100) { + if (a2dp_aptx_source_caps.sampleRate & A2DP_APTX_SAMPLERATE_44100) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100; } - if (a2dp_aptx_caps.sampleRate & A2DP_APTX_SAMPLERATE_48000) { + if (a2dp_aptx_source_caps.sampleRate & A2DP_APTX_SAMPLERATE_48000) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000; } - codec_local_capability_.bits_per_sample = a2dp_aptx_caps.bits_per_sample; - if (a2dp_aptx_caps.channelMode & A2DP_APTX_CHANNELS_MONO) { + codec_local_capability_.bits_per_sample = + a2dp_aptx_source_caps.bits_per_sample; + if (a2dp_aptx_source_caps.channelMode & A2DP_APTX_CHANNELS_MONO) { codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; } - if (a2dp_aptx_caps.channelMode & A2DP_APTX_CHANNELS_STEREO) { + if (a2dp_aptx_source_caps.channelMode & A2DP_APTX_CHANNELS_STEREO) { codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; } } @@ -597,7 +598,7 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability, uint8_t* p_result_codec_config) { std::lock_guard<std::recursive_mutex> lock(codec_mutex_); - tA2DP_APTX_CIE sink_info_cie; + tA2DP_APTX_CIE peer_info_cie; tA2DP_APTX_CIE result_config_cie; uint8_t sampleRate; uint8_t channelMode; @@ -619,9 +620,9 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, sizeof(ota_codec_peer_config_)); tA2DP_STATUS status = - A2DP_ParseInfoAptx(&sink_info_cie, p_peer_codec_info, is_capability); + A2DP_ParseInfoAptx(&peer_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", + LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d", __func__, status); goto fail; } @@ -630,13 +631,13 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, // Build the preferred configuration // memset(&result_config_cie, 0, sizeof(result_config_cie)); - result_config_cie.vendorId = a2dp_aptx_caps.vendorId; - result_config_cie.codecId = a2dp_aptx_caps.codecId; + result_config_cie.vendorId = a2dp_aptx_source_caps.vendorId; + result_config_cie.codecId = a2dp_aptx_source_caps.codecId; // // Select the sample frequency // - sampleRate = a2dp_aptx_caps.sampleRate & sink_info_cie.sampleRate; + sampleRate = a2dp_aptx_source_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: @@ -693,7 +694,7 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, // No user preference - try the default config if (select_best_sample_rate( - a2dp_aptx_default_config.sampleRate & sink_info_cie.sampleRate, + a2dp_aptx_default_config.sampleRate & peer_info_cie.sampleRate, &result_config_cie, &codec_config_)) { break; } @@ -706,9 +707,10 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, } while (false); if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) { LOG_ERROR(LOG_TAG, - "%s: cannot match sample frequency: source caps = 0x%x " - "sink info = 0x%x", - __func__, a2dp_aptx_caps.sampleRate, sink_info_cie.sampleRate); + "%s: cannot match sample frequency: local caps = 0x%x " + "peer info = 0x%x", + __func__, a2dp_aptx_source_caps.sampleRate, + peer_info_cie.sampleRate); goto fail; } @@ -735,7 +737,7 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, do { // Compute the selectable capability codec_selectable_capability_.bits_per_sample = - a2dp_aptx_caps.bits_per_sample; + a2dp_aptx_source_caps.bits_per_sample; if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) break; @@ -769,7 +771,7 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, // // Select the channel mode // - channelMode = a2dp_aptx_caps.channelMode & sink_info_cie.channelMode; + channelMode = a2dp_aptx_source_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: @@ -820,7 +822,7 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, // No user preference - try the default config if (select_best_channel_mode( - a2dp_aptx_default_config.channelMode & sink_info_cie.channelMode, + a2dp_aptx_default_config.channelMode & peer_info_cie.channelMode, &result_config_cie, &codec_config_)) { break; } @@ -833,17 +835,20 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, } while (false); if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) { LOG_ERROR(LOG_TAG, - "%s: cannot match channel mode: source caps = 0x%x " - "sink info = 0x%x", - __func__, a2dp_aptx_caps.channelMode, sink_info_cie.channelMode); + "%s: cannot match channel mode: local caps = 0x%x " + "peer info = 0x%x", + __func__, a2dp_aptx_source_caps.channelMode, + peer_info_cie.channelMode); goto fail; } // // Set the rest of the fields as bit-wise AND operation // - result_config_cie.future1 = a2dp_aptx_caps.future1 & sink_info_cie.future1; - result_config_cie.future2 = a2dp_aptx_caps.future2 & sink_info_cie.future2; + result_config_cie.future1 = + a2dp_aptx_source_caps.future1 & peer_info_cie.future1; + result_config_cie.future2 = + a2dp_aptx_source_caps.future2 & peer_info_cie.future2; if (A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie, p_result_codec_config) != A2DP_SUCCESS) { @@ -865,10 +870,10 @@ bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info, // Create a local copy of the peer codec capability/config, and the // result codec config. if (is_capability) { - status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie, + status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, ota_codec_peer_capability_); } else { - status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie, + status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, ota_codec_peer_config_); } CHECK(status == A2DP_SUCCESS); @@ -892,3 +897,64 @@ fail: sizeof(ota_codec_peer_config_)); return false; } + +bool A2dpCodecConfigAptx::setPeerCodecCapabilities( + const uint8_t* p_peer_codec_capabilities) { + std::lock_guard<std::recursive_mutex> lock(codec_mutex_); + tA2DP_APTX_CIE peer_info_cie; + uint8_t sampleRate; + uint8_t channelMode; + + // Save the internal state + btav_a2dp_codec_config_t saved_codec_selectable_capability = + codec_selectable_capability_; + uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE]; + memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_, + sizeof(ota_codec_peer_capability_)); + + tA2DP_STATUS status = + A2DP_ParseInfoAptx(&peer_info_cie, p_peer_codec_capabilities, true); + if (status != A2DP_SUCCESS) { + LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d", + __func__, status); + goto fail; + } + + // Compute the selectable capability - sample rate + sampleRate = a2dp_aptx_source_caps.sampleRate & peer_info_cie.sampleRate; + if (sampleRate & A2DP_APTX_SAMPLERATE_44100) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + } + if (sampleRate & A2DP_APTX_SAMPLERATE_48000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + } + + // Compute the selectable capability - bits per sample + codec_selectable_capability_.bits_per_sample = + a2dp_aptx_source_caps.bits_per_sample; + + // Compute the selectable capability - channel mode + channelMode = a2dp_aptx_source_caps.channelMode & peer_info_cie.channelMode; + if (channelMode & A2DP_APTX_CHANNELS_MONO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + } + if (channelMode & A2DP_APTX_CHANNELS_STEREO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_capability_); + CHECK(status == A2DP_SUCCESS); + return true; + +fail: + // Restore the internal state + codec_selectable_capability_ = saved_codec_selectable_capability; + memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability, + sizeof(ota_codec_peer_capability_)); + return false; +} diff --git a/system/stack/a2dp/a2dp_vendor_aptx_hd.cc b/system/stack/a2dp/a2dp_vendor_aptx_hd.cc index 3a85b72ea02..798e4fd218d 100644 --- a/system/stack/a2dp/a2dp_vendor_aptx_hd.cc +++ b/system/stack/a2dp/a2dp_vendor_aptx_hd.cc @@ -51,7 +51,7 @@ typedef struct { } tA2DP_APTX_HD_CIE; /* aptX-HD Source codec capabilities */ -static const tA2DP_APTX_HD_CIE a2dp_aptx_hd_caps = { +static const tA2DP_APTX_HD_CIE a2dp_aptx_hd_source_caps = { A2DP_APTX_HD_VENDOR_ID, /* vendorId */ A2DP_APTX_HD_CODEC_ID_BLUETOOTH, /* codecId */ (A2DP_APTX_HD_SAMPLERATE_44100 | @@ -418,7 +418,7 @@ btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexAptxHd( const char* A2DP_VendorCodecIndexStrAptxHd(void) { return "aptX-HD"; } bool A2DP_VendorInitCodecConfigAptxHd(AvdtpSepConfig* p_cfg) { - if (A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_hd_caps, + if (A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_hd_source_caps, p_cfg->codec_info) != A2DP_SUCCESS) { return false; } @@ -439,17 +439,18 @@ A2dpCodecConfigAptxHd::A2dpCodecConfigAptxHd( : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD, "aptX-HD", codec_priority) { // Compute the local capability - if (a2dp_aptx_hd_caps.sampleRate & A2DP_APTX_HD_SAMPLERATE_44100) { + if (a2dp_aptx_hd_source_caps.sampleRate & A2DP_APTX_HD_SAMPLERATE_44100) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100; } - if (a2dp_aptx_hd_caps.sampleRate & A2DP_APTX_HD_SAMPLERATE_48000) { + if (a2dp_aptx_hd_source_caps.sampleRate & A2DP_APTX_HD_SAMPLERATE_48000) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000; } - codec_local_capability_.bits_per_sample = a2dp_aptx_hd_caps.bits_per_sample; - if (a2dp_aptx_hd_caps.channelMode & A2DP_APTX_HD_CHANNELS_MONO) { + codec_local_capability_.bits_per_sample = + a2dp_aptx_hd_source_caps.bits_per_sample; + if (a2dp_aptx_hd_source_caps.channelMode & A2DP_APTX_HD_CHANNELS_MONO) { codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; } - if (a2dp_aptx_hd_caps.channelMode & A2DP_APTX_HD_CHANNELS_STEREO) { + if (a2dp_aptx_hd_source_caps.channelMode & A2DP_APTX_HD_CHANNELS_STEREO) { codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; } } @@ -614,7 +615,7 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability, uint8_t* p_result_codec_config) { std::lock_guard<std::recursive_mutex> lock(codec_mutex_); - tA2DP_APTX_HD_CIE sink_info_cie; + tA2DP_APTX_HD_CIE peer_info_cie; tA2DP_APTX_HD_CIE result_config_cie; uint8_t sampleRate; uint8_t channelMode; @@ -636,9 +637,9 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, sizeof(ota_codec_peer_config_)); tA2DP_STATUS status = - A2DP_ParseInfoAptxHd(&sink_info_cie, p_peer_codec_info, is_capability); + A2DP_ParseInfoAptxHd(&peer_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", + LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d", __func__, status); goto fail; } @@ -647,13 +648,13 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, // Build the preferred configuration // memset(&result_config_cie, 0, sizeof(result_config_cie)); - result_config_cie.vendorId = a2dp_aptx_hd_caps.vendorId; - result_config_cie.codecId = a2dp_aptx_hd_caps.codecId; + result_config_cie.vendorId = a2dp_aptx_hd_source_caps.vendorId; + result_config_cie.codecId = a2dp_aptx_hd_source_caps.codecId; // // Select the sample frequency // - sampleRate = a2dp_aptx_hd_caps.sampleRate & sink_info_cie.sampleRate; + sampleRate = a2dp_aptx_hd_source_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: @@ -710,7 +711,7 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, // No user preference - try the default config if (select_best_sample_rate( - a2dp_aptx_hd_default_config.sampleRate & sink_info_cie.sampleRate, + a2dp_aptx_hd_default_config.sampleRate & peer_info_cie.sampleRate, &result_config_cie, &codec_config_)) { break; } @@ -723,9 +724,10 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, } while (false); if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) { LOG_ERROR(LOG_TAG, - "%s: cannot match sample frequency: source caps = 0x%x " - "sink info = 0x%x", - __func__, a2dp_aptx_hd_caps.sampleRate, sink_info_cie.sampleRate); + "%s: cannot match sample frequency: local caps = 0x%x " + "peer info = 0x%x", + __func__, a2dp_aptx_hd_source_caps.sampleRate, + peer_info_cie.sampleRate); goto fail; } @@ -752,7 +754,7 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, do { // Compute the selectable capability codec_selectable_capability_.bits_per_sample = - a2dp_aptx_hd_caps.bits_per_sample; + a2dp_aptx_hd_source_caps.bits_per_sample; if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) break; @@ -786,7 +788,8 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, // // Select the channel mode // - channelMode = a2dp_aptx_hd_caps.channelMode & sink_info_cie.channelMode; + channelMode = + a2dp_aptx_hd_source_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: @@ -837,7 +840,7 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, // No user preference - try the default config if (select_best_channel_mode( - a2dp_aptx_hd_default_config.channelMode & sink_info_cie.channelMode, + a2dp_aptx_hd_default_config.channelMode & peer_info_cie.channelMode, &result_config_cie, &codec_config_)) { break; } @@ -850,10 +853,10 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, } while (false); if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) { LOG_ERROR(LOG_TAG, - "%s: cannot match channel mode: source caps = 0x%x " - "sink info = 0x%x", - __func__, a2dp_aptx_hd_caps.channelMode, - sink_info_cie.channelMode); + "%s: cannot match channel mode: local caps = 0x%x " + "peer info = 0x%x", + __func__, a2dp_aptx_hd_source_caps.channelMode, + peer_info_cie.channelMode); goto fail; } @@ -861,17 +864,17 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, // Set the rest of the fields as bit-wise AND operation // result_config_cie.acl_sprint_reserved0 = - a2dp_aptx_hd_caps.acl_sprint_reserved0 & - sink_info_cie.acl_sprint_reserved0; + a2dp_aptx_hd_source_caps.acl_sprint_reserved0 & + peer_info_cie.acl_sprint_reserved0; result_config_cie.acl_sprint_reserved1 = - a2dp_aptx_hd_caps.acl_sprint_reserved1 & - sink_info_cie.acl_sprint_reserved1; + a2dp_aptx_hd_source_caps.acl_sprint_reserved1 & + peer_info_cie.acl_sprint_reserved1; result_config_cie.acl_sprint_reserved2 = - a2dp_aptx_hd_caps.acl_sprint_reserved2 & - sink_info_cie.acl_sprint_reserved2; + a2dp_aptx_hd_source_caps.acl_sprint_reserved2 & + peer_info_cie.acl_sprint_reserved2; result_config_cie.acl_sprint_reserved3 = - a2dp_aptx_hd_caps.acl_sprint_reserved3 & - sink_info_cie.acl_sprint_reserved3; + a2dp_aptx_hd_source_caps.acl_sprint_reserved3 & + peer_info_cie.acl_sprint_reserved3; if (A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie, p_result_codec_config) != A2DP_SUCCESS) { @@ -893,10 +896,10 @@ bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info, // Create a local copy of the peer codec capability/config, and the // result codec config. if (is_capability) { - status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie, + status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, ota_codec_peer_capability_); } else { - status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie, + status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, ota_codec_peer_config_); } CHECK(status == A2DP_SUCCESS); @@ -919,3 +922,65 @@ fail: sizeof(ota_codec_peer_config_)); return false; } + +bool A2dpCodecConfigAptxHd::setPeerCodecCapabilities( + const uint8_t* p_peer_codec_capabilities) { + std::lock_guard<std::recursive_mutex> lock(codec_mutex_); + tA2DP_APTX_HD_CIE peer_info_cie; + uint8_t sampleRate; + uint8_t channelMode; + + // Save the internal state + btav_a2dp_codec_config_t saved_codec_selectable_capability = + codec_selectable_capability_; + uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE]; + memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_, + sizeof(ota_codec_peer_capability_)); + + tA2DP_STATUS status = + A2DP_ParseInfoAptxHd(&peer_info_cie, p_peer_codec_capabilities, true); + if (status != A2DP_SUCCESS) { + LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d", + __func__, status); + goto fail; + } + + // Compute the selectable capability - sample rate + sampleRate = a2dp_aptx_hd_source_caps.sampleRate & peer_info_cie.sampleRate; + if (sampleRate & A2DP_APTX_HD_SAMPLERATE_44100) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + } + if (sampleRate & A2DP_APTX_HD_SAMPLERATE_48000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + } + + // Compute the selectable capability - bits per sample + codec_selectable_capability_.bits_per_sample = + a2dp_aptx_hd_source_caps.bits_per_sample; + + // Compute the selectable capability - channel mode + channelMode = + a2dp_aptx_hd_source_caps.channelMode & peer_info_cie.channelMode; + if (channelMode & A2DP_APTX_HD_CHANNELS_MONO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + } + if (channelMode & A2DP_APTX_HD_CHANNELS_STEREO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_capability_); + CHECK(status == A2DP_SUCCESS); + return true; + +fail: + // Restore the internal state + codec_selectable_capability_ = saved_codec_selectable_capability; + memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability, + sizeof(ota_codec_peer_capability_)); + return false; +} diff --git a/system/stack/a2dp/a2dp_vendor_ldac.cc b/system/stack/a2dp/a2dp_vendor_ldac.cc index 5eb0c7d8423..266db81a897 100644 --- a/system/stack/a2dp/a2dp_vendor_ldac.cc +++ b/system/stack/a2dp/a2dp_vendor_ldac.cc @@ -48,7 +48,7 @@ typedef struct { } tA2DP_LDAC_CIE; /* LDAC Source codec capabilities */ -static const tA2DP_LDAC_CIE a2dp_ldac_caps = { +static const tA2DP_LDAC_CIE a2dp_ldac_source_caps = { A2DP_LDAC_VENDOR_ID, // vendorId A2DP_LDAC_CODEC_ID, // codecId // sampleRate @@ -501,7 +501,7 @@ btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexLdac( const char* A2DP_VendorCodecIndexStrLdac(void) { return "LDAC"; } bool A2DP_VendorInitCodecConfigLdac(AvdtpSepConfig* p_cfg) { - if (A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_ldac_caps, + if (A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_ldac_source_caps, p_cfg->codec_info) != A2DP_SUCCESS) { return false; } @@ -547,32 +547,33 @@ A2dpCodecConfigLdac::A2dpCodecConfigLdac( : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, "LDAC", codec_priority) { // Compute the local capability - if (a2dp_ldac_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_44100) { + if (a2dp_ldac_source_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_44100) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100; } - if (a2dp_ldac_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_48000) { + if (a2dp_ldac_source_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_48000) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000; } - if (a2dp_ldac_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_88200) { + if (a2dp_ldac_source_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_88200) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_88200; } - if (a2dp_ldac_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_96000) { + if (a2dp_ldac_source_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_96000) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_96000; } - if (a2dp_ldac_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_176400) { + if (a2dp_ldac_source_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_176400) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_176400; } - if (a2dp_ldac_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_192000) { + if (a2dp_ldac_source_caps.sampleRate & A2DP_LDAC_SAMPLING_FREQ_192000) { codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_192000; } - codec_local_capability_.bits_per_sample = a2dp_ldac_caps.bits_per_sample; - if (a2dp_ldac_caps.channelMode & A2DP_LDAC_CHANNEL_MODE_MONO) { + codec_local_capability_.bits_per_sample = + a2dp_ldac_source_caps.bits_per_sample; + if (a2dp_ldac_source_caps.channelMode & A2DP_LDAC_CHANNEL_MODE_MONO) { codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; } - if (a2dp_ldac_caps.channelMode & A2DP_LDAC_CHANNEL_MODE_STEREO) { + if (a2dp_ldac_source_caps.channelMode & A2DP_LDAC_CHANNEL_MODE_STEREO) { codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; } - if (a2dp_ldac_caps.channelMode & A2DP_LDAC_CHANNEL_MODE_DUAL) { + if (a2dp_ldac_source_caps.channelMode & A2DP_LDAC_CHANNEL_MODE_DUAL) { codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; } } @@ -825,7 +826,7 @@ 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 peer_info_cie; tA2DP_LDAC_CIE result_config_cie; uint8_t channelMode; uint8_t sampleRate; @@ -848,9 +849,9 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, sizeof(ota_codec_peer_config_)); tA2DP_STATUS status = - A2DP_ParseInfoLdac(&sink_info_cie, p_peer_codec_info, is_capability); + A2DP_ParseInfoLdac(&peer_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", + LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d", __func__, status); goto fail; } @@ -859,13 +860,13 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, // 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; + result_config_cie.vendorId = a2dp_ldac_source_caps.vendorId; + result_config_cie.codecId = a2dp_ldac_source_caps.codecId; // // Select the sample frequency // - sampleRate = a2dp_ldac_caps.sampleRate & sink_info_cie.sampleRate; + sampleRate = a2dp_ldac_source_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: @@ -969,7 +970,7 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, // No user preference - try the default config if (select_best_sample_rate( - a2dp_ldac_default_config.sampleRate & sink_info_cie.sampleRate, + a2dp_ldac_default_config.sampleRate & peer_info_cie.sampleRate, &result_config_cie, &codec_config_)) { break; } @@ -982,9 +983,10 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, } 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); + "%s: cannot match sample frequency: local caps = 0x%x " + "peer info = 0x%x", + __func__, a2dp_ldac_source_caps.sampleRate, + peer_info_cie.sampleRate); goto fail; } @@ -993,7 +995,7 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, // // 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; + bits_per_sample = a2dp_ldac_source_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: @@ -1028,7 +1030,7 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, do { // Compute the selectable capability codec_selectable_capability_.bits_per_sample = - a2dp_ldac_caps.bits_per_sample; + a2dp_ldac_source_caps.bits_per_sample; if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) break; @@ -1038,7 +1040,7 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, // No user preference - the the codec audio config if (select_audio_bits_per_sample(&codec_audio_config_, - a2dp_ldac_caps.bits_per_sample, + a2dp_ldac_source_caps.bits_per_sample, &result_config_cie, &codec_config_)) { break; } @@ -1050,7 +1052,7 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, } // No user preference - use the best match - if (select_best_bits_per_sample(a2dp_ldac_caps.bits_per_sample, + if (select_best_bits_per_sample(a2dp_ldac_source_caps.bits_per_sample, &result_config_cie, &codec_config_)) { break; } @@ -1067,7 +1069,7 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, // // Select the channel mode // - channelMode = a2dp_ldac_caps.channelMode & sink_info_cie.channelMode; + channelMode = a2dp_ldac_source_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: @@ -1131,7 +1133,7 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, // No user preference - try the default config if (select_best_channel_mode( - a2dp_ldac_default_config.channelMode & sink_info_cie.channelMode, + a2dp_ldac_default_config.channelMode & peer_info_cie.channelMode, &result_config_cie, &codec_config_)) { break; } @@ -1144,9 +1146,10 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, } 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); + "%s: cannot match channel mode: local caps = 0x%x " + "peer info = 0x%x", + __func__, a2dp_ldac_source_caps.channelMode, + peer_info_cie.channelMode); goto fail; } @@ -1170,10 +1173,10 @@ bool A2dpCodecConfigLdac::setCodecConfig(const uint8_t* p_peer_codec_info, // 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, + status = A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, ota_codec_peer_capability_); } else { - status = A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie, + status = A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, ota_codec_peer_config_); } CHECK(status == A2DP_SUCCESS); @@ -1196,3 +1199,84 @@ fail: sizeof(ota_codec_peer_config_)); return false; } + +bool A2dpCodecConfigLdac::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; + + // Save the internal state + btav_a2dp_codec_config_t saved_codec_selectable_capability = + codec_selectable_capability_; + uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE]; + memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_, + sizeof(ota_codec_peer_capability_)); + + tA2DP_STATUS status = + A2DP_ParseInfoLdac(&peer_info_cie, p_peer_codec_capabilities, true); + if (status != A2DP_SUCCESS) { + LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d", + __func__, status); + goto fail; + } + + // Compute the selectable capability - sample rate + sampleRate = a2dp_ldac_source_caps.sampleRate & peer_info_cie.sampleRate; + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_44100) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_44100; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_48000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_88200) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_88200; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_96000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_96000; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_176400) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_176400; + } + if (sampleRate & A2DP_LDAC_SAMPLING_FREQ_192000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_192000; + } + + // Compute the selectable capability - bits per sample + codec_selectable_capability_.bits_per_sample = + a2dp_ldac_source_caps.bits_per_sample; + + // Compute the selectable capability - channel mode + channelMode = a2dp_ldac_source_caps.channelMode & peer_info_cie.channelMode; + if (channelMode & A2DP_LDAC_CHANNEL_MODE_MONO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + } + if (channelMode & A2DP_LDAC_CHANNEL_MODE_STEREO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + if (channelMode & A2DP_LDAC_CHANNEL_MODE_DUAL) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + status = A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_capability_); + CHECK(status == A2DP_SUCCESS); + return true; + +fail: + // Restore the internal state + codec_selectable_capability_ = saved_codec_selectable_capability; + memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability, + sizeof(ota_codec_peer_capability_)); + return false; +} diff --git a/system/stack/include/a2dp_aac.h b/system/stack/include/a2dp_aac.h index 6b0be673893..45849669bcc 100644 --- a/system/stack/include/a2dp_aac.h +++ b/system/stack/include/a2dp_aac.h @@ -35,6 +35,8 @@ class A2dpCodecConfigAacBase : public A2dpCodecConfig { 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 diff --git a/system/stack/include/a2dp_codec_api.h b/system/stack/include/a2dp_codec_api.h index 40742bf7a3b..38a2089731f 100644 --- a/system/stack/include/a2dp_codec_api.h +++ b/system/stack/include/a2dp_codec_api.h @@ -191,6 +191,12 @@ class A2dpCodecConfig { bool* p_restart_input, bool* p_restart_output, bool* p_config_updated) = 0; + // Sets the codec capabilities for a peer. + // |p_peer_codec_capabiltities| is the peer codec capabilities to set. + // Returns true on success, otherwise false. + virtual bool setPeerCodecCapabilities( + const uint8_t* p_peer_codec_capabilities) = 0; + // Constructor where |codec_index| is the unique index that identifies the // codec. The user-friendly name is |name|. // The default codec priority is |codec_priority|. If the value is @@ -414,6 +420,16 @@ class A2dpCodecs { uint8_t* p_result_codec_config, bool* p_restart_input, bool* p_restart_output, bool* p_config_updated); + // Sets the codec capabilities for a Sink peer. + // |p_peer_codec_capabiltities| is the peer codec capabilities to set. + // Returns true on success, otherwise false. + bool setPeerSinkCodecCapabilities(const uint8_t* p_peer_codec_capabilities); + + // Sets the codec capabilities for a Source peer. + // |p_peer_codec_capabiltities| is the peer codec capabilities to set. + // Returns true on success, otherwise false. + bool setPeerSourceCodecCapabilities(const uint8_t* p_peer_codec_capabilities); + // Gets the current codec configuration and the capabilities of // all configured codecs. // The current codec configuration is stored in |p_codec_config|. diff --git a/system/stack/include/a2dp_sbc.h b/system/stack/include/a2dp_sbc.h index ad37d452eb0..6f601b49a71 100644 --- a/system/stack/include/a2dp_sbc.h +++ b/system/stack/include/a2dp_sbc.h @@ -35,6 +35,8 @@ class A2dpCodecConfigSbcBase : public A2dpCodecConfig { 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 diff --git a/system/stack/include/a2dp_vendor_aptx.h b/system/stack/include/a2dp_vendor_aptx.h index 516814ea9ae..bc1ab56c2b0 100644 --- a/system/stack/include/a2dp_vendor_aptx.h +++ b/system/stack/include/a2dp_vendor_aptx.h @@ -34,6 +34,8 @@ class A2dpCodecConfigAptx : public A2dpCodecConfig { period_ms_t encoderIntervalMs() 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; diff --git a/system/stack/include/a2dp_vendor_aptx_hd.h b/system/stack/include/a2dp_vendor_aptx_hd.h index b343fd1fd6a..1276097f082 100644 --- a/system/stack/include/a2dp_vendor_aptx_hd.h +++ b/system/stack/include/a2dp_vendor_aptx_hd.h @@ -34,6 +34,8 @@ class A2dpCodecConfigAptxHd : public A2dpCodecConfig { period_ms_t encoderIntervalMs() 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; diff --git a/system/stack/include/a2dp_vendor_ldac.h b/system/stack/include/a2dp_vendor_ldac.h index 3d3f81c194d..3f8c3b58cc4 100644 --- a/system/stack/include/a2dp_vendor_ldac.h +++ b/system/stack/include/a2dp_vendor_ldac.h @@ -34,6 +34,8 @@ class A2dpCodecConfigLdac : public A2dpCodecConfig { period_ms_t encoderIntervalMs() 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; -- GitLab