Android 蓝牙 A2dp 听歌卡音?audio数据到a2dp通道流程解析----A2dp流控原理(Acl Flow Control),一文搞懂蓝牙卡音问题处理

Android 蓝牙 A2dp 听歌卡音?audio数据到a2dp通道流程解析----A2dp流控原理(Acl Flow Control),一文搞懂蓝牙卡音问题处理,第1张

本文章将卡音问题彻头彻尾的讲了个清楚,哈哈哈,文章有点长,我敢保证,绝对是全网最详细的讲解A2DP卡音问题的文章。

A2dp播放音乐完整的Android 源码详解解析,请点击下面链接下载:

Android 蓝牙A2dp-Avrcp初始化-连接-播放源码分析文档大全 - 点击下载

android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程

一、A2dp卡音问题-环境因素 先确认问题概率。(低概率则可能为干扰引起,着重关注高概率/必现卡顿问题)确认是否是共存场景问题。(WiFi/BT共存,a2dp/hid共存等)确认BT RF是否正常。(NV错误导致卡顿问题)WIFI OTA (WIFI天线和芯片是一起的)指标满足要求。 二、传统蓝牙HCI流控(flow control)

        流控制用于在主机和主机控制器之间,避免传送到主机控制器的未应答远程设备的 ACL 数据溢出主机控制器数据缓冲区。主机(蓝牙协议栈)负责管理主机控制器(蓝牙芯片)的数据缓冲区。流控分为两种:

1)蓝牙协议栈到蓝牙芯片的流控

2)蓝牙芯片到蓝牙协议栈的流控

下面我们来一一介绍下:

1. 蓝牙协议栈到蓝牙芯片的流控
从host到Controller的数据流控分为两种:Packet-based Data Flow Control和Data-Block-Based Data Flow Control。通过HCI_Write_Flow_Control_Mode 命令来切换,

但是Packet-based data flow control对于BR/EDR芯片来说是默认的,所以你可以不下这个命令。但是我们既然讲到这个命令,还是看下这个命令的格式,如下图:

这个命令看截图已经很清晰了,一笔带过就好了,我们重点来说下协议栈到蓝牙芯片的流控原理(只说传统蓝牙的Packet-baseddata flow control):

        在初始化时,主机将发送 Read_Buffer_Size 指令。通过该指令返回的两参数可以确定从主机发往主机控制器的 HCI ACL 和 SCO 数据分组(不包括报头)的最大长度。

        另有两返回参数表示主机控制器可以缓存等待传输的 HCI ACL 和SCO 数据分组数。在至少有一个连接或处于本地回送的情况下,主机控制器利用Number Of Completed Packets 事件控制从主机发来的数据流。

        事件分组包括一个连接句柄列表, 以及自从前一个事件返回后已经完成的 HCI 数据分组的相应数目(如果对于一个特定的连接句柄,没有事件返回发生,就从连接建立的时间算起 )。发送完成是指数据分组的传输、清除和回送至主机。

        根据该事件返回的信息和Read Buffer Size 命令返回的参数(该参数决定可以存储在主机控制器中的 HCI ACL 和 SCO 数据分组的总数目)主机决定哪个连接句柄的 HCI 数据分组应该发送。        

        在 HCI 每次发送数据分组后,它就假设在主机控制器上所对应链路类型的存储空间减少一个 HCI 分组的量,当主机收到一个新的能提供关于有多少 HCI 数据分组已经完成的 Number Of Completed Packets 事件后,它就可以得到从上一次事件返回后缓冲器数量的减少信息,并可以计算当前的缓冲器的使用量。

        当主机控制器在其缓存中存放有 HCI 数据分组时,它必须向主机周期性持续发送Number Of Completed Packets 事件, 直到最终所有 ACL 数据分组都已发送完毕或溢出。

        事件发送频率由厂商指定。注意:如果 SCO 流控制失效,则 Number OfCompleted Packets 就不能在 SCO 连接句柄中进行报告。

看概念可能我们不会那么清晰,我们就以一个举一个列子,根据btsnoop的flow来说明下吧:

协议栈初始化的时候会采用Read_Buffer_Size来读取acl,sco的个数,以及acl,sco的每包size.如图,那此步骤分为切开两个小节:

①蓝牙芯片回应协议栈comand complete的event携带acl,sco的个数,以及acl,sco的每包size等信息,截图如下:
Bluetooth Controller 底层会有6个BR/EDR的ACL buffer,用于缓存数据。每次蓝牙reset后会初始化,host主动询问controller可以缓存多少包数据。

②同时,host也会主动告知controller,自己可以缓存20包ACL数据和10包SCO数据。

③BLE 的host也会主动询问controller可以缓存的LE数据的数量

当协议栈发送给蓝牙芯片ACL数据,acl的buffer size-1(注意此部分是维护在协议栈中),

而且只有通过frontline能看到剩余的acl buffer size(我试过ellisys以及Wireshark都不会显示,是因为Frontline的软件工具有算法会自动显示)

如图:在初始化的时候我们读到的acl buffer size是10,所以我们发送了一个acl数据,此部分变为9.

当蓝牙协议栈收到蓝牙芯片回送的Num of complete packet event后,协议栈更新acl buffer size数量,增加1,变为10。

 音频流传输路径:bt侧每20ms 从audio的buf中拿pcm data,之后进行encoder,然后将encoder的数据入队,之后我们stack AVDTP 会将队列里的数据写进L2CAP的buff里,如果Controller 中NOCP(Number of complete packet)回的慢,L2CAP的buff就无法发送到peer,从而造成bt侧的queue内部overflow,进而BT侧就会清除queue中的数据,从而造成了声音不连续。所以此题需要看是否环境中有干扰造成NOCP回复慢。

==> 卡音原因是FW 那边回BT侧的NOCP 较慢,造成BT侧数据overflow,所以从HCIlog的wav音频会出现卡音

==>(NOCP是指蓝牙 HOST发送给FW一包数据后,FW tx数据到remote 设备完成时,FW回复给HOST的包:Event: HCI_Number_Of_Completed_Packets,此包回复后,HOST继续给FW发送数据)

从固件中查看remote dev回复:

发现是由于对方每隔60ms回复我们一包ACK,其他时间DUT在重传数据包,对方无响应,造成数据不能正常发送,进而BT这边塞包,形成卡顿。

三、将播放音乐此条ACL链路调整为高优先级,保证播放音乐可以发多个数据包

1、首先蓝牙芯片重启.

①对应的HCI log:

 ②对应的Android 源码

//蓝牙新品重启
void BTM_DeviceReset(UNUSED_ATTR tBTM_CMPL_CB* p_cb) {
227  /* Flush all ACL connections */
228  btm_acl_device_down(); //断开所有的ACL链路
229
230  /* Clear the callback, so application would not hang on reset */
231  btm_db_reset();
232 //开始重启新品,重启完成,调用reset_complete函数处理
233  module_start_up_callbacked_wrapper(get_module(CONTROLLER_MODULE),
234                                     bt_workqueue_thread, reset_complete);
235}
static void reset_complete(void* result) {
171  CHECK(result == FUTURE_SUCCESS);
172  const controller_t* controller = controller_get_interface();//获取到controller接口
173
174  /* Tell L2CAP that all connections are gone */
175  l2cu_device_reset();
176
177  /* Clear current security state */
178  list_foreach(btm_cb.sec_dev_rec, set_sec_state_idle, NULL);
179
180  /* After the reset controller should restore all parameters to defaults. */
//重启芯片后,所有的参数设置为默认值
181  btm_cb.btm_inq_vars.inq_counter = 1;
182  btm_cb.btm_inq_vars.inq_scan_window = HCI_DEF_INQUIRYSCAN_WINDOW;
183  btm_cb.btm_inq_vars.inq_scan_period = HCI_DEF_INQUIRYSCAN_INTERVAL;
184  btm_cb.btm_inq_vars.inq_scan_type = HCI_DEF_SCAN_TYPE;
185
186  btm_cb.btm_inq_vars.page_scan_window = HCI_DEF_PAGESCAN_WINDOW;
187  btm_cb.btm_inq_vars.page_scan_period = HCI_DEF_PAGESCAN_INTERVAL;
188  btm_cb.btm_inq_vars.page_scan_type = HCI_DEF_SCAN_TYPE;
189
190  btm_cb.ble_ctr_cb.conn_state = BLE_CONN_IDLE;
191  btm_cb.ble_ctr_cb.bg_conn_type = BTM_BLE_CONN_NONE;
192  gatt_reset_bgdev_list();
193
194  btm_pm_reset();
195  //获取到BR/EDR controller可接收得buff数量
196  l2c_link_processs_num_bufs(controller->get_acl_buffer_count_classic());
197
198#if (BLE_PRIVACY_SPT == TRUE)
199  /* Set up the BLE privacy settings */
200  if (controller->supports_ble() && controller->supports_ble_privacy() &&
201      controller->get_ble_resolving_list_max_size() > 0) {
202    btm_ble_resolving_list_init(controller->get_ble_resolving_list_max_size());
203    /* set the default random private address timeout */
204    btsnd_hcic_ble_set_rand_priv_addr_timeout(BTM_BLE_PRIVATE_ADDR_INT_MS /
205                                              1000);
206  }
207#endif
208
209  if (controller->supports_ble()) {
210    btm_ble_white_list_init(controller->get_ble_white_list_size());
    //获取到BLE controller可接收得buff数量
211    l2c_link_processs_ble_num_bufs(controller->get_acl_buffer_count_ble());
212  }
213
214  BTM_SetPinType(btm_cb.cfg.pin_type, btm_cb.cfg.pin_code,
215                 btm_cb.cfg.pin_code_len);
216
217  for (int i = 0; i <= controller->get_last_features_classic_index(); i++) {
218    btm_decode_ext_features_page(i,
219                                 controller->get_features_classic(i)->as_array);
220  }
221
222  btm_report_device_status(BTM_DEV_STATUS_UP);
223}

此时调用下面两个函数,来获取Controller 可以接收buff数量。

 BR/EDR : l2c_link_processs_num_bufs(controller->get_acl_buffer_count_classic())
 BLE : l2c_link_processs_ble_num_bufs(controller->get_acl_buffer_count_ble())

注意:br acl buff 为8个包,只用于br分配,ble acl buff 10个包,只用于ble。也有可能br和ble的buff在一起,这样就要一起进行分配。

2、设置A2dp  L2cap链路优先级,并进行acl buff 分配

        当AVDTP建立成功后,调用bta_av_start_ok(),调用bta_av_stream_chg(p_scb,true),在这个函数中,将此条播放音乐的a2dp acl链路调为HIGH优先级。

/*******************************************************************************
2213 *
2214 * Function         bta_av_start_ok
2215 *
2216 * Description      Stream started.
2217 *
2218 * Returns          void
2219 *
2220 ******************************************************************************/
2221void bta_av_start_ok(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
2222  bool initiator = false;
2223  bool suspend = false;
2224  uint16_t flush_to;
2225  uint8_t new_role = p_scb->role;
2226  BT_HDR hdr;
2227  uint8_t clear_policy = 0;
2228  uint8_t cur_role;
2229  uint8_t local_tsep = p_scb->seps[p_scb->sep_idx].tsep;
。。。。。。。。

  if (!suspend) {
2351    p_scb->q_tag = BTA_AV_Q_TAG_STREAM;
    //调用bta_av_stream_chg(p_scb,true),在这个函数中,将此条播放音乐的a2dp acl链路调为HIGH优先级。
2352    bta_av_stream_chg(p_scb, true);//
2353  }
2354
/*******************************************************************************
1067 *
1068 * Function         bta_av_stream_chg
1069 *
1070 * Description      audio streaming status changed.
1071 *
1072 * Returns          void
1073 *
1074 ******************************************************************************/
1075void bta_av_stream_chg(tBTA_AV_SCB* p_scb, bool started) {
1076  uint8_t started_msk = BTA_AV_HNDL_TO_MSK(p_scb->hdi);
1077
1078  APPL_TRACE_DEBUG("%s: peer %s started:%s started_msk:0x%x", __func__,
1079                   p_scb->PeerAddress().ToString().c_str(),
1080                   logbool(started).c_str(), started_msk);
1081
1082  if (started) {
1083    bta_av_cb.audio_streams |= started_msk;
1084    /* Let L2CAP know this channel is processed with high priority */
        //如果stream stared ,就将此条链路调为高优先级
1085    L2CA_SetAclPriority(p_scb->PeerAddress(), L2CAP_PRIORITY_HIGH);
1086  } else {
1087    bta_av_cb.audio_streams &= ~started_msk;
1088    /* Let L2CAP know this channel is processed with low priority */
1089    L2CA_SetAclPriority(p_scb->PeerAddress(), L2CAP_PRIORITY_NORMAL);
1090  }
1091}
 L2CA_SetAclPriority- 》l2cu_set_acl_priority(bd_addr, priority, false));
bool l2cu_set_acl_priority(const RawAddress& bd_addr, uint8_t priority,
2355                           bool reset_after_rs) {
2356  tL2C_LCB* p_lcb;
2357  uint8_t* pp;
2358  uint8_t command[HCI_BRCM_ACL_PRIORITY_PARAM_SIZE];//优先级最多三个acl链路
2359  uint8_t vs_param;
2360
2361  APPL_TRACE_EVENT("SET ACL PRIORITY %d", priority);
2362
2363  /* Find the link control block for the acl channel */
2364  p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_BR_EDR);
2365  if (p_lcb == NULL) {
2366    L2CAP_TRACE_WARNING("L2CAP - no LCB for L2CA_SetAclPriority");
2367    return (false);
2368  }
2369
2370  if (BTM_IS_BRCM_CONTROLLER()) {
        //reset_after_rs:这个是角色转换标志位。此时是播放音乐,此标志位为false.
2371    /* Called from above L2CAP through API; send VSC if changed */
2372    if ((!reset_after_rs && (priority != p_lcb->acl_priority)) ||
2373        /* Called because of a master/slave role switch; if high resend VSC */
        //当a link's master/slave role  change event is received,如果需要,重置高优先级链接。只要这两种情况下会调节acl 链路优先级。
2374        (reset_after_rs && p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)) {
2375      pp = command;
2376    //如果当前acl优先级不是HIGH,将优先级置为HIGH priorify,并设置到controller中。
2377      vs_param = (priority == L2CAP_PRIORITY_HIGH) ? HCI_BRCM_ACL_PRIORITY_HIGH
2378                                                   : HCI_BRCM_ACL_PRIORITY_LOW;
2379
2380      UINT16_TO_STREAM(pp, p_lcb->handle);
2381      UINT8_TO_STREAM(pp, vs_param);
2382
2383      BTM_VendorSpecificCommand(HCI_BRCM_SET_ACL_PRIORITY,
2384                                HCI_BRCM_ACL_PRIORITY_PARAM_SIZE, command,
2385                                NULL);
2386    }
2387  }
2388
2389  /* Adjust lmp buffer allocation for this channel if priority changed */
2390  if (p_lcb->acl_priority != priority) {
2391    p_lcb->acl_priority = priority;
        //接下来调整链路层buffer数量为此信道,acl链路.
2392    l2c_link_adjust_allocation();
2393  }
2394  return (true);
2395}
2396

上面函数主要工作:如果当前acl优先级不是HIGH,将优先级置为HIGH priorify,并设置到controller中。

最最最重要的函数来了

接下来调整此条A2dp链路层 controller 可接收buffer数量。一般只有A2dp这条链路为高优先级。

 总结下 l2c_link_adjust_allocation()函数做的工作:

1、判断是否有active link。

2、若此刻没有链路,复位buff定额和芯片buff.

3、若此刻有链路,分别计算出高--低优先级链路数量

4.1如果存在低优先级链路,首先保留1个Buff
4.2当高优先级链路数乘以高优先级定额数加上低定额数(至少保证1个buff),大于总的acl 链路buff数量。此时需要将高优先级链路定额buff,依次减少1,直至满足这个while循环。

5、计算出高优先级定额总数-高优先级链路数乘以高优先级定额数

6、计算出低优先级定额总数- 总的定额数减去第5步高优先级定额数,若高优先级定额数不小于总的定额数,则至少保证1个buff

7、判断是否有低优先级链路数

8、如果低优先级链路数,大于低优先级定额的Buff,说明都不能保证一条acl 链路一个Buff,将剩下的buff,给到轮询算法中,并且qq_remainder 保留为1个

9、如果低优先级链路数大于0个,qq保存 = 所剩的低优先级定额除以低优先级链路上,取商(不用想,肯定是1)。起码保证每条链路有一个可用的Buff。将余数保存到qq_remainder中。

10、如果没有低优先级链路上,则qq_remainder为1

11、给每条链路分配buff

12、给高优先级链路赋值高优先级定额buff

13、如果是低优先级链路,并且qq = 0,意味着每条链路都分不到一个Buff,轮询算法中,未回复数增加1

14、若qq 不等于0,将qq的buff,赋值给每条acl链路,最后将剩余的qq_remainder中的buff依次给acl链路赠送。

总结:所以当手机同时连接一个蓝牙耳机,再连接一个蓝牙键盘、蓝牙鼠标时候,当播放音乐的时候,移动蓝牙鼠标,会有迟滞现象,因为此时会给a2dp这条链路分配5个buff,蓝牙键盘和蓝牙鼠标分配1个buff,所以会迟滞。

