A2DP Hardware Offload

这篇具有很好参考价值的文章主要介绍了A2DP Hardware Offload。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

关于A2DP硬件卸载功能,描述可以看https://source.android.com/docs/core/connect/bluetooth/hci_requirements#a2dp-hardware-offload-support。

如我在Android Bluetooth A2DP_阅后即奋的博客-CSDN博客中的3.2.7节所述,Audio Stream通过Audio处理器直接发给了BT控制器。

1. 功能开关

1.1 UI开关

继续以Android手机为例,该功能的开关,可以开发者选项中看到开关。

A2DP Hardware Offload

 默认地,停用蓝牙A2DP硬件卸载功能是关闭的,双重否定即肯定,那么这里的意思就是默认支持A2DP Hardware Offload功能,也就是前面所述Audio Stream通过Audio处理器直接发给了BT控制器。

1.2 Code Check

判断是否使能A2DP Hardware Offload功能。

// packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java

    // a2dp offload是否禁用
    private static final String A2DP_OFFLOAD_DISABLED_PROPERTY =
            "persist.bluetooth.a2dp_offload.disabled";
    // a2dp offload是否支持
    private static final String A2DP_OFFLOAD_SUPPORTED_PROPERTY =
            "ro.bluetooth.a2dp_offload.supported";
    // 请斟酌使能和支持的区别

    public void init(RemoteDevices remoteDevices) {
        // ......

        mA2dpOffloadEnabled =
                SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false)
                && !SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);

        // ......
    }

    /**
     * @return A2DP offload support
     */
    boolean isA2dpOffloadEnabled() {
        return mA2dpOffloadEnabled;
    }

2. 日志分析。

资源下载:https://download.csdn.net/download/hihan_5/87108298

下面的日志,表示Audio Source设备不支持Bluetooth Broadcast Audio profile。

11-21 16:15:17.255  1002 13479 13515 D A2dpService:  setActiveDevice: BA active false
11-21 16:15:17.255  1002 13479 13515 D A2dpService: Switch A2DP devices to CC:98:8B:57:23:39 from null
11-21 16:15:17.255  1002 13479 13515 W A2dpService: setActiveDevice coming out of mutex lock
11-21 16:15:17.256  1002 13479 13515 I BluetoothA2dpServiceJni: setActiveDeviceNative: sBluetoothA2dpInterface: 0x7d043e3d30
// packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java
// vendor/qcom/opensource/commonsys/packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java
    private boolean setActiveDeviceInternal(BluetoothDevice device) {
        // .......
        try {
            mA2dpNativeInterfaceLock.readLock().lock();
            if (mA2dpNativeInterface != null && !mA2dpNativeInterface.setActiveDevice(device)) {
                Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer");
                return false;
            }
        } finally {
            mA2dpNativeInterfaceLock.readLock().unlock();
        }
        // ......
    }

mA2dpNativeInterface.setActiveDevice(device)调用Native函数。

// packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp
// vendor/qcom/opensource/commonsys/packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp
static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
                                      jbyteArray address) {
  ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sBluetoothA2dpInterface) {
    ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__);
    return JNI_FALSE;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);

  RawAddress bd_addr = RawAddress::kEmpty;
  if (addr) {
    bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
  }
  bt_status_t status = sBluetoothA2dpInterface->set_active_device(bd_addr);
  if (status != BT_STATUS_SUCCESS) {
    ALOGE("%s: Failed A2DP set_active_device, status: %d", __func__, status);
  }
  env->ReleaseByteArrayElements(address, addr, 0);
  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

static void initNative(JNIEnv* env, jobject object,
                       jint maxConnectedAudioDevices,
                       jobjectArray codecConfigArray,
                       jobjectArray codecConfigOffload) {
// ......
  const bt_interface_t* btInf = getBluetoothInterface();
  if (btInf == nullptr) {
    ALOGE("%s: Bluetooth module is not loaded", __func__);
    return;
  }
// ......
  sBluetoothA2dpInterface =
      (btav_source_interface_t *)btInf->get_profile_interface(
          BT_PROFILE_ADVANCED_AUDIO_ID);
// ......
  bt_status_t status = sBluetoothA2dpInterface->init(
      &sBluetoothA2dpCallbacks, maxConnectedAudioDevices, codec_priorities,
      codec_offloading);
// ......
}

这里面重点是找到sBluetoothA2dpInterface指向谁?JNI函数初始化时会执行initNative,这里进行了sBluetoothA2dpInterface的赋值,然后执行了init函数。

先看btInf的指向。

// packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp
// vendor/qcom/opensource/commonsys/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp
const bt_interface_t* getBluetoothInterface() { return sBluetoothInterface; }

static void classInitNative(JNIEnv* env, jclass clazz) {
//......
  if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) {
    ALOGE("No Bluetooth Library found");
  }
}


int hal_util_load_bt_library(const bt_interface_t** interface) {
  // #define BLUETOOTH_INTERFACE_STRING "bluetoothInterface"
  const char* sym = BLUETOOTH_INTERFACE_STRING;
  bt_interface_t* itf = nullptr;

  // The library name is not set by default, so the preset library name is used.
  char path[PROPERTY_VALUE_MAX] = "";
  // 高通机型,这里加载了libbluetooth_qti.so
  // 非高通机型,例如MTK,加载libbluetooth.so
  property_get(PROPERTY_BT_LIBRARY_NAME, path, DEFAULT_BT_LIBRARY_NAME);
  void* handle = dlopen(path, RTLD_NOW);
  if (!handle) {
    const char* err_str = dlerror();
    LOG(ERROR) << __func__ << ": failed to load Bluetooth library, error="
               << (err_str ? err_str : "error unknown");
    goto error;
  }

  // Get the address of the bt_interface_t.
  itf = (bt_interface_t*)dlsym(handle, sym);
  if (!itf) {
    LOG(ERROR) << __func__ << ": failed to load symbol from Bluetooth library "
               << sym;
    goto error;
  }

  // Success.
  LOG(INFO) << __func__ << " loaded HAL: btinterface=" << itf
            << ", handle=" << handle;
  *interface = itf;
  return 0;

error:
  *interface = NULL;
  if (handle) dlclose(handle);

  return -EINVAL;
}

拿到全局变量bluetoothInterface的地址,即btInf指向该地址。

// system/bt/btif/src/bluetooth.cc
// vendor/qcom/opensource/commonsys/system/bt/btif/src/bluetooth.cc
EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
    sizeof(bluetoothInterface),
    init,
    enable,
    disable,
    cleanup,
    get_adapter_properties,
    get_adapter_property,
    set_adapter_property,
    get_remote_device_properties,
    get_remote_device_property,
    set_remote_device_property,
    get_remote_service_record,
    get_remote_services,
    start_discovery,
    cancel_discovery,
    create_bond,
    create_bond_out_of_band,
    remove_bond,
    cancel_bond,
    get_connection_state,
    pin_reply,
    ssp_reply,
    get_profile_interface,
    dut_mode_configure,
    dut_mode_send,
    le_test_mode,
    set_os_callouts,
    read_energy_info,
    dump,
    dumpMetrics,
    config_clear,
    interop_database_clear,
    interop_database_add,
    interop_database_name_add,
    get_avrcp_service,
    obfuscate_address,
    get_metric_id,
    set_dynamic_audio_buffer_size,
    generate_local_oob_data,
};

到这明朗了,sBluetoothA2dpInterface指向get_profile_interface返回的地址。

