Bluedroid: 音频数据的传输流程
Posted sheldon_blogs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bluedroid: 音频数据的传输流程相关的知识,希望对你有一定的参考价值。
一. UIPC:
Audio Flinger获取到a2dp的hw module,然后蓝牙协议栈有专用于发送和接收media数据的线程,
名称:btif_media_task.
蓝牙与Audio的通信则采用了socket的方式,管理socket的中间文件为:UIPC。主要的作用就是,接收Audio的控制命令和音频数据发送给BT,即 Audio_a2dp_hw -> UIPC -> btif_media_task
UIPC建立了两条socket,分别为:
#define A2DP_CTRL_PATH "/data/misc/bluedroid/.a2dp_ctrl"
#define A2DP_DATA_PATH "/data/misc/bluedroid/.a2dp_data"
1.control socket在btif media task启动的时创建:
bool btif_a2dp_start_media_task(void) { ... thread_post(worker_thread, btif_media_thread_init, NULL); APPL_TRACE_EVENT("## A2DP MEDIA THREAD STARTED ##"); return true; ... } static void btif_media_thread_init(UNUSED_ATTR void *context) { memset(&btif_media_cb, 0, sizeof(btif_media_cb)); UIPC_Init(NULL); #if (BTA_AV_INCLUDED == TRUE) UIPC_Open(UIPC_CH_ID_AV_CTRL , btif_a2dp_ctrl_cb); #endif raise_priority_a2dp(TASK_HIGH_MEDIA); media_task_running = MEDIA_TASK_STATE_ON; }
2.data socket在收到audio cmd start时创建:
case A2DP_CTRL_CMD_START: /* Don\'t sent START request to stack while we are in call. Some headsets like the Sony MW600, don\'t allow AVDTP START in call and respond BAD_STATE. */ if (!btif_hf_is_call_idle()) { a2dp_cmd_acknowledge(A2DP_CTRL_ACK_INCALL_FAILURE); break; } if (btif_av_stream_ready() == TRUE) { /* setup audio data channel listener */ UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb); /* post start event and wait for audio path to open */ btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0); #if (BTA_AV_SINK_INCLUDED == TRUE) if (btif_media_cb.peer_sep == AVDT_TSEP_SRC) a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);//发送回执 #endif } else if (btif_av_stream_started_ready()) { /* already started, setup audio data channel listener and ack back immediately */ UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb); a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);//发送回执 } else { a2dp_cmd_acknowledge(A2DP_CTRL_ACK_FAILURE); break; } break;
3.UIPC接收command机制:
(1) 启动时,创建一个thread,接收command:
void UIPC_Init(void *p_data) { UNUSED(p_data); BTIF_TRACE_DEBUG("UIPC_Init"); memset(&uipc_main, 0, sizeof(tUIPC_MAIN)); uipc_main_init(); uipc_start_main_server_thread(); } int uipc_start_main_server_thread(void) { uipc_main.running = 1; if (pthread_create(&uipc_main.tid, (const pthread_attr_t *) NULL, (void*)uipc_read_task, NULL) < 0) { BTIF_TRACE_ERROR("uipc_thread_create pthread_create failed:%d", errno); return -1; } return 0; }
(2)监听每一个socket,发现数据后,优先判断是不是音频,再判断是不是命令:
static void uipc_read_task(void *arg) { while (uipc_main.running) { result = select(uipc_main.max_fd+1, &uipc_main.read_set, NULL, NULL, NULL); //发现有数据 /* make sure we service audio channel first */ uipc_check_fd_locked(UIPC_CH_ID_AV_AUDIO); //先确定是不是音频 /* check for other connections */ for (ch_id = 0; ch_id < UIPC_CH_NUM; ch_id++) { if (ch_id != UIPC_CH_ID_AV_AUDIO) uipc_check_fd_locked(ch_id); //再确定是不是command } } static int uipc_check_fd_locked(tUIPC_CH_ID ch_id) { if (uipc_main.ch[ch_id].cback) //通知btif,有command过来 uipc_main.ch[ch_id].cback(ch_id, UIPC_RX_DATA_READY_EVT); }
(3).通知btif去读取command并处理:
static void btif_a2dp_ctrl_cb(tUIPC_CH_ID ch_id, tUIPC_EVENT event) { switch(event) { case UIPC_RX_DATA_READY_EVT: btif_recv_ctrl_data(); //去获取command break; } } static void btif_recv_ctrl_data(void) { UINT8 cmd = 0; int n; n = UIPC_Read(UIPC_CH_ID_AV_CTRL, NULL, &cmd, 1); //读取该socket上的命令 }
二、btif_media_task:
1.btif_media_task是蓝牙协议栈接收HAL层音频数据的主处理线程,数据流程框图如下:
- BTIF reads PCM data from audio flinger via Audio HAL.(Step 6)
- BTIF calls SBC encoder to encode PCM data to SBC frames which are put in a queue.(Step 7 and 8)
- BTIF notifies BTA that the source data is ready in the queue.(Step 9~13)
- BTA gets the SBC frames from the queue, then adds SBC Header. Media PL is constructed now.(Step 15~17)
- BTA writes Media PL to AVDTP.(Step 18)
- AVDTP adds Media Packet Header.(Step 19)
2.主要代码框架:
- btif/src/btif_av.c Bluedroid AV HAL implementation which implements the interface defined in AOSP/hardware/bt_av.h.
- btif/src/btif_media_task.c This is the multimedia module for the BTIF system. It contains task implementations AV, HS and HF profiles\' audio&video processing.
- btif/co/bta_av_co.c This is the advanced audio/video call-out function implementation for BTIF.
- bta/av/bta_av_ci.c This is the implementation for advanced audio/video call-in functions which are called from BTIF.
- bta/av/bta_av_api.c This is the implementation of the API for the advanced audio/video(AV) subsystem of BTA. This interface is called from btif_av.c.
- bta/av/bta_av_mian.c This is the main implementation file for BTA advanced audio/video.
- bta/av/bta_av_ssm.c This is the stream state machine for the BTA advanced audio/video.
- bta/av/bta_av_aact.c This file contains action functions for advanced audio/video stream.
- bta/av/bta_av_sbc.c This module contains utility functions for dealing with SBC data frames and codec capabilities.
- stack/a2dp/a2d_api.c This is the implementation of the API for the Advanced Audio Distribution Profile(A2DP)
- stack/a2dp/a2d_sbc.c This file contains utility functions to help build and parse SBC codec information element and media payload.
- embdrv/sbc/sbc/encoder This folder contains the files which implement SBC decoder.
三、A2DP 数据处理流程:
1.ACL 包发送流程:
由前面所述,HAL层socket发送的音频数据通过a2dp继续下发到AVDTP:
而在 AVDTP 中,所有的功能想发送 Data,必须调用 avdt_ad_write_req 这个函数:
1 //当CCB或SCB给l2cap的 Channel 发送数据时,他们最终都会使用到L2CAP的 API:L2CA_Data_Write 2 UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf) 3 { 4 UINT8 tcid; 5 6 /* get tcid from type, scb */ 7 tcid = avdt_ad_type_to_tcid(type, p_scb); 8 9 10 return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid, p_buf); 11 } 12 //L2CA_DataWrite的返回形式有三种,分别是: 13 //1. L2CAP_DW_SUCCESS:此数据写成功 14 //2.L2CAP_DW_CONGESTED:写数据成功,但是当前信道拥堵 15 //3.L2CAP_DW_FAILED:写数据失败 16 UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data) 17 { 18 L2CAP_TRACE_API2 ("L2CA_DataWrite() CID: 0x%04x Len: %d", cid, p_data->len); 19 return l2c_data_write (cid, p_data, L2CAP_FLUSHABLE_CH_BASED); 20 }
当音频数据流到达 l2c_data_write 这个函数时,标志数据流正式进入到L2CAP层。
l2c_data_write 这个函数做的事情主要有:
- 根据参数 cid(Channel ID) 找到 对应的 ccb(Channel Control Block), 找不到返回 L2CAP_DW_FAILED
- 如果测试者 打开 TESTER 这个宏,发送任意数据,当数据大小 大于 MTU 最大值,也会返回L2CAP_DW_FAILED
- 通过检查 p_ccb->cong_sent 字段,TRUE,则说明当前 Channel 已经拥挤,此时L2CAP的这个Channel不在接收数据,返回 L2CAP_DW_FAILED
- 以上三个条件都通过,说明数据可发送,将数据通过 l2c_csm_execute 继续处理。进入 l2c_csm_execute 函数,标志着这笔数据已经成功交给 l2CAP 来处理,与上层已经没有关系了。
- l2c_csm_execute 函数执行结束后,再次检查 p_ccb->cong_sent 字段,看看当前的 Channel 是否拥挤,如果拥挤则告诉上层 L2CAP_DW_CONGESTED,否则返回 L2CAP_DW_SUCCESS,表示数据已经成功发送。
1 //返回的数据跟上面的 L2CA_DataWrite 作用相同 2 UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags) 3 { 4 tL2C_CCB *p_ccb; 5 6 //遍历l2cb.ccb_pool,通过Channel ID找到对应的Channel Control Block 7 //l2cu_find_ccb_by_cid 见下面源码注释 8 if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) 9 { 10 L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid); 11 GKI_freebuf (p_data); 12 return (L2CAP_DW_FAILED); 13 } 14 15 #ifndef TESTER /* Tester may send any amount of data. otherwise sending message 16 bigger than mtu size of peer is a violation of protocol */ 17 if (p_data->len > p_ccb->peer_cfg.mtu) 18 { 19 L2CAP_TRACE_WARNING1 ("L2CAP - CID: 0x%04x cannot send message bigger than peer\'s mtu size", cid); 20 GKI_freebuf (p_data); 21 return (L2CAP_DW_FAILED); 22 } 23 #endif 24 25 /* channel based, packet based flushable or non-flushable */ 26 //Bluedroid中默认的是 L2CAP_FLUSHABLE_CH_BASED 27 //这个 layer_specific 在 数据发送的 l2c_link_send_to_lower 中表示 ACL包分包 个数 28 p_data->layer_specific = flags; 29 30 //发现本 Channel 已经拥堵,直接返回L2CAP_DW_FAILED 告诉上层等会再发数据 31 //当几个应用 共用 此 Channel 可能会出现这种情况 32 if (p_ccb->cong_sent) 33 { 34 L2CAP_TRACE_ERROR3 ("L2CAP - CID: 0x%04x cannot send, already congested xmit_hold_q.count: %u buff_quota: %u", 35 p_ccb->local_cid, p_ccb->xmit_hold_q.count, p_ccb->buff_quota); 36 37 GKI_freebuf (p_data); 38 return (L2CAP_DW_FAILED); 39 } 40 //毫无疑问啦,这个函数就是我们继续需要分析的函数 41 l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data); 42 43 //已经将上层的这笔数据发送完,如果此 Channel 拥挤了(之前发送这笔包还没拥挤) 44 //返回 L2CAP_DW_CONGESTED 告诉上层当前信道拥挤,你要给我L2CAP层发数据,是不发下来的 45 if (p_ccb->cong_sent) 46 return (L2CAP_DW_CONGESTED); 47 48 //成功发送,并且此时 Channel 并不拥挤 49 return (L2CAP_DW_SUCCESS); 50 } 51 52 //通过 Channel ID 找到 Channel Control Block 53 tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid) 54 { 55 tL2C_CCB *p_ccb = NULL; 56 #if (L2CAP_UCD_INCLUDED == TRUE) 57 UINT8 xx; 58 #endif 59 60 if (local_cid >= L2CAP_BASE_APPL_CID) //大于或等于 0x0040 说明不是 Fixed Channel 61 { 62 /* find the associated CCB by "index" */ 63 local_cid -= L2CAP_BASE_APPL_CID; 64 65 if (local_cid >= MAX_L2CAP_CHANNELS) 66 return NULL; 67 68 p_ccb = l2cb.ccb_pool + local_cid; //直接通过地址偏移找到 69 70 /* make sure the CCB is in use */ 71 if (!p_ccb->in_use) 72 { 73 p_ccb = NULL; 74 } 75 /* make sure it\'s for the same LCB */ 76 else if (p_lcb && p_lcb != p_ccb->p_lcb) 77 { 78 p_ccb = NULL; 79 } 80 } 81 #if (L2CAP_UCD_INCLUDED == TRUE) //默认是关闭的,既然从上层来的都是 数据包了,我认为不会用到 Fixed Channel 82 else 83 { 84 /* searching fixed channel */ 85 p_ccb = l2cb.ccb_pool; 86 for ( xx = 0; xx < MAX_L2CAP_CHANNELS; xx++ ) 87 { 88 if ((p_ccb->local_cid == local_cid) 89 &&(p_ccb->in_use) 90 &&(p_lcb == p_ccb->p_lcb)) 91 break; 92 else 93 p_ccb++; 94 } 95 if ( xx >= MAX_L2CAP_CHANNELS ) 96 return NULL; 97 } 98 #endif 99 100 return (p_ccb); 101 }
首先进入了 L2CAP 层的状态机:
1 void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data) 2 { 3 switch (p_ccb->chnl_state) 4 { 5 case CST_CLOSED: 6 l2c_csm_closed (p_ccb, event, p_data); 7 break; 8 9 case CST_ORIG_W4_SEC_COMP: 10 l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data); 11 break; 12 13 case CST_TERM_W4_SEC_COMP: 14 l2c_csm_term_w4_sec_comp (p_ccb, event, p_data); 15 break; 16 17 case CST_W4_L2CAP_CONNECT_RSP: 18 l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data); 19 break; 20 21 case CST_W4_L2CA_CONNECT_RSP: 22 l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data); 23 break; 24 25 case CST_CONFIG: 26 l2c_csm_config (p_ccb, event, p_data); 27 break; 28 29 case CST_OPEN: 30 l2c_csm_open (p_ccb, event, p_data); 31 break; 32 33 case CST_W4_L2CAP_DISCONNECT_RSP: 34 l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data); 35 break; 36 37 case CST_W4_L2CA_DISCONNECT_RSP: 38 l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data); 39 break; 40 41 default: 42 break; 43 } 44 } 具体的 Channel 状态信息如下 1 typedef enum 2 { 3 CST_CLOSED, /* Channel is in clodes state */ 4 CST_ORIG_W4_SEC_COMP, /* Originator waits security clearence */ 5 CST_TERM_W4_SEC_COMP, /* Acceptor waits security clearence */ 6 CST_W4_L2CAP_CONNECT_RSP, /* Waiting for peer conenct response */ 7 CST_W4_L2CA_CONNECT_RSP, /* Waiting for upper layer connect rsp */ 8 CST_CONFIG, /* Negotiating configuration */ 9 CST_OPEN, /* Data transfer state */ 10 CST_W4_L2CAP_DISCONNECT_RSP, /* Waiting for peer disconnect rsp */ 11 CST_W4_L2CA_DISCONNECT_RSP /* Waiting for upper layer disc rsp */ 12 } tL2C_CHNL_STATE;
音频数据包在函数 l2c_csm_open 中流转,经过各种选择和判断,最后走的是 L2CEVT_L2CA_DATA_WRITE 这个 case。这个 case 调用了 l2c_enqueue_peer_data 让数据进入到当前 ccb 的 xmit_hold_q 队列中,暂存此数据包。l2c_link_check_send_pkts 这个函数发送数据包:
1 //l2c_csm_open 处理 Channel 处于 OPEN 状态下的各种 Event 2 static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data) 3 { 4 UINT16 local_cid = p_ccb->local_cid; 5 tL2CAP_CFG_INFO *p_cfg; 6 tL2C_CHNL_STATE tempstate; 7 UINT8 tempcfgdone; 8 UINT8 cfg_result; 9 10 #if (BT_TRACE_VERBOSE == TRUE) 11 L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: OPEN evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); 12 #else 13 L2CAP_TRACE_EVENT1 ("L2CAP - st: OPEN evt: %d", event); 14 #endif 15 16 #if (L2CAP_UCD_INCLUDED == TRUE) //默认 UCD 是关闭的 17 if ( local_cid == L2CAP_CONNECTIONLESS_CID ) 18 { 19 /* check if this event can be processed by UCD */ 20 if ( l2c_ucd_process_event (p_ccb, event, p_data) ) 21 { 22 /* The event is processed by UCD state machine */ 23 return; 24 } 25 } 26 #endif 27 28 switch (event) 29 { 30 case L2CEVT_LP_DISCONNECT_IND: //Link 都断开连接了,自然 Channel也没有存在的必要了,各种清除 CCB 的工作 31 L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid); 32 l2cu_release_ccb (p_ccb);//释放 当前的 CCB 33 if (p_ccb->p_rcb) 34 (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE); 35 break; 36 37 case L2CEVT_LP_QOS_VIOLATION_IND: /* QOS violation */ 38 /* Tell upper layer. If service guaranteed, then clear the channel */ 39 if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb) 40 (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr); 41 break; 42 43 case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */ 44 p_cfg = (tL2CAP_CFG_INFO *)p_data; 45 46 tempstate = p_ccb->chnl_state; 47 tempcfgdone = p_ccb->config_done; 48 p_ccb->chnl_state = CST_CONFIG; //如果数据流中的数据是 L2CEVT_L2CAP_CONFIG_REQ,当然要转到 CST_CONFIG中继续处理 49 p_ccb->config_done &= ~CFG_DONE_MASK; 50 //启动一个 timer ,一段时间后,查看 cfg 的状态 51 //如果配置处于 L2CAP_PEER_CFG_UNACCEPTABLE,继续尝试配置 52 //如果配置处于断开状态,那当前 Channel 直接断开连接。 53 btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); 54 55 if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK) 56 { 57 (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg); 58 } 59 60 /* Error in config parameters: reset state and config flag */ 61 else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE) 62 { 63 btu_stop_timer(&p_ccb->timer_entry); 64 p_ccb->chnl_state = tempstate; 65 p_ccb->config_done = tempcfgdone; 66 l2cu_send_peer_config_rsp (p_ccb, p_cfg); 67 } 68 else /* L2CAP_PEER_CFG_DISCONNECT */ 69 { 70 /* Disconnect if channels are incompatible 71 * Note this should not occur if reconfigure 72 * since this should have never passed original config. 73 */ 74 l2cu_disconnect_chnl (p_ccb); 75 } 76 break; 77 78 case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */ 79 // btla-specific ++ 80 /* Make sure we are not in sniff mode */ 81 #if BTM_PWR_MGR_INCLUDED == TRUE 82 { 83 tBTM_PM_PWR_MD settings; 84 settings.mode = BTM_PM_MD_ACTIVE; 85 BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); 86 } 87 #else 88 BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr); 89 #endif 90 // btla-specific -- 91 92 p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; //Peer 发送 Disconnect,我们要对此发 Response 93 btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); 94 L2CAP_TRACE_API1 ("a2dp播放流程源码分析