linux內Bluetooth的協議棧爲BlueZ,http://www.bluez.org/。在4.46上,BlueZ實現了對A2DP Sink的支持,而以前的版本只支持A2DP Source。php
主機實現到HCI層,底層由藍牙芯片實現。HCI層實現的是藍牙芯片與主機通信的方式。目前通常是串口或者USB通信。所謂的USB也不是真正意義上的USB通信,而是相似與USB轉串口的方式,即經過驅動模擬USB設備實現串口通信。目前USB藍牙適配器基本都是這種設備模式。PC端實現了L2CAP, SDP, RFCOMM協議,以及USB轉串口的驅動。Windows XP SP2操做系統以上版本的都內置了這些協議棧,還有如WIDCOMM等公司提供的第三方協議棧。html
實際中只需在市場上購買這種藍牙適配器(USB接口),而後經過配置內核藍牙的接口驅動(即上圖中的HCI層驅動),這樣相應的藍牙協議(linux官方版本是bluez)就已經在內核中了,這就至關於內核驅動中已經支持了相應的藍牙協議(SDP,RFCOMM...),有了驅動就須要接口庫提供給應用程序使用,這裏用到的接口庫是開源的bluez,其實就是要在內核之上移植bluez及工具bluez-utils。linux
bluez分爲兩部分:內核代碼和用戶態程序及工具集。api
內核部分
內核代碼:bluez核心協議和驅動程序等模塊組成。自從linux2.4.6開始linux內核集成bluez。安全
HCI. 這個是最底層的了,稱爲 Host Control Interface. 之因此稱爲 HCI 是源於藍牙的應用模型的。藍牙是鏈接智能外設的無線接口,接口的一側是設備,另外一側就是主機 (Host) 了,採用相似記法的還有 USB, IEEE1394,因此,從設計初衷來看,這幾個東東都是針對差很少的市場的,固然,各有所長了。一個藍牙適配器是否能被驅動起來,就看 HCI 的支持性了。最多見的藍牙適配器就是筆者持有的這類 USB 接口的了,對於大部分標準的藍牙設備,它的驅動模塊是: hci-usb,對於咱們的 2.6 內核,插入這個適配器,該模塊就被自動加載了。服務器
L2CAP之上有兩個協議被較廣地使用着:RFCOMM和BNEP,前者用於取代傳統的串行口,包括串行口上的各類應用,好比,傳真和撥號上網、打印機、文件圖片等數據傳輸;後者則能夠提供一個以太網接口,更適於計算機組網。天然地,對於手機和計算機之間,RFCOMM 老是更常被用到。網絡
內核藍牙配置:less
[*] Networking support ---> <*> Bluetooth subsystem support ---> //藍牙子系統必須選擇 <*> L2CAP protocol suppor //邏輯鏈路控制和適配協議。 <*> SCO links support //藍牙語音和耳機支持 <*> RFCOMM protocol suppor //面向流的傳輸協議,支持撥號網絡等 [*] RFCOMM TTY support // <*> BNEP protocol support //藍牙網絡封裝協議,自組網支持 [*] Multicast filter support //藍牙多播,支持BNEP [*] Protocol filter support <*> HIDP protocol support //基本支持協議 Bluetooth device drivers ---> <*> HCI USB driver //USB藍牙模塊支持 <M>HCI UART driver //基於串口,CF卡或PCMCIA的藍牙 <*> HCI BlueFRITZ! USB driver <*> HCI VHCI (Virtual HCI device) driver
此外,在Bluetooth device drivers裏選上你所須要支持的Bluetooth設備。若使用CSR的chip,經過串口和cpu通信的,芯片默認使用BCSP做爲通信協議,因此選擇HCI UART driver和BCSP protocol support socket
如果經過usb接口使用藍牙適配器,須要選擇HCI USB driver。ide
用戶態部分
用戶態程序及工具集:應用程序接口和bluez工具集。
bluez軟件包名稱:bluez,提供bluetoothd守護進程。
bluez工具集:bluez-utils,提供bluetoothctl命令。
可用bluetoothctl完成藍牙設備配對,步驟以下:
-
(optional) Select a default controller with select MAC_address.
-
Enter power on to turn the power to the controller on. It is off by default and will turn off again each reboot, see #Auto power-on after boot.
-
Enter devices to get the MAC Address of the device with which to pair.
-
Enter device discovery mode with scan on command if device is not yet on the list.
-
Turn the agent on with agent on or choose a specific agent: if you press tab twice after agent you should see a list of available agents, e.g. DisplayOnly KeyboardDisplay NoInputNoOutput DisplayYesNo KeyboardOnly off on.
-
Enter pair MAC_address to do the pairing (tab completion works).
-
If using a device without a PIN, one may need to manually trust the device before it can reconnect successfully. Enter trust MAC_address to do so.
-
Enter connect MAC_address to establish a connection.
一個操做示例以下,鏈接藍牙音箱:
select 48:51:B7:DE:56:DD //48:51:B7:DE:56:DD爲網關藍牙模塊的地址 power off power on agent on default-agent scan on //搜索藍牙設備,等待,直到待鏈接設備被搜索到 pair FC:58:FA:5B:7E:DD //配對 connect FC:58:FA:5B:7E:DD //鏈接,會提示鏈接成功 exit //退出bluetoothctl
藍牙音頻Audio
藍牙profile
Bluetooth的一個很重要特性,就是全部的Bluetooth產品都無須實現所有的Bluetooth規範。爲了更容易的保持Bluetooth設備之間的兼容,Bluetooth規範中定義了Profile。Profile定義了設備如何實現一種鏈接或者應用,你能夠把Profile理解爲鏈接層或者應用層協議。
好比,若是一家公司但願它們的Bluetooth芯片支援全部的Bluetooth耳機,那麼它只要支持HeadSet Profile便可,而無須考慮該芯片與其它Bluetooth設備的通信與兼容性問題。若是你想購買Bluetooth產品,你應該瞭解你的應用須要哪些Profile來完成,而且確保你購買的Bluetooth產品支持這些Profile。
之因此把Profile翻譯爲配置文件,是爲避免和JavaME中的簡表混淆,配置文件也是藍牙 SIG官方網站給出的標準翻譯。
想要使用藍牙無線技術,設備必須可以翻譯特定藍牙配置文件,配置文件定義了可能的應用。藍牙配置文件表達了通常行爲,藍牙設備能夠經過這些行爲與其餘設備進行通訊。
藍牙技術定義了普遍的配置文件,描述了許多不一樣類型的使用安全。按藍牙規格中提供的指導,開發商可建立應用程序以用來與其餘符合藍牙規格的設備協同工做。在最低限度下,各配置文件規格應包含下列主題的相關信息:
1)與其餘配置文件的相關性。
2)建議的用戶界面格式。
3)配置文件使用的藍牙協議堆棧的特定部分。
爲執行其任務,每一個配置文件都使用堆棧各層上的特定選項和參數。若須要,也可包括必需的服務記錄概要。
在全部的Profile中,有四種是基本的Profile,這些Profile會被其它的Profile使用。它們是:
GAP Profile: Generic Access Profile,該Profile保證不一樣的Bluetooth產品能夠互相發現對方並創建鏈接。
SDAP Profile: Service Discovery Application Profile,經過該Profile,一個Bluetooth設備能夠找到其它Bluetooth設備提供的服務,以及查詢相關的信息。
SPP Profile: Serial Port Profile,模擬串口通信。
GOEP Profile: Generic Object Exchange Profile,通用對象交換。這個Profile的名字有些費解,它定義的是數據的傳輸,包括同步,文件傳輸,或者推送其它的數據。你能夠把它理解爲內容無關的傳輸層協議,能夠被任何應用用來傳輸本身定義的數據對象。
Bluetooth還定義了9種應用(usage)Profile。
CTP Profile: Cordless Telephone Profile,無繩電話。
IP Profile: Intercom Profile,這是在兩個設備之間創建語音鏈接,換句話說,把兩個昂貴的藍牙設備變成廉價的對講機。
HS Profile: HeadSet Profile,用於鏈接耳機。
DNP Profile: Dial-up Networking Profile,用於爲PC提供撥號網絡功能。
FP Profile: Fax Profile,傳真功能。
LAP Profile: LAN Access Profile,使用PPP協議創建局域網。
OPP Profile: Object Push Profile,用於設備之間傳輸數據對象。
FTP Profile: File Transfer Profile,用於文件傳輸。
SP Profile: Synchronization Profile,用於不一樣的Bluetooth設備同步,保持數據的一致性。
目前經常使用的配置藍牙配置(profile)是A2DP。
A2DP全名是Advanced Audio Distribution Profile 高級音頻分發配置文件,描述了立體聲音頻如何從媒體輸出(source)傳輸至輸入(sink)。A2DP是可以採用耳機內的芯片來堆棧數據,達到聲音的高清晰度。然而並不是支持A2DP的耳機就是藍牙立體聲耳機,立體聲實現的基本要求是雙聲道,因此單聲道的藍牙耳機是不能實現立體聲的。
使用場景:簡單來講,對於一個藍牙音樂播放器(MP3),音頻輸出是音樂播放器,而音頻輸入是無線耳機或無線立體聲音響。
此配置文件定義了音頻設備的兩個角色:輸出和輸入。
輸出(SRC,source):音頻的輸入端對音頻數據進行編碼,發送到Sink端。
輸入(SNK,sink):接收到音頻數據後,進行解碼操做還原出音頻。
A2DP定義了在ACL信道實現高品質音頻內容的單聲道或立體聲分發協議和程序。 所以, 「高級音頻」與「藍牙音頻」應該區別開來,後者是指根據基帶規格定義的SCO信道中分發窄幅波段的語音。
此配置文件創建在GAVDP基礎上。它包括對複雜程度低的次頻寬編解碼技術(SBC)的必備支持和對MPEG-1,2音頻、 MPEG-2,4 AAC和自適應聲學轉換編碼技術(ATRAC)的可選支持。
音頻數據按適當的格式進行壓縮後能在有限頻寬中正常使用。環繞聲的分發不在此配置文件的範圍。
PulseAudio
https://www.freedesktop.org/wiki/Software/PulseAudio/
Bluetooth的音頻應使用軟件pulseaudio,要使用藍牙耳機或音響的話要先安裝pulseaudio-bluetooth或pulseaudio-module-bluetooth。PulseAudio 5.x 開始默認支持 A2DP。
PulseAudio是一個開源的、跨平臺的、支持網絡的sound server。聲音服務器基本上就是您的聲音應用的代理者。它能夠支持從一個或多個source(進程或音頻採集設備)輸入聲音並重定向它到一個或多個sink(聲卡,遠程網絡PulseAudio server或其餘進程)。PulseAudio的目的之一就是經過它來reroute全部的音頻流。
爲了支持Bluetooth audio source,PulseAuido還實現了動態檢測藍牙音頻設備的功能。這個功能和BlueZ A2DP Sink都是由João Paulo實現的,能夠在它的blog《BlueZ now has A2DP Sink support》中找到相關信息。
PulseAudio如何從BlueZ獲得音頻數據
雖然BlueZ內部對A2DP Sink的實現較爲複雜,可是暴露給外部的數據接口確很是簡單。在bluez/audio/ipc.c中實現了三個bt_audio_service函數。PulseAudio使用bt_audio_service_open()打開一個socket,而後調用bt_audio_service_get_data_fd()獲得音頻數據文件描述符fd。這個fd是經過那個socket從BlueZ的進程傳遞到PulseAudio的進程的。最後,使用完畢,調用bt_audio_service_close()來關閉socket。PulseAudio經過D-bus和BlueZ進行通訊,進行參數的讀取和設置,決定合適的讀取時機,發送讀取的狀態。
PulseAudio從fd讀出的音頻數據流是通過SBC壓縮編碼的(對於採用其餘編碼,如MPEG-1,的狀況,本文不作討論),PulseAudio還須要對這些音頻數據流進行解碼。在BlueZ中已經實現了SBC編解碼,源文件位於bluez/sbc。PluseAudio直接使用了這些源代碼,把它們放在pulseadio/src/modules/bluetooth/sbc中。
sudo apt install pulseaudio-module-bluetooth Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: libasound2-plugins libpulsedsp libspeexdsp1 pulseaudio pulseaudio-utils rtkit Suggested packages: pavumeter pavucontrol paman paprefs The following NEW packages will be installed: libasound2-plugins libpulsedsp libspeexdsp1 pulseaudio pulseaudio-module-bluetooth pulseaudio-utils rtkit 0 upgraded, 7 newly installed, 0 to remove and 238 not upgraded. Need to get 32.2 kB/1,387 kB of archives. After this operation, 6,764 kB of additional disk space will be used. Do you want to continue? [Y/n] Get:1 http://mirrors.ustc.edu.cn/raspbian/raspbian stretch/main armhf rtkit armhf 0.11-4+deb9u1 [32.2 kB] Fetched 32.2 kB in 0s (112 kB/s) Selecting previously unselected package libspeexdsp1:armhf. (Reading database ... 126798 files and directories currently installed.) Preparing to unpack .../0-libspeexdsp1_1.2~rc1.2-1_armhf.deb ... Unpacking libspeexdsp1:armhf (1.2~rc1.2-1) ....................................................] Selecting previously unselected package libasound2-plugins:armhf...............................] Preparing to unpack .../1-libasound2-plugins_1.1.1-1_armhf.deb ... Unpacking libasound2-plugins:armhf (1.1.1-1) ..................................................] Selecting previously unselected package libpulsedsp:armhf......................................] Preparing to unpack .../2-libpulsedsp_10.0-1+deb9u1_armhf.deb ... Unpacking libpulsedsp:armhf (10.0-1+deb9u1) ...................................................] Selecting previously unselected package pulseaudio-utils.......................................] Preparing to unpack .../3-pulseaudio-utils_10.0-1+deb9u1_armhf.deb ... Unpacking pulseaudio-utils (10.0-1+deb9u1) ....................................................] Selecting previously unselected package pulseaudio.............................................] Preparing to unpack .../4-pulseaudio_10.0-1+deb9u1_armhf.deb ... Unpacking pulseaudio (10.0-1+deb9u1) ...#####..................................................] Selecting previously unselected package rtkit.####.............................................] Preparing to unpack .../5-rtkit_0.11-4+deb9u1_armhf.deb ... Unpacking rtkit (0.11-4+deb9u1) ...#################...........................................] Selecting previously unselected package pulseaudio-module-bluetooth............................] Preparing to unpack .../6-pulseaudio-module-bluetooth_10.0-1+deb9u1_armhf.deb ... Unpacking pulseaudio-module-bluetooth (10.0-1+deb9u1) ...#.....................................] Setting up libpulsedsp:armhf (10.0-1+deb9u1) ...##############.................................] Setting up pulseaudio-utils (10.0-1+deb9u1) ...####################............................] Setting up rtkit (0.11-4+deb9u1) ...###################################........................] Created symlink /etc/systemd/system/graphical.target.wants/rtkit-daemon.service → /lib/systemd/system/rtkit-daemon.service. Processing triggers for man-db (2.7.6.1-2) ...#############################....................] Processing triggers for dbus (1.10.26-0+deb9u1) ... Setting up libspeexdsp1:armhf (1.2~rc1.2-1) ... Setting up libasound2-plugins:armhf (1.1.1-1) ...###############################...............] Setting up pulseaudio (10.0-1+deb9u1) ...###########################################...........] Adding user pulse to group audio######################################################.........] Setting up pulseaudio-module-bluetooth (10.0-1+deb9u1) ...##############################.......] Processing triggers for dbus (1.10.26-0+deb9u1) ...#########################################...]
啓動pulseaudio:
/usr/bin/pulseaudio --start --log-target=syslog
pulseaudio安裝完成後,藍牙音響設備pair/connect完成後,能夠經過其提供的命令pactl查看藍牙設備,pacmd設置profile、sink等。
#pactl list cards Card #0 Name: bluez_card.FC_58_FA_5B_7E_DD Driver: module-bluez5-device.c Owner Module: 20 Properties: device.description = "A3" device.string = "FC:58:FA:5B:7E:DD" device.api = "bluez" device.class = "sound" device.bus = "bluetooth" device.form_factor = "headset" bluez.path = "/org/bluez/hci0/dev_FC_58_FA_5B_7E_DD" bluez.class = "0x260404" bluez.alias = "A3" device.icon_name = "audio-headset-bluetooth" device.intended_roles = "phone" Profiles: headset_head_unit: Headset Head Unit (HSP/HFP) (sinks: 1, sources: 1, priority: 20, available: yes) a2dp_sink: High Fidelity Playback (A2DP Sink) (sinks: 1, sources: 0, priority: 10, available: yes) off: Off (sinks: 0, sources: 0, priority: 0, available: yes) Active Profile: headset_head_unit Ports: headset-output: Headset (priority: 0, latency offset: 0 usec) Part of profile(s): headset_head_unit, a2dp_sink headset-input: Headset (priority: 0, latency offset: 0 usec) Part of profile(s): headset_head_unit # pactl list sinks Sink #1 State: SUSPENDED Name: bluez_sink.FC_58_FA_5B_7E_DD Description: A3 Driver: module-bluez5-device.c Sample Specification: s16le 1ch 8000Hz Channel Map: mono Owner Module: 20 Mute: no Volume: mono: 65536 / 100% balance 0.00 Base Volume: 65536 / 100% Monitor Source: bluez_sink.FC_58_FA_5B_7E_56.monitor Latency: 0 usec, configured 0 usec Flags: HARDWARE HW_VOLUME_CTRL LATENCY Properties: bluetooth.protocol = "headset_head_unit" device.intended_roles = "phone" device.description = "A3" device.string = "FC:58:FA:5B:7E:DD" device.api = "bluez" device.class = "sound" device.bus = "bluetooth" device.form_factor = "headset" bluez.path = "/org/bluez/hci0/dev_FC_58_FA_5B_7E_DD" bluez.class = "0x260404" bluez.alias = "A3" device.icon_name = "audio-headset-bluetooth" Ports: headset-output: Headset (priority: 0) Active Port: headset-output Formats: pcm
pacmd獲取到音響card索引號和sink索引號後,可經過pactl設置profile和默認輸出:
pacmd set-card-profile 0 a2dp_sink // Card #0 pacmd set-default-sink 1 // Sink #1
設置完成後,可用pulseaudio自帶的paplay播放wav格式音頻文件:
paplay test.wav
至此,藍牙音響可播放出聲音。
參考: