ESP32 esp-idf bluetooth a2dp_sink への aptx decode 実装の考察 #4

ESP32 esp-idf bluetooth a2dp_sink への aptx decode 実装の考察 の続き、 #4 です。

A2DP aptx,aptx-hd のプロフィールのスペックは、 インターネットで探してみましたが、
結局見つかりませんでした。

注) その後、https://docs.microsoft.com/ja-jp/windows-hardware/drivers/bluetooth/bluetooth-profile-drivers-overview を見ていたら、 Details of the Bluetooth protocol are available on the Bluetooth website. があったので、見てみると、https://www.bluetooth.com/ja-jp/specifications/profiles-overview に A2DP の仕様書をみつけました。
Version1.3 から、Non-A2DP codecs (Vendor-Specific A2DP codecs) が加わったみたいです。 

しかし、Andoroid Developer の Fluoride Bluetooth stack に、A2DP の SBC,APTX,APTX-HD,LDAC
のプロファイル処理のサンプルがあったので、参考にしてみます。
Fluoride Bluetooth stack は、Andoriod Ver. 6 あたりから、Bluedroid に代わって採用されたBluetooth スタックとの事です。

サンプルは、android / platform / system / bt / master / . / stack / a2dp にあります。

プログラム階層は、
a2dp_vendor.cc > a2dp_vendor_aptx.cc > a2dp_vendor_aptx_encoder.cc
になるのでしょうか。
但し、esp32 a2dp_sink は、最後の a2dp_vendor_aptx_encoder.cc  が、ここにはありませんが、
a2dp_vendor_aptx_decoder.cc になる構造ではないでしょうか。

注) このプログラムの中には、なぜか、 a2dp_vendor_ldac_decoder.cc が含まれています。
但し、ldac decode の本体は、libldacBT_dec.so でライブラリーで提供されています。
こちらが、本来の目的でしたが?

先ずは、該当プログラムを、esp-idf 環境で、ビルド出来るか試してみます。

当面の目標は、a2dp_sink の中の、a2dp プロファイルのネゴシエーション処理に aptx を追加して、
bluetooth で繋げた PC 側で、sbc と aptx の選択画面が出てくるようにする事です。 

Windows10 PC のbluetooth ドングルを挿せば、どうやら、 APTX のコーデックが使えるとの
情報を得たので、esp32 a2dp_sink でDebug 表示をして試してみました。

どうやら、Windows10 の場合は、通常は、 SBC コーデックのコンフィグ設定が送られてきて、
sink がそれに正常の応答したら、そこで、 SBC コーデックの使用に決まってしまうようです。
その後、aptx のコーデックのコンフィグ設定の要求も来ません。

接続時の最初の、SBC コーデックのコンフィグ設定リクエスト に Bad コーデック& Reconf を返答すると、
その後、 codec_type=0x00(SBC) と codec_type=0xFF(Non-A2DP(vender-specifc)) の
getCodecConfig がそれぞれ送られてくるので、その際の codec_type=0xFF に対して、コンフィグ情報を返答する流れのようです。

念のため、この流れのコンフィグの設定で、SBCコーデックで音楽が聴けるかテストしてみます。
方法
1. bta_av_co.c::bta_av_co_audio_getconfig() の入り口で、
p_codec_info[0]=6 -> 7 に、いじります。(ここは、受信したコーデック情報の長さ=6 が通常入ります)
そうすると、bta_av_co_audio_setconfig reject s=13 c=7
(s->status, c->category の意味) でエラーをソース側に返して終了します。

その後、ソースから
bta_av_co.c::bta_av_co_audio_getconfig() : #1 hndl=41 ,codec_type=0 <- SBC
bta_av_co.c::bta_av_co_audio_getconfig() : #1 hndl=41 ,codec_type=ff  <- Non-A2DP(vender-specifc)
が、それぞれ送られてきます。

それに対する応答は、オリジナルの a2dp_sink のままの処理を実行させます。
そうすると、最終的には、再度、bta_av_co_audio_setconfig()に来る事無く、
SBC コーデック で、WindowsPC から音楽が聴こえて来る事が確認できました。

APTX のコンフィグ設定の、手順としては、
1. ソースから、audio_setconfig(SBC) が来たら、 NG で応答
2. ソースから、audio_getconfig(SBC) がきたら、NG で応答
3. ソースから、audio_getconfig(Non-A2DP) がきたら、vendor id, codec id をチェックして、 APTX であれば、OK を応答して、APTX のコンフィグを設定する。

注) Linux での確認は、未だなので、確認が必要です。

問題は、esp-idf の bt/bluedroid での A2DPの構成が、SBCのみなのが、厄介です。  

後日、CentOS7 と Android Ver 5.x で SBCコーデックので接続で、audio_setconfig(SBC) NG を返して、audio_getconfig(SBC) OK で 試してみましたが、上手く行きませんでした。どうやらこの方法では、一般的な手順とはいかないようです。audio_setconfig(SBC) が来る前にやり取りして決めている気がします。