源码分析如下:
void l2c_link_adjust_allocation(void) {
666  uint16_t qq, yy, qq_remainder;
667  tL2C_LCB* p_lcb;
668  uint16_t hi_quota, low_quota; //高优先级buff数量,低优先级buff数量
669  uint16_t num_lowpri_links = 0; //低优先级acl链路数量
670  uint16_t num_hipri_links = 0;//高优先级acl链路数量
671  uint16_t controller_xmit_quota = l2cb.num_lm_acl_bufs; //controller中总acl数量,由l2c_link_process_num_buff()函数获得。
672  uint16_t high_pri_link_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A; //高优先级的buff数量为5个buff.
673
674  /* If no links active, reset buffer quotas and controller buffers */
675  if (l2cb.num_links_active == 0) {
676    l2cb.controller_xmit_window = l2cb.num_lm_acl_bufs;
677    l2cb.round_robin_quota = l2cb.round_robin_unacked = 0; //轮询调度Round-robin(负载均衡算法),采用这种模式,消息在队列中保存,以轮询的方式将消息发送给监听消息队列的消费者,可以动态的增加消费者以提高消息的处理能力。 此时,没有高优先级链路,所以不使用轮询调度算法,故对应的buff置为0.
	 return;
679  }
680
681  /* First, count the links */ 
682  for (yy = 0, p_lcb = &l2cb.lcb_pool[0]; yy < MAX_L2CAP_LINKS; yy++, p_lcb++) {
683    if (p_lcb->in_use) {
684      if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) 
685        num_hipri_links++;//计算出高优先级链路数。
686      else
687        num_lowpri_links++;//计算出低优先级链路数。
688    }
689  }
690
691  /* now adjust high priority link quota */
692  low_quota = num_lowpri_links ? 1 : 0; //如果存在低优先级链路,至少保留1个buff.
693  while ((num_hipri_links * high_pri_link_quota + low_quota) >
694         controller_xmit_quota)   //当高优先级链路数乘以高优先级定额数(5个buff)加上低定额数(至少保证1个buff),大于总的acl 链路buff数量。
695    high_pri_link_quota--; //此时需要将高优先级链路定额buff,依次减少1,以至满足这个while循环。
696
697  /* Work out the xmit quota and buffer quota high and low priorities */
698  hi_quota = num_hipri_links * high_pri_link_quota; //高优先级链路数乘以高优先级定额数(5个buff),一般高优先级只有1条,所以结果是5。
699  low_quota =
700      (hi_quota < controller_xmit_quota) ? controller_xmit_quota - hi_quota : 1;//剩下的buff,就为低定额数buff(至少为1个)。
701
702  /* Work out and save the HCI xmit quota for each low priority link */
703
704  /* If each low priority link cannot have at least one buffer */
705  if (num_lowpri_links > low_quota) { //如果低优先级链路数,大于低优先级定额的buff,说明都不能保证一条acl 链路一个buff。
706    l2cb.round_robin_quota = low_quota;//将剩下的buff,给到轮询算法中。
707    qq = qq_remainder = 1;//遗留buff为1.
708  }
709  /* If each low priority link can have at least one buffer */
710  else if (num_lowpri_links > 0) { //如果低优先级链路数大于0个。
711    l2cb.round_robin_quota = 0;
712    l2cb.round_robin_unacked = 0;
713    qq = low_quota / num_lowpri_links;//qq保存 = 所剩的低优先级定额除以低优先级链路上,取商(不用想,肯定是1)。起码保证每条链路有一个可用的buff.
714    qq_remainder = low_quota % num_lowpri_links; //将余数保存到qq_remainder中。
715  }
716  /* If no low priority link */
717  else {
718    l2cb.round_robin_quota = 0;
719    l2cb.round_robin_unacked = 0;
720    qq = qq_remainder = 1; //如果没有低优先级链路上,则qq_remainder为1.
721  }
722
723  L2CAP_TRACE_EVENT(
724      "l2c_link_adjust_allocation  num_hipri: %u  num_lowpri: %u  low_quota: "
725      "%u  round_robin_quota: %u  qq: %u",
726      num_hipri_links, num_lowpri_links, low_quota, l2cb.round_robin_quota, qq);
727
728  /* Now, assign the quotas to each link */ //现在给每条链路分配buff.
729  for (yy = 0, p_lcb = &l2cb.lcb_pool[0]; yy < MAX_L2CAP_LINKS; yy++, p_lcb++) {
730    if (p_lcb->in_use) {
731      if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) {
732        p_lcb->link_xmit_quota = high_pri_link_quota;//给高优先级链路赋值高优先级定额buff.
733      } else {
734        /* Safety check in case we switched to round-robin with something
735         * outstanding */
736        /* if sent_not_acked is added into round_robin_unacked then don't add it
737         * again */
738        /* l2cap keeps updating sent_not_acked for exiting from round robin */
739        if ((p_lcb->link_xmit_quota > 0) && (qq == 0)) //如果是低优先级链路,并且qq = 0,意味着每条链路都分不到一个buff.
740          l2cb.round_robin_unacked += p_lcb->sent_not_acked; //轮询算法中,未回复数增加1.
741
742        p_lcb->link_xmit_quota = qq; //若qq 不等于0,将qq的buff,赋值给每条acl链路。
743        if (qq_remainder > 0) {           //最后将剩余的qq_remainder中的buff依次给acl链路赠送。
744          p_lcb->link_xmit_quota++;
745          qq_remainder--;
746        }
747      }
748
749      L2CAP_TRACE_EVENT(
750          "l2c_link_adjust_allocation LCB %d   Priority: %d  XmitQuota: %d", yy,
751          p_lcb->acl_priority, p_lcb->link_xmit_quota);
752
753      L2CAP_TRACE_EVENT("        SentNotAcked: %d  RRUnacked: %d",
754                        p_lcb->sent_not_acked, l2cb.round_robin_unacked);
755
756      /* There is a special case where we have readjusted the link quotas and */
757      /* this link may have sent anything but some other link sent packets so */
758      /* so we may need a timer to kick off this link's transmissions. */
759      if ((p_lcb->link_state == LST_CONNECTED) &&
760          (!list_is_empty(p_lcb->link_xmit_data_q)) &&
761          (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) {
762        alarm_set_on_mloop(p_lcb->l2c_lcb_timer,
763                           L2CAP_LINK_FLOW_CONTROL_TIMEOUT_MS,
764                           l2c_lcb_timer_timeout, p_lcb);
765      }
766    }
767  }
768}
四、a2dp音乐播放 audio到蓝牙侧流程 最最最重要的函数来了

下图为audio音频流到蓝牙a2dp整个流程图

上图完整的Android 源码详解解析,请点击下面链接下载:

Android 蓝牙A2dp-Avrcp初始化-连接-播放源码分析文档大全 - 点击下载

图片解析:

1、蓝牙和AUDIO之间的接口

   蓝牙和audio之间的通信是通过socket,管理socket中的文件是UIPC,UIPC管理两条socket.

   A2DP_CTRL_PATH  /data/misc/bluedroid/.a2dp_ctrl  

    A2DP_DATA_PATH /data/misc/bluedroid/.a2dp_data

   这两个socket的作用就是接收audio的控制命令和音频数据。
2、a2dp初始化
    首先初始化a2dp端的UIPC控制socket:btif_a2dp_control_init()->uipc_init()->uipc_main_init()
    开启uipc服务线程:uipc_start_main_server_thread()->uipc_read_task();
    并且注册控制信息回调UIPC_OPEN(btif_a2dp_ctrl_cb)。
3、a2dp codec初始化
    开始设置编码格式:btif_a2dp_source_setup_codec(),btif_a2dp_source_setup_codec_delay()
在此函数中获取编码接口bta_av_co_get_encoder_interface()
    在a2dp_sbc_encoder_init()函数中注册两个回调函数 :1、btif_a2dp_source_read_callback 底层编码消费数据
从这里通知读取数据。2、 btif_a2dp_source_enqueue_callback 负责将编码后的数据加入btif_a2dp_source_cb.tx_audio_queue,
并取数据使用。
4、audio获取蓝牙 module
    当我们通过蓝牙耳机播放音频时,最终通过Audio Flinger 调用到openOutput函数来输出音频道蓝牙耳机。
openoutput()->.....->hal_module_methods->adev_open()。
5、audio收到a2dp连接成功通知
    调用adev_open_output_stream()->...->连接控制通道skt_connect()。做了一个socket的连接,连接的path
是data/misc/bluedroid/.a2dp_data,当skt_connect连接后,后续的audio传下来的数据只要写到common->audio_fd
就可以了。
6、audio数据写入
    out_write()-> a2dp_command(A2DP_CTRL_CMD_START) ,此处对应HCI command:AVDTP_START,a2dp控制
socket,收到消息进行回调,在UIPC的机制中,有数据来就会携带宏UIPC_RX_DATA_READY_EVT。
7、打开数据通道
    在收到audio cmd start时候,打开数据通道socket。
8、avdtp协议的连接
    BTA_AvStart()->....并将结果上报上层。
9、真正音频流写入
    skt_write()-->btif_a2dp_data_cb()->...->btif_a2dp_source_audio_tx_start_event()
        //#define A2DP_AAC_ENCODER_INTERVAL_MS 20  //A2DP AAC encoder interval in milliseconds
        btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms()//每20ms 去读一次数据,而不是通过UIPC的回调函数来 *** 作。
        btif_a2dp_source_alarm_cb。
10、读取数据
    之前注册的回调函数,btif_a2dp_source_read_callback,通过UIPC_Read去读取数据,也就是从socket文件中读取,
