Android bluetooth介紹(四): a2dp connect流程分析,androida2dp

來源:互聯網
上載者:User

Android bluetooth介紹(四): a2dp connect流程分析,androida2dp

關鍵詞:藍芽blueZ  A2DP、SINK、sink_connect、sink_disconnect、sink_suspend、sink_resume、sink_is_connected、sink_get_properties、AUDIO、DBUS
版本:基於android4.2之前版本 bluez
核心:linux/linux3.08
系統:android/android4.1.3.4
作者:xubin341719(歡迎轉載,請註明作者,請尊重著作權謝謝)
歡迎指正錯誤,共同學習、共同進步!!

Android bluetooth介紹(一):基本概念及硬體介面
Android bluetooth介紹(二): android 藍芽代碼架構及其uart 到rfcomm流程
Android bluetooth介紹(三): 藍芽掃描(scan)裝置分析
Android bluetooth介紹(四): a2dp connect流程分析

一、A2DP_CONNECT上層代碼流程

二、從HCI log中看AVDTP 建立過程
1AVDTP l2cap建立過程

2AVDTP相關信令處理流程在HCI 中的流程

DISCOVER \GET_CAPABILITIES\SET_CONFIGURATION\OPEN\START\SUSPEND
三、audiosink函數註冊、及命令處理流程
AVDTP_DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:等一系列控制命令
(一)、sink_connect建立流程
        整體流程如下所示


1、idh.code\external\bluetooth\bluez\audio\sink.c

static DBusMessage *sink_connect(DBusConnection *conn,DBusMessage *msg, void *data){…………if (!sink->session)//(1)、如果沒有AVDTP會話,擷取AVDTP串連狀態;sink->session = avdtp_get(&dev->src, &dev->dst);if (!sink->session)//相關失敗操作return btd_error_failed(msg, "Unable to get a session");if (sink->connect || sink->disconnect)//如果正在串連、斷開,發送busy訊息;return btd_error_busy(msg);if (sink->stream_state >= AVDTP_STATE_OPEN)//如果已經開啟,發送已經串連訊息;return btd_error_already_connected(msg);if (!sink_setup_stream(sink, NULL))//(2)、建立AVDTP流;return btd_error_failed(msg, "Failed to create a stream");dev->auto_connect = FALSE;pending = sink->connect;pending->conn = dbus_connection_ref(conn);//(3)、儲存用戶端dbus資訊;pending->msg = dbus_message_ref(msg);DBG("stream creation in progress");return NULL;}

(1)、如果沒有AVDTP會話,擷取AVDTP串連狀態;

sink->session = avdtp_get(&dev->src, &dev->dst);idh.code\external\bluetooth\hcidump\parser\avdtp.cstruct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst){………………session = avdtp_get_internal(src, dst);………………}avdtp_get_internal 中設定 session->state狀態,session->state = AVDTP_SESSION_STATE_DISCONNECTED;

(2)、建立AVDTP流;
sink_setup_stream(sink,NULL)
idh.code\external\bluetooth\hcidump\parser\avdtp.c

gboolean sink_setup_stream(struct sink *sink, struct avdtp *session){…………avdtp_set_auto_disconnect(sink->session, FALSE);//不能自動斷開;if (avdtp_discover(sink->session, discovery_complete, sink) < 0)//調用avdtp_discover,discovery_complete為回呼函數;return FALSE;sink->connect = g_new0(struct pending_request, 1);return TRUE;}

idh.code\external\bluetooth\hcidump\parser\avdtp.c

int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,void *user_data){int err;if (session->discov_cb)return -EBUSY;if (session->seps) {session->discov_cb = cb;session->user_data = user_data;g_idle_add(process_discover, session);return 0;}err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);//發送AVDTP_DISCOVER命令出去if (err == 0) {session->discov_cb = cb;session->user_data = user_data;}return err;}
idh.code\external\bluetooth\hcidump\parser\avdtp.c
static int send_request(struct avdtp *session, gboolean priority,struct avdtp_stream *stream, uint8_t signal_id,void *buffer, size_t size){struct pending_req *req;if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {DBG("Unable to send requests while aborting");return -EINVAL;}req = g_new0(struct pending_req, 1);req->signal_id = signal_id;req->data = g_malloc(size);memcpy(req->data, buffer, size);req->data_size = size;req->stream = stream;return send_req(session, priority, req);//這個函數我們後面分析;}

(3)、儲存用戶端dbus資訊;

pending->conn = dbus_connection_ref(conn);pending->msg = dbus_message_ref(msg);

2、send_req 建立L2CAP串連
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static int send_req(struct avdtp *session, gboolean priority,struct pending_req *req){static int transaction = 0;int err;if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//如果AVDTP沒有串連,session->io = l2cap_connect(session);//(1)、建立l2cap串連;if (!session->io) {err = -EIO;goto failed;}avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);}if (session->state < AVDTP_SESSION_STATE_CONNECTED ||session->req != NULL) {//如果AVDTP沒串連queue_request(session, req, priority);//把相關參數放入隊列return 0;//在這裡返回,後面AVDTP sock建立完成後,會再次調用這個函數;}req->transaction = transaction++;transaction %= 16;/* FIXME: Should we retry to send if the bufferwas not totally sent or in case of EINTR? */if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,req->signal_id, req->data, req->data_size)) {//(2)、發送相關命令err = -EIO;goto failed;}…………}

(1)、建立l2cap串連
sink connect的過程本質上是建立一個avdtp 串連的過程,avdtp是基於l2cap的,包括控制命令的發送和資料的發送都是l2cap的,所以這個圖紙表示了建立一個發送控制命令的l2cap的socket,等這個socket建立起來以後,開始發送AVDPT_DISCOVER的請求;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

session->io = l2cap_connect(session);static GIOChannel *l2cap_connect(struct avdtp *session){GError *err = NULL;GIOChannel *io;io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session,NULL, &err,BT_IO_OPT_SOURCE_BDADDR, &session->server->src,BT_IO_OPT_DEST_BDADDR, &session->dst,BT_IO_OPT_PSM, AVDTP_PSM,BT_IO_OPT_INVALID);if (!io) {error("%s", err->message);g_error_free(err);return NULL;}return io;}

這個函數中注意兩點,1)、bt_io_connect;2)、avdtp_connect_cb回呼函數;
1)、bt_io_connect
idh.code\external\bluetooth\bluez\btio\btio.c

GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,gpointer user_data, GDestroyNotify destroy,GError **gerr, BtIOOption opt1, ...){…………io = create_io(type, FALSE, &opts, gerr);if (io == NULL)return NULL;sock = g_io_channel_unix_get_fd(io);switch (type) {case BT_IO_L2RAW:err = l2cap_connect(sock, &opts.dst, 0, opts.cid);break;//不同協議的串連,如L2CPA、RFCOMM、SCOcase BT_IO_L2CAP:err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);break;case BT_IO_RFCOMM:err = rfcomm_connect(sock, &opts.dst, opts.channel);break;case BT_IO_SCO:err = sco_connect(sock, &opts.dst);break;…………connect_add(io, connect, user_data, destroy);return io;}

Btio中l2cap_connect的實現:
idh.code\external\bluetooth\bluez\btio\btio.c

static int l2cap_connect(int sock, const bdaddr_t *dst,uint16_t psm, uint16_t cid){int err;struct sockaddr_l2 addr;memset(&addr, 0, sizeof(addr));addr.l2_family = AF_BLUETOOTH;bacpy(&addr.l2_bdaddr, dst);if (cid)addr.l2_cid = htobs(cid);elseaddr.l2_psm = htobs(psm);err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));//建立BTPROTO_L2CAPif (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))return err;return 0;}

2)、avdtp_connect_cb回呼函數
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data){………………if (session->state == AVDTP_SESSION_STATE_CONNECTING) {//如果處於正在串連狀態;DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);session->buf = g_malloc0(session->imtu);avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);//設定AVDTP狀態為已經串連狀態;if (session->io_id)g_source_remove(session->io_id);/* This watch should be low priority since otherwise the * connect callback might be dispatched before the session * callback if the kernel wakes us up at the same time for * them. This could happen if a headset is very quick in * sending the Start command after connecting the stream * transport channel. */session->io_id = g_io_add_watch_full(chan,G_PRIORITY_LOW,G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,(GIOFunc) session_cb, session,NULL);………………process_queue(session);//發送DISCOVERreturn;…………}

3、process_queue(session)發送DISCOVER命令出去
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static int process_queue(struct avdtp *session){…………*queue = g_slist_remove(*queue, req);return send_req(session, FALSE, req);}

這個函數調用send_req,這個函數前面已經調用過,可是現在AVDTP的狀態不同,第一次調用AVDTP_SESSION_STATE_DISCONNECTED狀態,第二次調用為

AVDTP_SESSION_STATE_CONNECTED狀態;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static int send_req(struct avdtp *session, gboolean priority,struct pending_req *req){static int transaction = 0;int err;if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//第二次調用時,就不走這段函數session->io = l2cap_connect(session);if (!session->io) {err = -EIO;goto failed;}avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);}if (session->state < AVDTP_SESSION_STATE_CONNECTED ||//第二次調用也越過這段函數session->req != NULL) {queue_request(session, req, priority);return 0;}req->transaction = transaction++;transaction %= 16;/* FIXME: Should we retry to send if the bufferwas not totally sent or in case of EINTR? */if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,req->signal_id, req->data, req->data_size)) {//avdtp_send就是主要的操作err = -EIO;goto failed;}

4、avdtp_send的實現
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,uint8_t message_type, uint8_t signal_id,void *data, size_t len){…………     /* Send the start packet */memset(&start, 0, sizeof(start));start.transaction = transaction;start.packet_type = AVDTP_PKT_TYPE_START;start.message_type = message_type;start.no_of_packets = cont_fragments + 1;start.signal_id = signal_id;memcpy(session->buf, &start, sizeof(start));memcpy(session->buf + sizeof(start), data,session->omtu - sizeof(start));if (!try_send(sock, session->buf, session->omtu))return FALSE;………………cont.message_type = message_type;memcpy(session->buf, &cont, sizeof(cont));memcpy(session->buf + sizeof(cont), data + sent, to_copy);if (!try_send(sock, session->buf, to_copy + sizeof(cont)))return FALSE;sent += to_copy;}return TRUE;} 

5、Try_sends函數的實現

static gboolean try_send(int sk, void *data, size_t len){int err;do {err = send(sk, data, len, 0);} while (err < 0 && errno == EINTR);if (err < 0) {error("send: %s (%d)", strerror(errno), errno);return FALSE;} else if ((size_t) err != len) {error("try_send: complete buffer not sent (%d/%zu bytes)",err, len);return FALSE;}return TRUE;}

(二)、AVDTP_DISCOVER的命令發送流程如所示;
avdtp是基於l2cap的,包括控制命令的發送和資料的發送都是l2cap的,所以建立一個發送控制命令的l2cap的socket,等這個socket建立起來以後,開始發送AVDPT_DISCOVER的請求;|
`AVDTP_DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:等一系列控制命令
建立了一個l2cap的串連,等有資料過來的時候,就開始觸發邏輯,session_cb是一個非常重要的函數,這裡控制了整個串連的流程,我們下面會講,剩下的就是通過avdtp_send來發送一個AVDTP_DISCOVER的命令,這個命令的作用就是查看遠程裝置看它支援那些sep(stream end point),也就是說是否支援source,sink等;
四、AVDTP_GET_CAPABILITIES命令發送(其他代碼流程比較類似)
如所示:

這個圖在發送了avdtp discover命令以後,會被先前設立好的回呼函數執行,裡面會把遠程裝置的sep都加入到session的seps連邊裡面去,然後開始發送AVDTP_GET_CAPABILITIES命令了;
當收到遠端裝置的回複訊息後觸發調用下面的邏輯:

在系列初始化、狀態設定之後,發送哦AVDTP_SET_CONFIGURATION
五、AVDTP_SET_CONFIGURATION命令發送

發送AVDTP_OPEN命令;
六、AVDTP_OPEN的處理流程
到這裡就表示已經確立了sep和caps,開始開啟AVDTP了,如下:

數stream_setup_complete裡面會對先前的dbus訊息進行回複;
七、AVDTP_START命令發送

這裡發送AVDTP_START的命令,它的觸發是由用戶端引起的,比如aplay –Dbluetooth 2.wav的時候通過alsa提供的bluetooth的外掛程式,daemonbluetoothd-service-audio通過socket(PF_LOCAL, SOCK_STREAM,0);建立起一個socket來監聽用戶端的接入,觸發server_cb的執行,在這裡accept用戶端,並設定監聽函數client_cb,當收到用戶端的啟動流播放命令的時候就開始調用avdtp_start函數來發送命令,注意這裡設定了一個回呼函數a2dp_resume_complete,後面會被調用;當bluetoothd-service-audio收到了這個命令AVDTP_START的響應訊息時執行下面的邏輯:

進程間傳遞檔案描述符,核心層裡面的實現,通過socket發送這個檔案描述符,在核心裡面把struct file資訊傳遞給socket的peer端,它再取得一個空的fd把它和struct file關聯起來,於是就實現了檔案描述符傳遞。








安卓235的藍芽支援A2DP

A2DP全名是Advanced Audio Distribution Profile 藍芽音頻傳輸協議。它與手機系統沒有直接關係。
HTC G11是支援A2DP功能的,我們有實際測試過。
 
支援藍芽40+EDR+A2DP 是什

EDR 即Enhanced data rate,是藍芽技術中增強速率的縮寫,其特色是大大提高了藍芽技術的資料轉送速率,達到了2.1Mbps ,是目前藍芽技術的三倍。因此除了可獲得更穩定的音頻流傳送的更低的耗電量之外,還可充分利用頻寬優勢同時串連多個藍牙裝置。
A2DP全名是Advanced Audio Distribution Profile 藍芽音頻傳輸模型協定! A2DP是能夠採用耳機內的晶片來堆棧資料,達到聲音的高清晰度。有A2DP的耳機就是藍芽立體聲耳機。聲音能達到44.1kHz,一般的耳機只能達到8kHz。
 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.