// system/bt/btif/src/bluetooth.cc
// vendor/qcom/opensource/commonsys/system/bt/btif/src/bluetooth.cc
static const void* get_profile_interface(const char* profile_id) {
  // ......
  if (is_profile(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
    return btif_av_get_src_interface();
  // ......
}

 最终,sBluetoothA2dpInterface指向全局变量bt_av_src_interface。

// system/bt/btif/src/btif_av.cc
// vendor/qcom/opensource/commonsys/system/bt/btif/src/btif_av.cc
const btav_source_interface_t* btif_av_get_src_interface(void) {
  BTIF_TRACE_EVENT("%s", __func__);
  return &bt_av_src_interface;
}

static const btav_source_interface_t bt_av_src_interface = {
    sizeof(btav_source_interface_t),
    init_src,
    src_connect_sink,
    src_disconnect_sink,
    set_silence_device,
    set_active_device,
#ifdef MULTI_A2DP_ENABLE
    enable_multi_a2dp,
    disable_multi_a2dp,
    set_multi_a2dp_device,
    get_multi_a2dp_device,
#endif
    codec_config_src,
    cleanup_src,
#ifdef BT_AV_SHO_FEATURE
    allow_connection,
    select_audio_device,
#endif
};

sBluetoothA2dpInterface赋值后,就进行了init初始化动作。

  bt_status_t status = sBluetoothA2dpInterface->init(
      &sBluetoothA2dpCallbacks, maxConnectedAudioDevices, codec_priorities,
      codec_offloading);
// vendor/qcom/opensource/commonsys/system/bt/btif/src/btif_av.cc
/*******************************************************************************
 *
 * Function         init_src
 *
 * Description      Initializes the AV interface for source mode
 *
 * Returns          bt_status_t
 *
 ******************************************************************************/
static bt_status_t init_src(
    btav_source_callbacks_t* callbacks,
    const std::vector<btav_a2dp_codec_config_t> &codec_priorities,
    const std::vector<btav_a2dp_codec_config_t> &offload_enabled_codecs,
    int max_a2dp_connections, int a2dp_multicast_state) {
  bt_status_t status = BT_STATUS_FAIL;
  BTIF_TRACE_EVENT("%s() with max conn = %d", __func__, max_a2dp_connections);
  char value[PROPERTY_VALUE_MAX] = {'\0'};

#if (OFF_TARGET_TEST_ENABLED == FALSE)
  // 这部分的代码,就要去看到vendor/qcom/opensource/commonsys/system/bt/device/src/controller.cc
  // start_up函数进行了变量的赋值
  bt_split_a2dp_enabled = controller_get_interface()->supports_spilt_a2dp();
#else
  bt_split_a2dp_enabled = false;
#endif
  btif_av_update_multicast_state(0);
  BTIF_TRACE_DEBUG("split_a2dp_status = %d",bt_split_a2dp_enabled);
  osi_property_get("persist.vendor.btstack.twsplus.defaultchannelmode",
                                    value, "mono");
  BTIF_TRACE_DEBUG("tws default channel mode = %s",value);
  tws_defaultmono_supported = (strcmp(value, "mono") == 0);
  BTIF_TRACE_DEBUG("default mono channel mode = %d",tws_defaultmono_supported);
  offload_enabled_codecs_config_ = offload_enabled_codecs;
  codec_priorities_ = codec_priorities;
#if (TWS_STATE_ENABLED == TRUE)
  //osi_property_get("persist.vendor.btstack.twsplus.state", value, "false");
  tws_state_supported =
       controller_get_interface()->supports_twsp_remote_state();
#endif
  if (bt_av_sink_callbacks != NULL)
        // already did btif_av_init()
        status = BT_STATUS_SUCCESS;
  else {
    if (a2dp_multicast_state)
      is_multicast_supported = true;
    btif_max_av_clients = max_a2dp_connections;
    BTIF_TRACE_EVENT("%s() with max conn changed to = %d", __func__,
                                btif_max_av_clients);
    if (btif_av_is_split_a2dp_enabled()) {
      btif_a2dp_src_vsc.multi_vsc_support = false;
    }

    for (int i = 0; i < btif_max_av_clients; i++)
      btif_av_cb[i].codec_priorities = codec_priorities;
    if (codec_config_update_enabled != false) {
        BTIF_TRACE_IMP("%s: Codec cfg update enabled changed to false", __func__);
        codec_config_update_enabled = false;
    }
    // btif_av初始化动作
    status = btif_av_init(BTA_A2DP_SOURCE_SERVICE_ID);
    if (status == BT_STATUS_SUCCESS) bt_av_src_callbacks = callbacks;
  }
  return status;
}

如下为蓝牙控制器start_up函数(代码有点长,就不放了,感兴趣的读者自行查阅)打印的日志。 

11-21 16:15:15.120  1002 13479 13545 I bt_controller: start_up:: soc_name:hastings, soc_type = 4
11-21 16:15:15.120  1002 13479 13545 I bt_controller: start_up:: spilt_a2dp_supported = 0
11-21 16:15:15.120  1002 13479 13545 I bt_controller: start_up:: a2dp_offload_Cap = sbc-aptx-aptxtws-aptxhd-aac-ldac-aptxadaptiver2
11-21 16:15:15.120  1002 13479 13545 I bt_controller: start_up:: wipower_supported = 0
11-21 16:15:15.120  1002 13479 13545 I bt_controller: start_up:: aac_frame_ctl_enabled = 1
11-21 16:15:15.120  1002 13479 13545 I bt_controller: start_up:: a2dp_multicast_supported = 0
11-21 16:15:15.120  1002 13479 13545 I bt_controller: start_up:: twsp_state_supported = 0
11-21 16:15:15.120  1002 13479 13545 I bt_controller: decode_max_power_values: MAX POW property is not set
11-21 16:15:15.120  1002 13479 13545 I bt_controller: start_up:: max_power_prop_enabled = 0
11-21 16:15:15.128  1002 13479 13545 I bt_controller: start_up Send command to enable soc logging 
11-21 16:15:15.128  1002 13479 13545 I bt_controller: send_soc_log_command for soc_type: 4
11-21 16:15:15.160  1002 13479 13545 D bt_controller: start_up read local simple pairing options
11-21 16:15:15.161  1002 13479 13545 D bt_controller: start_up simple pairing options is 0x1
11-21 16:15:15.163  1002 13479 13545 D bt_controller: start_up HCI write RF compensation tx value : 0, rx value : 0

如下为init_src日志。

11-21 16:15:15.329  1002 13479 13479 I bt_btif : btif_av_get_src_interface
11-21 16:15:15.330  1002 13479 13479 I bt_btif : init_src() with max conn = 5
11-21 16:15:15.330  1002 13479 13479 D bt_btif : btif_av_update_multicast_state: max_multi_a2dp:0,multicast_disabled:1
11-21 16:15:15.330  1002 13479 13479 E bt_btif : btif_sm_get_state : Invalid handle
11-21 16:15:15.330  1002 13479 13479 D bt_btif : btif_av_get_num_connected_devices: AV Connection count: 0
11-21 16:15:15.330  1002 13479 13479 D bt_btif : split_a2dp_status = 0
11-21 16:15:15.330  1002 13479 13479 D bt_btif : tws default channel mode = mono
11-21 16:15:15.330  1002 13479 13479 D bt_btif : default mono channel mode = 1
11-21 16:15:15.330  1002 13479 13479 I bt_btif : init_src() with max conn changed to = 5

再其后,btif_av_init -> bta_av_co_init -> A2DP_SetOffloadStatus,这里面会进行各个codec offload的设置,后续会用到。

再回到最初的bt_status_t status = sBluetoothA2dpInterface->set_active_device(bd_addr)。set_active_device中,会去判断btif_av_is_split_a2dp_enabled的值

// vendor/qcom/opensource/commonsys/system/bt/btif/src/btif_av.cc
/*******************************************************************************
 *
 * Function         set_active_device
 *
 * Description      Tears down the AV signalling channel with the remote headset
 *
 * Returns          bt_status_t
 *
 ******************************************************************************/
static bt_status_t set_active_device(const RawAddress& bd_addr) {
  CHECK_BTAV_INIT();

  int active_index = btif_av_get_latest_device_idx_to_start();
  int set_active_device_index = btif_av_idx_by_bdaddr(&(RawAddress&)bd_addr);
  int tws_pair_index = btif_max_av_clients;
  BTIF_TRACE_EVENT("%s: active_index: %d, set_active_device_index: %d",
               __func__, active_index, set_active_device_index);
  // ......
  // 在bta_av_core中设置配对设备
  if (!bta_av_co_set_active_peer(bd_addr)) {
    BTIF_TRACE_WARNING("%s: unable to set active peer in BtaAvCo",__func__);
  }
#ifndef SUPPORT_LHDC_CODEC
  if (btif_a2dp_source_is_hal_v2_supported()) {
#else
  if (btif_av_is_split_a2dp_enabled() || btif_av_get_multicast_state() == true) {
#endif
    std::unique_lock<std::mutex> guard(session_wait_mutex_);
    session_wait = false;
    /* Initiate handoff for the device with address in the argument*/
    btif_transfer_context(btif_av_handle_event, BTIF_AV_TRIGGER_HANDOFF_REQ_EVT,
                                 (char *)&bd_addr, sizeof(RawAddress), NULL);
    BTIF_TRACE_EVENT("%s: wating for signal",__func__);
    session_wait_cv.wait_for(guard, std::chrono::milliseconds(1000),
                      []{return session_wait;});
    BTIF_TRACE_EVENT("%s: done with signal",__func__);
    if (!bd_addr.IsEmpty())
      btif_transfer_context(btif_av_handle_event, BTIF_AV_CHECK_PENDING_PLAY_EVT,
                                    (char *)&bd_addr, sizeof(RawAddress), NULL);
    return BT_STATUS_SUCCESS;
  } else {
    /* Initiate handoff for the device with address in the argument*/
    return btif_transfer_context(btif_av_handle_event,
    BTIF_AV_TRIGGER_HANDOFF_REQ_EVT,(char *)&bd_addr, sizeof(RawAddress), NULL);
  }
}


/* SPLITA2DP*/
/*******************************************************************************
 *
 * Function         btif_av_is_split_a2dp_enabled
 *
 * Description      Check if split a2dp is enabled.
 *
 * Returns          TRUE if split a2dp is enabled, FALSE otherwise
 *
 ******************************************************************************/
bool btif_av_is_split_a2dp_enabled() {
  //BTIF_TRACE_DEBUG("btif_a2dp_source_is_hal_v2_supported %d ",
  //                      btif_a2dp_source_is_hal_v2_supported());
  if (is_multicast_supported) {
    //BTIF_TRACE_ERROR("%s,Mulitcast enabled, default non-split mode",__func__);
    return false;
  }
  if (!btif_a2dp_source_is_hal_v2_supported()) {
    BTIF_TRACE_DEBUG("btif_av_is_split_a2dp_enabled: %d", bt_split_a2dp_enabled);
    return bt_split_a2dp_enabled;
  } else if(isBATEnabled()) {
    BTIF_TRACE_DEBUG("%s:  going for split as BA is active", __func__);
    return true;
#ifdef ADV_AUDIO_FEATURE
  } else if (btif_bap_broadcast_is_active()) {
    return true;
#endif
  }else {
    if (!bta_av_co_is_active_peer()) {
      BTIF_TRACE_ERROR("%s:  No active peer codec config found, "
                        "by default splitmode", __func__);
      return true;
    }
    A2dpCodecConfig* a2dpCodecConfig = bta_av_get_a2dp_current_codec();
    if (a2dpCodecConfig == nullptr) {
      BTIF_TRACE_ERROR("%s: active peer set but still no current codec "
          "available, by default splitmode ", __func__);
      return true;
    }

    if(A2DP_IsCodecEnabledInOffload(a2dpCodecConfig->codecIndex())) {
      BTIF_TRACE_DEBUG("%s:  going for split ", __func__);
      return true;
    } else if(A2DP_IsCodecEnabledInSoftware(a2dpCodecConfig->codecIndex())) {
      BTIF_TRACE_DEBUG("%s:  going for non split ", __func__);
      return false;
    } else {
      BTIF_TRACE_ERROR("%s: current codec is not enabled either of modes"
                        " going ahead with split", __func__);
      return true;
    }
  }
}

对应日志如下:

11-21 16:15:17.256  1002 13479 13515 E bt_btif : btif_av_get_latest_device_idx_to_start:No valid active device found
11-21 16:15:17.256  1002 13479 13515 I bt_btif : set_active_device: active_index: 5, set_active_device_index: 0
11-21 16:15:17.256  1002 13479 13515 I bt_btif : set_active_device: set_active_device_index flags: 0
11-21 16:15:17.256  1002 13479 13515 I bt_btif :  btif_ba_get_state BA not iniitlized 
11-21 16:15:17.256  1002 13479 13515 D bt_btif : [BapBroadcast]:btif_bap_broadcast_is_active
11-21 16:15:17.256  1002 13479 13515 E bt_btif : bta_av_co_get_active_peer: active peer index: 0
11-21 16:15:17.256  1002 13479 13515 E bt_btif : bta_av_co_get_active_peer: active peer index: 0
11-21 16:15:17.256  1002 13479 13515 D bt_btif : btif_av_is_split_a2dp_enabled:  going for split 

从上面日志看,A2DP_IsCodecEnabledInOffload函数返回了true。

bool A2DP_IsCodecEnabledInOffload(btav_a2dp_codec_index_t codec_index) {
  bool codec_status = false;
  if (offload_capability) {
    switch (codec_index) {
    case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
      codec_status = sbc_offload;
      break;
    case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
      codec_status = aac_offload;
      break;
    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
      codec_status = aptx_offload;
      break;
    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
      codec_status = aptxhd_offload;
      break;
    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_ADAPTIVE:
      codec_status = aptx_adaptive_offload;
      break;
    case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
      if (!ldac_offload)
          LOG_INFO(LOG_TAG,"LDAC not enabled in offload currently");
      codec_status = ldac_offload;
      break;
    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_TWS:
      codec_status = aptxtws_offload;
      break;
#ifdef SUPPORT_LHDC_CODEC
    case BTAV_A2DP_CODEC_INDEX_SOURCE_LHDCV3:
    case BTAV_A2DP_CODEC_INDEX_SOURCE_LHDCV2:
    case BTAV_A2DP_CODEC_INDEX_SOURCE_LHDCV1:
      if (!lhdc_offload)
          LOG_INFO(LOG_TAG,"LHDC not enabled in offload currently");
      codec_status = lhdc_offload;
      break;
#endif
    case BTAV_A2DP_CODEC_INDEX_SINK_AAC:
    case BTAV_A2DP_QVA_CODEC_INDEX_SOURCE_MAX:
    case BTAV_A2DP_CODEC_INDEX_SINK_MAX:
    default:
      break;
    }
  }
  return codec_status;
}

codec_index的值,是在bta_av_co_init -> A2dpCodecs::init -> A2dpCodecConfig::createCodec中完成了设置。这下面的codec其实就是设备支持的codec。

11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec SBC
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec AAC
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec aptX
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec aptX-HD
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec LDAC
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec aptX-adaptive
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec aptX-TWS
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec UNKNOWN CODEC INDEX
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec UNKNOWN CODEC INDEX
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec UNKNOWN CODEC INDEX
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec UNKNOWN CODEC INDEX
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec UNKNOWN CODEC INDEX
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec LHDC_V2
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec LHDC_V3
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec LHDC_V1
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec SBC SINK
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec UNKNOWN CODEC INDEX
11-21 16:15:15.333  1002 13479 13479 D a2dp_codec: createCodec: codec UNKNOWN CODEC INDEX

btif_av_is_split_a2dp_enabled的返回值为true,则表示支持a2dp offload。

11-21 16:15:17.257  1002 13479 13510 E bt_btif_a2dp_source: btif_a2dp_source_restart_session: old_peer_address=00:00:00:00:00:00 new_peer_address=cc:98:8b:57:23:39 is_streaming=0 state=2
11-21 16:15:17.257  1002 13479 13510 E bt_btif_a2dp_source: btif_a2dp_source_start_session: peer_address=cc:98:8b:57:23:39 state=2
11-21 16:15:17.257  1002 13479 13510 W bt_stack: [WARNING:a2dp_encoding.cc(2609)] init
11-21 16:15:17.257  1002 13479 13510 W bt_stack: [WARNING:a2dp_encoding.cc(2584)] get_hal_version
11-21 16:15:17.259  1002 13479 13510 W bt_stack: [WARNING:a2dp_encoding.cc(2591)] get_hal_version:hal version 2.1
11-21 16:15:17.259  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(1698)] a2dp_get_selected_hal_codec_config_2_1: profile_type: 1
11-21 16:15:17.260  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(2463)] a2dp_get_selected_hal_codec_config_2_1: codec_type\xFF
11-21 16:15:17.261  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(2475)] a2dp_get_selected_hal_codec_config_2_1LDAC bitrate0
11-21 16:15:17.261  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(2526)] a2dp_get_selected_hal_codec_config_2_1: CodecConfiguration={.codecType = LDAC, .encodedAudioBitrate = 0, .peerMtu = 882, .isScmstEnabled = 0, .isScramblingEnabled = 0, .config = {.sbcConfig = {.sampleRate = RATE_96000, .channelMode = JOINT_STEREO, .blockLength = 0x7f, .numSubbands = SUBBAND_8, .allocMethod = 0, .bitsPerSample = BITS_UNKNOWN, .minBitpool = 0, .maxBitpool = 0}, .aacConfig = {.objectType = 0x8, .sampleRate = 0x47f01, .channelMode = UNKNOWN, .variableBitRateEnabled = DISABLED, .bitsPerSample = BITS_UNKNOWN, .frameControlEnabled = 0}, .ldacConfig = {.sampleRate = RATE_96000, .channelMode = STEREO, .qualityIndex = QUALITY_ABR, .bitsPerSample = BITS_32}, .aptxConfig = {.sampleRate = RATE_96000, .channelMode = MONO, .bitsPerSample = 0x7f}, .aptxAdaptiveConfig = {.sampleRate = RATE_96000, .channelMode = 0x47f01, .bitsPerSample = BITS_UNKNOWN, .aptxMode = UNKNOWN, .sinkBuffering = {.minSinkBuff_LL = 0, .maxSinkBuff_LL = 0, .minSinkBuff_HQ = 0, .maxSinkBuff_HQ = 0, .minSinkBuff_TWS = 0, .maxSinkBuff_TWS = 0}, .ttp = {.TTP_LL_low = 0, .TTP_LL_high = 0, .TTP_HQ_low = 0, .TTP_HQ_high = 0, .TTP_TWS_low = 0, .TTP_TWS_high = 0}, .inputMode = STEREO, .inputFadeDuration = 0, .aptxAdaptiveConfigStream = [25]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, .aptxTwsConfig = {.sampleRate = RATE_96000, .channelMode = MONO, .syncMode = 127}, .baCeltConfig = {.sampleRate = RATE_96000, .channelMode = MONO, .frameSize = 4, .complexity = 0, .predictionMode = 0, .vbrFlag = 0}, .lc3Config = {.txConfig = {.sampleRate = RATE_96000, .channelMode = STEREO, .octetsPerFrame = 4, .frameDuration = 0, .bitrate = 0, .bitsPerSample = BITS_UNKNOWN, .numBlocks = 0}, .rxConfig = {.sampleRate = RATE_UNKNOWN, .channelMode = UNKNOWN, .octetsPerFrame = 0, .frameDuration = 0, .bitrate = 0, .bitsPerSample = BITS_UNKNOWN, .numBlocks = 0}, .rxConfigSet = 0, .rxLatency = 0, .decoderOuputChannels = 0, .mode = 0, .codecSpecific = [16]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .defaultQlevel = 0, .NumStreamIDGroup = 0, .streamMap = [48]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, .lhdcConfig = {.sampleRate = RATE_96000, .channelMode = STEREO, .qualityIndex = 0x7f, .bitsPerSample = BITS_32, .lhdcMode = UNKNOWN, .lhdcVersion = UNKNOWN, .lhdcMaxTargetBitrate = MaxTargerBitrate_900k, .lhdcChannelSplitMode = 0, .arEnable = DISABLE, .metaEnable = DISABLE, .llacEnable = DISABLE, .mbrEnable = DISABLE, .larcEnable = DISABLE}}}
11-21 16:15:17.261  1002 13479 13510 W bt_stack: [WARNING:a2dp_encoding.cc(2629)] init:Session type OFFLOAD
11-21 16:15:17.262  1002 13479 13510 W bt_stack: [WARNING:a2dp_encoding.cc(2654)] initInit A2dpTransport_2_1

从MSC中可以看到如下的audio stream setup流程。

A2DP Hardware Offload文章来源地址https://www.toymoban.com/news/detail-447087.html

11-21 16:15:17.266  1002 13479 13510 W bt_stack: [WARNING:a2dp_encoding.cc(2928)] start_session
11-21 16:15:17.270  1002 13479 13510 I bt_stack: [INFO:client_interface.cc(526)] startSession_cb(SUCCESS)

11-21 16:15:37.790  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(3021)] ack_stream_started: result=PENDING
11-21 16:15:38.496  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(3021)] ack_stream_started: result=SUCCESS_FINISHED

11-21 16:15:44.742  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(3052)] ack_stream_suspended: result=PENDING
11-21 16:15:44.792  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(3052)] ack_stream_suspended: result=SUCCESS_FINISHED

11-21 16:15:50.365  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(3021)] ack_stream_started: result=PENDING
11-21 16:15:50.474  1002 13479 13510 I bt_stack: [INFO:a2dp_encoding.cc(3021)] ack_stream_started: result=SUCCESS_FINISHED

到了这里,关于A2DP Hardware Offload的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【SA8295P 源码分析】70 - QAM8295P 原理图参考设计 之 DP、eDP 接口硬件原理分析

    【源码分析】 因为一些原因,本文需要移除, 对于已经购买的兄弟,不用担心,不是跑路, 我会继续持续提供技术支持, 有什么模块想学习的,或者有什么问题有疑问的, 请私聊我,我们 +VX 沟通技术问题,一起学习,一起进步 接下来,我一一私聊已经购买的兄弟添加V

    2024年02月12日
    浏览(56)
  • 网络offload之TSO、GSO、LRO、GRO

    网络offload技术主要是针对网络数据包的分片和合并而进行优化和处理的技术,也可以在网卡实现offload技术。 1. TSO(TCP Segmentation Offload): 是一种利用网卡对要发送的大数据包进行分片,主要是利用其技术进而达到降低CPU利用率的技术。当一个大的数据包达到网卡的时候,在网卡

    2024年02月12日
    浏览(33)
  • 关于手机Camera的硬件电路知识

    前阶段,小白教同事测了些Camere的基本功耗。正愁不知道写什么的小白,突然想到了素材,于是乎便趁着周末雷雨天宅家之际,写一篇关于手机Camere的硬件文章。 关于Camera,景物通过镜头生成光学图像投射到图像传感器表面上,然后光信号转换为模拟的电信号,经过A/D(模数

    2024年02月10日
    浏览(39)
  • 关于一个硬件测试工程师的若干思考

    前段时间生病停更了一段时间,中间请了很长时间的病假在家养病,闲暇之余对工作产生了一丝丝思考。作为一个工作了1609天的入门硬测工程师,一时觉得工作枯燥无味,一时又对工作充满希望。硬件测试工程师!懂得都懂!大部分的工作是枯燥乏味的,在一家公司工作时间

    2024年02月02日
    浏览(40)
  • 关于华为无线路由的一些软硬件的问题

        一、路由器的硬件包括RAM/DRAM、NVRAM、FLASH、ROM、CPU、各种端口以及主板和电源。硬件故障一般可以从LED指示灯上看出。  1、电源上有一个绿色的PWR状态指示灯,当这个指示灯亮着时,表示电源工作正常,接口模块上的ONLINE和OFFLINE指示灯以及TX、RX指示灯。Rx指示灯为绿色

    2024年02月05日
    浏览(55)
  • 关于硬件问题造成的MCU死机,过来人简单的谈一谈

    关于MCU死机问题,近期小编在出差期间遇到多起,且原因不同。所以,今日小白借此机会讲一讲因硬件问题造成的MCU死机。 在遇到死机问题时,已经可以判定是硬件原因造成的前提下,大多人的选择是交叉验证MCU,先判定是否是MCU单体不良造成的死机。在小编以前遇到的死机

    2024年02月11日
    浏览(39)
  • 【TensorRT】关于8.6版本开始的硬件兼容性的一些试错

    在工业应用中,边缘端的部署通常需要发布到不同的硬件,在自己的电脑上生成的模型,往往会因为与其他设备的显卡型号不同架构不同的问题,导致其他设备无法部署主机上导出的engine模型或者trt模型。因此,8.6版本的tensorRT发布了一项新的可能,可以支持基于不同显卡设

    2024年01月25日
    浏览(46)
  • 【OpenSSL 之一】OpenSSL初体验(编译安装、工作机制、Engine加载(afalg、cryptodev-linux)、offload等)

      OpenSSL是一个安全套接字层密码库,囊括主要的密码算法、常用密钥、证书封装管理功能及实现SSL协议。整个软件包大概可以分成三个主要的功能部分:SSL协议库libssl、应用程序命令工具以及密码算法库libcrypto。   OpenSSL项目是一个开放源代码安全项目,它的目标是开发

    2024年02月02日
    浏览(37)
  • Android图形-Hardware Composer HAL

    目录 一、引言 二、概览 三、实现HWC 3.1 为什么是HWC? 3.2 HWC的支持需求 3.3 HWC的实现思路 3.4 HWC的基元 3.5 HIDL接口 3.6 函数指针 3.7 图层和屏幕句柄 3.8 屏幕合成操作 3.9 多个屏幕 3.10 虚拟屏幕合成 3.10.1 模式 3.10.2 输出格式 3.11 同步fence 3.12 热插拔处理 3.12.1 概念理解 3.12.2 更新

    2024年02月09日
    浏览(45)
  • 14.2.2 【Linux】software, hardware RAID

    磁盘阵列分为硬件与软件。所谓的硬件磁盘阵列是通过磁盘阵列卡来达成阵列的目的。磁盘阵列卡上面有一块专门的芯片在处理 RAID 的任务,因此在性能方面会比较好。在很多任务 (例如 RAID 5 的同位检查码计算) 磁盘阵列并不会重复消耗原本系统的 I/O 总线,理论上性能会

    2024年02月14日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包