OpenHarmony Bluetooth Avrcp 调试指南

1 概述

​ AVRCP(Audio/Video Remote Control Profile),音视频远程控制协议。该Profile定义了AV/C数字命令控制集。命令和信息通过AVCTP(Audio/Video Control Transport Protocol)协议进行传输。浏览功能通过AVCTP的第二个channel而不是AV/C。传输媒体信息通过基于OBEX协议的BIP(Bluetooth Basic Imaging Profile)协议。


img

1.1 AVRCP应用场景

常见的使用到AVRCP控制功能的场景有如下几种:

  1. 耳机或车载等蓝牙设备控制其他设备上的音乐音源播放(手机)
  2. 远端设备控制其他设备上的视频播放

1.2 AVRCP版本差异

AVRCP协议版本变化,版本之间都是向下兼容的关系:

  • v1.0:基本的远程控制命令,如播放、暂停、切歌
  • v1.3:新增获取音乐当前的播放状态以及播放音乐的歌曲信息(歌曲总时长、当前播放位置、歌曲名、专辑名、歌手)
  • v1.4:新增浏览功能,支持绝对音量调节
  • v1.5:相关协议已通过的更改以纠正各种错误
  • v1.6:新增两个特性:
  1. 项目的数量,用于控制器的接口,请求和接收文件夹中的项数,而无需下载列表
  2. 封面艺术,支持通过基于OBEX协议上的BIP(Basic Imaging Profile)协议将图像传输到媒体项目

所以如果两端设备的AVRCP协议都支持1.6及以上,则可实现通过蓝牙传输图片的功能。由于蓝牙传输数据量的限制,该功能也只是适用于音乐专辑封面照等小数据量的传输,而不适合大批量图片的传输

1.3 AVRCP角色定义

AVRCP协议具有两个角色:CT(contorller devices)和 TG(target devices)

CT:controller控制器是通过向目标设备发送命令帧来启动事务的设备,如耳机、音箱、车载蓝牙设备

TG:target目标是接收控制器发送过来的命令帧并生成相应响应帧的设备,如手机

目前市面上的设备(手机/耳机)一般都是双角色(同时支持CT和TG)。

具体来说如果手机和一个蓝牙音箱设备连接上了,那么音箱可以控制手机播放/暂停/切歌以及获得手机上播放歌曲的信息,如专辑,歌名,歌手,时长、播放/暂停/下一曲/上一曲,以及获取播放状态等信息

img

1.4 AVRCP Feature

img


表1.1 Application layer features

C1 – Mandatory if Target supports Category 1, Optional otherwise

C2 – Mandatory if device supports Metadata Attributes for Current Media Item, Optional otherwise

C3 – Mandatory to support at least one Category

C4 – Mandatory if Category 2 supported, Excluded otherwise

C5 – Mandatory if Target supports Category 1 or Category 3, Optional otherwise

C6 – Mandatory if Browsing (item 18) is supported, optional otherwise

C7 – Optional if Category 1 or 3, or Browsing is supported, Excluded otherwise

C8 – Mandatory if any of items 10 – 20.1 are supported, Excluded otherwise

X – Excluded

C9 – Mandatory if Media Player Selection or Browsing is supported, Optional otherwise.

C10 – Mandatory if Category 1 or 3, or Browsing is supported, Optional otherwise.

1.5 AVRCP 命令

​ AVRCP协议中特有的常见命令如下图所示:

img

​ 部分命令交互日志:

img

1.5.1 AV/C Command

​ 通过AVCTP的控制通道交互,存在如下两组命令:

  • 在AV/C规范中定义的Pass Through命令、UNIT和SUBUNIT INFO命令,常用的播放、暂停、上下首切歌等控制命令都是Pass Through命令
  • AVRCP特有的AV/C命令
1.5.1.1 Pass Through命令

​ 播放、暂停、上下首切歌等控制命令

​ 下面四行表示一个命令,前面两个表示Button Pused,后面两个是Button Relaesed

img

1.5.1.2 Get Capabilities

该命令由 CT 发起,TG 回复支持的事件, CT会根据支持的事件,发起对应的 Register Notification

img

TG回复支持的EVENT ID

img

1.5.1.3 Register Notification

​ 该命令由 CT 发起,TG 回复当前状态,并在下一次状态改变时通知CT。

​ 根据TG回复的EVENT ID,注册对应的监听事件(VOLUME_CHANGD 事件 不取决于EVENT_ID回复,往往是SDP 服务中发现是否支持,一般是由audio source端发起注册。

​ 下图是一个完整的流程,表示:Register Notification ==> 回复 ==> CHANGED ==>Register Notification ==> 回复

img

1.5.2 Browsing Command

通过AVCTP的浏览通道交互,OH暂时没有应用场景,不作延申。

2 OpenHarmony上使用AVRCP

2.1 OpenHarmony上AVRCP现状:

  • 音乐应用部分没有实现AVSESSION,蓝牙无法控制音视频播放,无法获取播放信息。
  • AVRCP代码中缺失SDP_SERVICE_SEARCH_ATTR_REQ命令,该命令用于获取对端支持的feature(主要是是否支持Browsing通道和绝对音量,见表1.1),master代码正在合入
  • Avrcp 对端的默认配置不合理
  • 音量调节的接口未打通

2.2 OH AVRCP代码修改

2.2.1 修改编译配置

​ 在OH原代码中,默认打开的是ct_feature,在开发者手机目前的使用场景中,OH需要同时支持TG和CT,在绝对音量的场景中,OH作为CT,耳机/音响作为TG,其他的命令交互时,OH作为TG,耳机/音响作为CT。

​ 需要同时打开ct和tgfeature

--- a/bluetooth.gni
+++ b/bluetooth.gni
@@ -14,8 +14,8 @@
 declare_args() {
   bluetooth_service_a2dp_sink_feature = false
   bluetooth_service_a2dp_source_feature = true
   bluetooth_service_avrcp_ct_feature = true
-  bluetooth_service_avrcp_tg_feature = false
+  bluetooth_service_avrcp_tg_feature = true
   bluetooth_service_hfp_ag_feature = true
   bluetooth_service_hfp_hf_feature = false
   bluetooth_service_hid_host_feature = true

​ 在打开tg_feature后,有几处编译报错需要修改:

https://gitee.com/openharmony/communication_bluetooth_service/pulls/352/

2.2.2 Avrcp TG Features 设置

​ 在原有代码中,Tg默认对端支持所有的feature,这样会造成一些不必要的操作,比如:发起Browsing 通道的连接。在连接耳机的场景中,browsing 通道基本上用不到,即使OH上发起连接,也会因为耳机不支持而导致 browsing 通道建立失败。

这里我们需要先清除代码中关于对端feature的配置,然后基于SDP的发现结果来配置对端支持的feature。关于feature的配置,一般在profile被创建的时候赋予的,以TG为例:

--- a/services/bluetooth/service/src/avrcp_tg/avrcp_tg_service.cpp
+++ b/services/bluetooth/service/src/avrcp_tg/avrcp_tg_service.cpp
@@ -683,7 +719,14 @@ int AvrcpTgService::EnableProfile(void)
     config->GetValue(SECTION_AVRCP_TG_SERVICE, PROPERTY_CONTROL_MTU, controlMtu);
     config->GetValue(SECTION_AVRCP_TG_SERVICE, PROPERTY_BROWSE_MTU, browseMtu);

-    profile_ = std::make_unique<AvrcTgProfile>(features_,
+    profile_ = std::make_unique<AvrcTgProfile>(AVRC_TG_FEATURE_INVALID_FEATURE,
         AVRC_TG_DEFAULT_BLUETOOTH_SIG_COMPANY_ID,
         controlMtu,
         browseMtu,

​ 在连接时判断对端是否支持Browsing feature,从而决定是否建立browsing通道

​ 在原Avrcp代码中,只有服务查找的代码,没有实现 SDP_SERVICE_SEARCH_ATTR_REQ 命令和对该命令的解析,需要自己实现下

https://gitee.com/openharmony/communication_bluetooth_service/pulls/358/

2.2.3 打开AVSESSION宏后的编译问题

​ 打开AVSession 宏配置

--- a/services/bluetooth/service/BUILD.gn
+++ b/services/bluetooth/service/BUILD.gn
@@ -19,7 +19,7 @@ PART_DIR = "$SUBSYSTEM_DIR/bluetooth_service/services/bluetooth"
 BT_SERVICE_DIR = "$PART_DIR/service"

 declare_args() {
-  bluetooth_service_avrcp_avsession = false
+  bluetooth_service_avrcp_avsession = true
 }

 ServiceBleScanFilter = [ "src/ble/ble_scan_filter/src/ble_scan_filter_lsf.cpp" ]
@@ -366,7 +366,7 @@ ohos_shared_library("btservice") {
       "ability_runtime:wantagent_innerkits",
       "av_session:avsession_client",
       "input:libmmi-client",
-      "multimedia_image_framework:image_native",
+      "image_framework:image_native",
     ]
   }

​ 除此之外,还需要在bundle.json中添加image_framework模块依赖

--- a/bundle.json
+++ b/bundle.json
@@ -84,7 +84,8 @@
         "common_event_service",
         "state_registry",
         "c_utils",
-        "jsoncpp"
+        "jsoncpp",
+        "image_framework"    
       ],
       "third_party": [
         "googletest",

​ 打开宏后,还会需要一些报错,解决方案:

https://gitee.com/openharmony/communication_bluetooth_service/pulls/356

2.2.4 AVRCP连接

​ AVRCP的连接一般会由耳机主动发起,在OpenHarmony上主动连接没有做,依赖耳机发起连接;断连操作也没有做,会出现在设置中断开蓝牙耳机时,显示断开了,但是实际上还没有断开的情况

​ 可以在A2DP的断连中添加AVRCP的断连操作:

--- a/services/bluetooth/service/src/gavdp/a2dp_service.cpp
+++ b/services/bluetooth/service/src/gavdp/a2dp_service.cpp
@@ -24,6 +24,7 @@
 #include "profile_service_manager.h"
 #include "securec.h"
 #include "idevmgr_hdi.h"
+#include "avrcp_tg/avrcp_tg_service.h"
 
 constexpr const char *AUDIO_BLUETOOTH_SERVICE_NAME = "audio_bluetooth_hdi_service";
 
@@ -412,6 +413,12 @@ int A2dpService::Disconnect(const RawAddress &device)
         }
     }
 
+    auto servManager = IProfileManager::GetInstance();
+    auto service = static_cast<AvrcpTgService *>(servManager->GetProfileService(PROFILE_NAME_AVRCP_TG));
+    if (service != nullptr) {
+        service->Disconnect(device);
+    }
+
     return ret;
 }

​ 在连接过程中会根据对端的feature决定是否发起Browsing通道连接,依赖SDP发现

bool IsSupportedBrowsing(void) const
{
    return ((features_ & AVRC_TG_FEATURE_BROWSING) == AVRC_TG_FEATURE_BROWSING);
}

2.2.5 绝对音量的实现

​ 在检测到对端支持绝对音量后,OH会主动发送RegisterNotification命令注册VOLUME_CHANGD监听事件,当耳机/音响端的音量发送改变时,OH上会收到VOLUME_CHANGD Notification事件。

两种实现方法:

audio_framework中调用蓝牙接口:

audio中没有监听avrcp音量变化的部分,蓝牙部分接口还需要自己实现

// 1、设置蓝牙音量
multimedia/audio_framework/services/audio_policy/server/src/service/audio_policy_service.cpp
int32_t AudioPolicyService::SetSystemVolumeLevel(AudioStreamType streamType, int32_t volumeLevel, bool isFromVolumeKey)
===>
Bluetooth::AudioA2dpManager::SetDeviceAbsVolume(activeBTDevice_, volumeLevel);
// 蓝牙接口未实现,需要补齐

// 2、监听蓝牙音量变化
// audio中没有此部分功能代码,需要在 multimedia/audio_framework/frameworks/native/bluetoothclient 中实现相关代码
// 蓝牙接口未实现,需要补齐

Avrcp中调用audio_framework接口:

// 注册audio音量事件监听
int32_t RegisterVolumeKeyEventCallback(const int32_t clientPid,const std::shared_ptr<VolumeKeyEventCallback> &callback,  API_VERSION api_v = API_9);
AudioStandard::AudioSystemManager::GetInstance()->RegisterVolumeKeyEventCallback();
// 获取音量事件回调
VolumeKeyEventCallback::OnVloumeKeyEvent(AudioStandard::VolumeEvent volumeEvent);
// 取消注册音量事件监听
int32_t UnregisterVolumeKeyEventCallback(const int32_t clientPid)

// 调用Audio接口设置音量
AudioStandard::AudioSystemManager::GetInstance()->SetVolume(AudioStandard::AudioStreamType::STREAM_MUSIC, int volume);

// 注意: 蓝牙调用Audio的接口设置音量,也会产生音量事件回调,此时蓝牙部分应该忽略掉这个音量事件

img

​ 这里会涉及到音量的转换,OH上的音量范围(015),AVRCP中的音量范围(0127)。

uint8_t avrcpToSystemVolume(uint8_t avrcpVolume)
{
    // AVRCP_MAX_VOL 为AVRCP的最大音量 127
    return static_cast<uint8_t>(floor((double) avrcpVolume * sDeviceMaxVolume / AVRCP_MAX_VOL));
}

uint8_t systemToAvrcpVolume(uint8_t deviceVolume)
{
    // sDeviceMaxVolume 为OH设备的最大音量 15
    int avrcpVolume = static_cast<uint8_t>(ceil(static_cast<double>(deviceVolume) *
        AVRCP_MAX_VOL / sDeviceMaxVolume));
    if (avrcpVolume > AVRCP_MAX_VOL)
        avrcpVolume = AVRCP_MAX_VOL;

    return avrcpVolume;
}

​ 注意:不同设备上的音量转换方案(向上取整或者向下取整)是不一样的,当一些设备的转换方案与当前转换方案不一致时,会出现在耳机上无法将手机音量调整至100%或者0%的情况。

3 延申

3.1 关于AVSession

AVSession 简介

Avsession是OpenHarmony上的音视频播控服务,

  • 提供音视频统一管控能力,音视频类应用接入AVSession后,可以发送应用的数据(比如正在播放的歌曲、歌曲的播放状态等),用户可以通过系统播控中心、语音助手等应用切换多个应用、多个设备播放。

  • 提供音频后台约束能力,音频接入AVSession后,可以进行后台音频播放。此功能需要同时申请后台任务。

媒体会话交互过程

img

​ 蓝牙作为AVSession的媒体会话控制方,需要创建媒体会话控制器,监听会话数据,发送控制命令。

​ 关于A/C命令的部分,bluetooth_service中已经实现了 PASS_THOUGH命令,其他的AV/C 和Browsing Csommands还没有实现。

​ 由于OpenHarmony上蓝牙AVRCP的播控操作和播放内容的获取不直接与Audio_framework进行交互,而是依赖AVSession,当前的播控功能是无法使用的。因为需要音视频应用接入AVsession,并且作为AVSession的媒体会话提供方,蓝牙才能获取到媒体信息。

​ 所以如果想要实现蓝牙耳机控制音视频的功能,需要自定义音视频应用,当前社区上的Open Harmony的音乐HAP还没有接入AVSession。AVSession接入指南:

应用接入AVSession场景介绍

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