この点に関して、A2DP​ Advanced Audi​​o Distribution Profile 1.3.1(A2DP_v1.3.1.pdf) の Page71 13.1 Audio Streaming Set Up にそれらしい記述がありました。
IDLE から OPEN に遷移する際の手順で、Stream End Point Discovery, Get All Capabilities, Stream Configuration, Stream Establishment とあります。
どうやら、Get All Capabilities で、SNK が 対応している Codec Type 等をネゴシエーションしているのか。

この処の詳しい説明は、GAVDP(Generic Audio/Video Distribution Profile) と、AVDTP(Audio/Video Distribution Transport) 仕様書を参照との事です。
AVDTP では、6 Signaling Procedures 辺りでしょうか。

特に、6.7 Get capablities 6.8 Get All Capabilites で、AVDTP_GET_CAPABILITES_CMD,_RSP, AVDTP_GET_ALL_CAPABILITES_CMD,_RSP をやり取りする記述が出ています。

さらに、8.7.2 Get Capabbilites Response, 8.8.2 Get All Capabilites Response にフォーマット記述があって、その返される情報の中に、 8.21 Service Capabilities -> 8.21.5 Media Codec Capabilities がありました。
但し、具体値は、 AV Application Profile を参照との事です。
A2DP(A2DP_v1.3.1.pdf) では、Audio codec capabilities として記述されている所がそれか?

さて、プログラム上で、この所を調べてみないといけません。 
AVDTP_GET_ALL_CAPABILITIES  に近いシンボルを検索すると、下記が出てきました。
#define AVRC_PDU_GET_CAPABILITIES               0x10

なので、このシンボルを使っている所を当たって見ることにします。
結果は、外れでした。
AVDTP は、esp-idf では、AVDT 記述している様(bt_trace.h)で、関連のプログラムは、
bt/bluedroid/stack/avdt/ 下にありました。特に avdt_api.c がAVDTP に対応する関数が入っています。
AVDT_GetAllCapReq(), avdt_get_cap_req() が求める所で、
cap -> capabilites の略で、シンボル定義は、 AVDT_SIG_GET_ALLCAP がありました。

上記ルーチンに、DEBUG表示をしましたが、なにも出てきません。
それではと、avdt_ccb_event(),avdt_scb_event() にトレース表示があったので、有効にしてみました。
関係ない部分は、省略しています。

BT_AVDT: CCB が、avdt_ccb_event()
BT_AVDT: SCB が、avdt_scb_event() のトレースです。

event=MSG_GETCAP_CMD_EVT  が、Get Capabilitiy のようです。
当初、avdt_scb_event() にいきなり、MSG_SETCONFIG_CMD_EVT が来ているので、またいきなりaudio set config がくるのかと思いましたが、
そうではなくて、avdt_ccb_event() が受けているのが AVDTP の 6.5 State Machine Overview の Idle -> Configurred の手順のようです。
では、avdt_ccb.c は何かと思いましたが、http://97.64.18.231/papers/bluedroid/avdtp_state_machine.pdf によると、
avdt_ccb means the channel control block and is located in avdt_cb.ccb[] ...
avdt_scb means the stream control block and is located in avdt_cb.scb[] with ...
と役割で分かれているようです。

そして、MSG_GETCAP_CMD_EVT で、コールされるのは、MSG_CCB_HDL_GETCAP_CMD -> avdt_ccb_act.c::avdt_ccb_hdl_getcap_cmd() である事が解りました。

但し、SRCからメッセージを受信して、メッセージID で処理を振り分けている大元がまだわかりません。
この後、SRCからのメッセージ受信時に、avdt_ccb_event() をコールしている所を調べてたいと思います。

avdt_api.c は、SRC側 のapiファンクションを提供しているのか、たとえは、AVDT_OpenReq() は、
SNK にメッセージ送信を要求して終わるのかな。
そうでもないみたいだ。
AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs)::avdt_api.c は、
bta_av_api_register(tBTA_AV_DATA *p_data)::bta_av_main.c からコールされて、
a2dp_sink でも使われている様です。

上の、avdt_scb_event() にトレースにコール元を付加して行ってみました。

event=LL_OPEN_EVT -> avdt_ad.c::avdt_ad_tc_open_ind()
event=MSG_DISCOVER_CMD_EVT & MSG_GETCAP_CMD_EVT -> avdt_msg.c::avdt_msg_ind() -> avdt_ad.c::avdt_ad_tc_data_ind()
と判りました。

結局は、最初の接続の振り分け(connect dispatcher) は、avrt_ad_tc_open_ind()
メッセージの振り分け(message dispatcher) は、avrt_msg_ind()
の様です。 

tAVDT_CB avdt_cb; ::avdt_api.c と言う大元のテーブルがあるが、
bta_av_api_register(tBTA_AV_DATA *p_data)::bta_av_main.c で、一気に作成しているようです。

このブログ記事について

このページは、おんちゃんが2018年12月24日 18:21に書いたブログ記事です。

ひとつ前のブログ記事は「ESP32 esp-idf bluetooth a2dp_sink への aptx decode 実装の考察 #3」です。

次のブログ記事は「ESP32 esp-idf bluetooth a2dp_sink への aptx decode 実装の考察 5」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

カテゴリ

ウェブページ

サイトナビ