-->SBC_Encode()进行编码,将编码好的数据加入发送队列,就是添加到btif_a2dp_source_cb.tx_audio中,如果这个队列数据溢出,
会打印以下log:
bt_btif_a2dp_source: btif_a2dp_source_enqueue_callback: TX queue buffer size now=22 adding=7 max=28
    Cannot read RSSI
    Cannot read Failed Contact Counter
    Cannot read Automatic Flush Timeout
    Cannot read Tx Power
然后通知协议栈数据好了,可以过来取数据了。
11、调用数据通道bta_av_data_path()来取数据。

 其中最重要函数:调用数据通道bta_av_data_path()来取数据,给对端设备发送数据。

1、计算acl链路可缓存buff数量(l2c_buff),(a2dp acl链路优先级高,分配定额5个buff)。

2、判断a2dp_list队列是否有数据。
3、若有数据,取出a2dp_list链表头数据p_buff
4、若没有数据,调用bta_av_co_audio_source_data_path去btif_a2dp_source_cb.tx_audio_queue中读取数据,保存到p_buff
5、此时4的p_buff中有了数据。
6、判断acl链路可缓存buff数量(l2c_buff) 是否大于controller可接收buff总数(a2dp acl链路优先级高,分配定额5个buff)
7、若未超过定额5个buff,将取出来的数据p_buff,交给AVDTP,在AVDTP层,调用L2CA_DataWrite向下层发送数据,发送给对端设备
8、若超过定额5个buff,再次判断p_buff中数据,是从3中取出来的,还是从4中取出来的
9、将之前取出来的数据p_buff,继续放回到a2dp_list中
10、再次判断,此刻a2dp_list中数据是否小于3包
11、若小于3包,将之前取出来的数据p_buff,继续放回到a2dp_list中
12、若大于三包,丢弃掉之前取出来的数据p_buff


总结:若第6步,acl链路可缓存buff数量(l2c_buff) 等于controller可接收buff总数,证明协议栈5个包塞满了,没有发出去,
此时就不能从btif_a2dp_source_cb.tx_audio_queue中读取数据,这样,目前Android源生设定,每20ms发送一包音频数据,
audio给到蓝牙侧的数据会溢出overflow,会打印以下log:
bt_btif_a2dp_source: btif_a2dp_source_enqueue_callback: TX queue buffer size now=22 adding=7 max=28
    Cannot read RSSI
    Cannot read Failed Contact Counter
    Cannot read Automatic Flush Timeout
    Cannot read Tx Power
link_xmit_data_q只能发送5个L2CAP包,超出5个部分,将缓存到a2d_list中,如果a2d_list存在数据,那么就不会发送btif_a2dp_source_cb.tx_audio_queue里面的数据,
然后就出现数据包丢失和卡顿。如果收到controller上报的completed evt上报后,会主动去发送a2d_list已缓存的音频数据。


源码分析:
void bta_av_data_path(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
2098  BT_HDR* p_buf = NULL;
2099  uint32_t timestamp; //时间戳
2100  bool new_buf = false; //是否有新数据
2101  uint8_t m_pt = 0x60;
2102  tAVDT_DATA_OPT_MASK opt;
2103
2104  if (p_scb->cong) return;
2105
2106  if (p_scb->use_rtp_header_marker_bit) {
2107    m_pt |= AVDT_MARKER_SET;
2108  }
2109
2110  // Always get the current number of bufs que'd up
2111  p_scb->l2c_bufs =
2112      (uint8_t)L2CA_FlushChannel(p_scb->l2c_cid, L2CAP_FLUSH_CHANS_GET); //1、计算acl链路可缓存buff数量(l2c_buff),(a2dp acl链路
优先级高,分配定额5个buff)
2113
2114  if (!list_is_empty(p_scb->a2dp_list)) { //2、判断a2dp_list队列是否有数据,若有数据,
2115    p_buf = (BT_HDR*)list_front(p_scb->a2dp_list); //3、取出a2dp_list链表头数据p_buff
2116    list_remove(p_scb->a2dp_list, p_buf);
2117    /* use q_info.a2dp data, read the timestamp */
2118    timestamp = *(uint32_t*)(p_buf + 1);
2119  } else {
2120    new_buf = true;
2121    /* A2DP_list empty, call co_data, dup data to other channels */
//4、若没有数据,调用bta_av_co_audio_source_data_path去btif_a2dp_source_cb.tx_audio_queue中读取数据,保存到p_buff
2122    p_buf = p_scb->p_cos->data(p_scb->cfg.codec_info, ×tamp);
2123
2124    if (p_buf) {
2125      /* use the offset area for the time stamp */
2126      *(uint32_t*)(p_buf + 1) = timestamp;
2127
2128      /* dup the data to other channels */
2129      bta_av_dup_audio_buf(p_scb, p_buf);
2130    }
2131  }
2132
2133  if (p_buf) {
//6、判断acl链路可缓存buff数量(l2c_buff) 是否大于controller可接收buff总数(a2dp acl链路优先级高,分配定额5个buff(如果有多条高优先级链路,少于5个))
2134    if (p_scb->l2c_bufs < (BTA_AV_QUEUE_DATA_CHK_NUM)) {//未超过定额数,7、将取出来的数据p_buff,交给AVDTP,发送给对端设备
2135      /* There's a buffer, just queue it to L2CAP.
2136       * There's no need to increment it here, it is always read from
2137       * L2CAP (see above).
2138       */
2139
2140      /* opt is a bit mask, it could have several options set */
2141      opt = AVDT_DATA_OPT_NONE;
2142      if (p_scb->no_rtp_header) {
2143        opt |= AVDT_DATA_OPT_NO_RTP;
2144      }
2145
2146      //
2147      // Fragment the payload if larger than the MTU.
2148      // NOTE: The fragmentation is RTP-compatibie.
2149      //
2150      size_t extra_fragments_n = 0;
2151      if (p_buf->len > 0) {
2152        extra_fragments_n = (p_buf->len / p_scb->stream_mtu) +
2153                            ((p_buf->len % p_scb->stream_mtu) ? 1 : 0) - 1;
2154      }
2155      std::vector extra_fragments;
2156      extra_fragments.reserve(extra_fragments_n);
2157
2158      uint8_t* data_begin = (uint8_t*)(p_buf + 1) + p_buf->offset;
2159      uint8_t* data_end = (uint8_t*)(p_buf + 1) + p_buf->offset + p_buf->len;
2160      while (extra_fragments_n-- > 0) {
2161        data_begin += p_scb->stream_mtu;
2162        size_t fragment_len = data_end - data_begin;
2163        if (fragment_len > p_scb->stream_mtu) fragment_len = p_scb->stream_mtu;
2164
2165        BT_HDR* p_buf2 = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
2166        p_buf2->offset = p_buf->offset;
2167        p_buf2->len = 0;
2168        p_buf2->layer_specific = 0;
2169        uint8_t* packet2 =
2170            (uint8_t*)(p_buf2 + 1) + p_buf2->offset + p_buf2->len;
2171        memcpy(packet2, data_begin, fragment_len);
2172        p_buf2->len += fragment_len;
2173        extra_fragments.push_back(p_buf2);
2174        p_buf->len -= fragment_len;
2175      }
2176
2177      if (!extra_fragments.empty()) {
2178        // Reset the RTP Marker bit for all fragments except the last one
2179        m_pt &= ~AVDT_MARKER_SET;
2180      }
     //7、将取出来的数据p_buff,交给AVDTP,发送给对端设备
2181      AVDT_WriteReqOpt(p_scb->avdt_handle, p_buf, timestamp, m_pt, opt);
2182      for (size_t i = 0; i < extra_fragments.size(); i++) {
2183        if (i + 1 == extra_fragments.size()) {
2184          // Set the RTP Marker bit for the last fragment
2185          m_pt |= AVDT_MARKER_SET;
2186        }
2187        BT_HDR* p_buf2 = extra_fragments[i];
2188        AVDT_WriteReqOpt(p_scb->avdt_handle, p_buf2, timestamp, m_pt, opt);
2189      }
2190      p_scb->cong = true;
2191    } else { //超过定额数,8、判断p_buff中数据,是从3中取出来的,还是从4中取出来的
2192      /* there's a buffer, but L2CAP does not seem to be moving data */
2193      if (new_buf) {   / /new_buf == 1,说明从4取出来的, 证明之前a2dp_list为空
2194        /* just got this buffer from co_data,
2195         * put it in queue */
2196        list_append(p_scb->a2dp_list, p_buf); //9、将之前取出来的数据p_buff,继续放回到a2dp_list中
2197      } else { //若是从a2d_list中取出来的
2198        /* just dequeue it from the a2dp_list */
2199        if (list_length(p_scb->a2dp_list) < 3) { //10、再次判断,此刻a2dp_list中数据是否小于3包
2200          /* put it back to the queue */  //小于3包,11、将之前取出来的数据p_buff,继续放回到a2dp_list中
2201          list_prepend(p_scb->a2dp_list, p_buf);
2202        } else {
2203          /* too many buffers in a2dp_list, drop it. */
	//大于等于3包,12、丢弃掉之前取出来的数据p_buff
2204          bta_av_co_audio_drop(p_scb->hndl, p_scb->PeerAddress());
2205          osi_free(p_buf);
2206        }
2207      }
2208    }
2209  }
2210}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/web/992657.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存