Android蓝牙协议栈fluoride(八) - 音乐播放与控制(1)
2023-12-21 19:16:43
概述
通常情况下音乐播放与控制这两个profile(即A2DP和AVRCP)都是同时存在的,A2DP分为Sink(SNK)和Source(SRC)两个角色,ACRVP分为Controller(CT)和Target(TG)两个角色。接下来的几篇博客将详细介绍这两个profile。
Sink和Source、CT和TG都是成对出现的。CT和TG可以同时存在在一个设备上,而Sink和Source则不能同时存在在一个设备上(不能同时工作)。角色关系如下:
A2DP profile底层依赖AVDTP协议,AVRCP底层依赖AVCTP协议,它们都基于L2CAP协议实现,SDP支持双方的服务发现。AVDTP协议实现了音频流的传输,A2DP进行角色管理以及编解码;AVRCP实现播放控制与信息同步,控制包括:播放、暂停、上一首、下一首、音量调节等,信息包括播放位置、播放状态、专辑信息、音量同步等。SIG的spec对协议栈定义如下:
接口
flouride协议栈中,A2DP的接口定义如下:
// a2dp source的回调
typedef struct {
size_t size;
// 连接状态变化上报
btav_connection_state_callback connection_state_cb;
// 音频状态变化上报
btav_audio_state_callback audio_state_cb;
// 音频配置变化上报
btav_audio_source_config_callback audio_config_cb;
btav_mandatory_codec_preferred_callback mandatory_codec_preferred_cb;
} btav_source_callbacks_t;
// a2dp source的api
typedef struct {
size_t size;
// 向协议栈注册回调
bt_status_t (*init)(btav_source_callbacks_t* callbacks, int max_connected_audio_devices, const std::vector<btav_a2dp_codec_config_t>& codec_priorities, const std::vector<btav_a2dp_codec_config_t>& offloading_preference);
// 连接指定设备
bt_status_t (*connect)(const RawAddress& bd_addr);
// 端口指定设备
bt_status_t (*disconnect)(const RawAddress& bd_addr);
// 设置指定的设备是否静音
bt_status_t (*set_silence_device)(const RawAddress& bd_addr, bool silence);
// 设置制定的设备为active,有播放时会将音频发送给该设备
bt_status_t (*set_active_device)(const RawAddress& bd_addr);
// 编码器配置
bt_status_t (*config_codec)(const RawAddress& bd_addr, std::vector<btav_a2dp_codec_config_t> codec_preferences);
void (*cleanup)(void);
} btav_source_interface_t;
// a2dp sink的回调
typedef struct {
size_t size;
// 连接状态变化上报
btav_connection_state_callback connection_state_cb;
// audio状态变化上报
btav_audio_state_callback audio_state_cb;
// audio配置变化上报
btav_audio_sink_config_callback audio_config_cb;
} btav_sink_callbacks_t;
// a2dp sink的api
typedef struct {
size_t size;
// 向协议栈注册回调
bt_status_t (*init)(btav_sink_callbacks_t* callbacks);
// 连接指定的设备
bt_status_t (*connect)(const RawAddress& bd_addr);
// 断开指定设备
bt_status_t (*disconnect)(const RawAddress& bd_addr);
void (*cleanup)(void);
// 设置音频播放焦点
void (*set_audio_focus_state)(int focus_state);
// 设置播放增益
void (*set_audio_track_gain)(float gain);
//将制定设备设置为active,即播放收到的该设备的数据
bt_status_t (*set_active_device)(const RawAddress& bd_addr);
} btav_sink_interface_t;
可以看到,没有音频流相关的API,作为Sink时,解码后的数据直接写入到Audio模块,作为Source时,直接从Audio模块获取数据进行编码,蓝牙应用层不需要关注音频流的具体内容。
AVRCP中大部分接口都可以直接从函数名理解其作用,因此文章中没有详细列出,可参考源码进行理解。
AVRCP Controller的接口如下:
// ACRCP CT的回调
typedef struct {
...
} btrc_ctrl_callbacks_t;
//ACRCP TG的API
typedef struct {
...
} btrc_ctrl_interface_t;
以前的android版本使用的AVRCP target的接口定义如下:
// AVRCP TG的回调
typedef struct {
...
} btrc_callbacks_t;
// ACRCP TG的API
typedef struct {
...
} btrc_interface_t;
旧版本TG的API渐渐的不满足AVRCP版本更新的需求,新版本API采用了面向对象的思想重新实现,接口定义如下:
// 媒体相关的回调
class MediaCallbacks {
public:
virtual void SendMediaUpdate(bool track_changed, bool play_state, bool queue) = 0;
virtual void SendFolderUpdate(bool available_players, bool addressed_players, bool uids_changed) = 0;
virtual void SendActiveDeviceChanged(const RawAddress& address) = 0;
virtual ~MediaCallbacks() = default;
};
// JNI给协议栈提供的媒体相关接口
class MediaInterface {
public:
// 将CT端的按键事件(AVRCP中的Passthrough命令)发送给应用层,其中包含播放/暂停等
virtual void SendKeyEvent(uint8_t key, KeyState state) = 0;
// 从应用层获取歌曲信息
using SongInfoCallback = base::Callback<void(SongInfo)>;
virtual void GetSongInfo(SongInfoCallback info_cb) = 0;
// 从应用层获取播放状态
using PlayStatusCallback = base::Callback<void(PlayStatus)>;
virtual void GetPlayStatus(PlayStatusCallback status_cb) = 0;
// 当前播放列表
using NowPlayingCallback = base::Callback<void(std::string, std::vector<SongInfo>)>;
virtual void GetNowPlayingList(NowPlayingCallback now_playing_cb) = 0;
// 媒体播放器列表
using MediaListCallback = base::Callback<void(uint16_t curr_player, std::vector<MediaPlayerInfo>)>;
virtual void GetMediaPlayerList(MediaListCallback list_cb) = 0;
// 获取目录中的列表
using FolderItemsCallback = base::Callback<void(std::vector<ListItem>)>;
virtual void GetFolderItems(uint16_t player_id, std::string media_id, FolderItemsCallback folder_cb) = 0;
// 浏览播放器
using SetBrowsedPlayerCallback = base::Callback<void(bool success, std::string root_id, uint32_t num_items)>;
virtual void SetBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCallback browse_cb) = 0;
// 播放指定item
virtual void PlayItem(uint16_t player_id, bool now_playing, std::string media_id) = 0;
// 设置active设备
virtual void SetActiveDevice(const RawAddress& address) = 0;
// 注册/注销媒体更新的回调
virtual void RegisterUpdateCallback(MediaCallbacks* callback) = 0;
virtual void UnregisterUpdateCallback(MediaCallbacks* callback) = 0;
MediaInterface() = default;
virtual ~MediaInterface() = default;
};
// JNI给协议栈提供的音量相关接口
class VolumeInterface {
public:
using VolumeChangedCb = base::Callback<void(int8_t volume)>;
// 不支持绝对音量时指示连接状态
virtual void DeviceConnected(const RawAddress& bdaddr) = 0;
// 支持绝对音量时指示连接状态,cb是协议栈提供给JNI调节音量的回调,调用cb时向CT发送ACRCP中的SET_ABSOLUTE_VOLUME命令
virtual void DeviceConnected(const RawAddress& bdaddr, VolumeChangedCb cb) = 0;
// 指示断开状态
virtual void DeviceDisconnected(const RawAddress& bdaddr) = 0;
// 收到CT端的AVRC_EVT_VOLUME_CHANGE事件时调用,通知应用层音量变化
virtual void SetVolume(int8_t volume) = 0;
virtual ~VolumeInterface() = default;
};
// 协议栈提供给JNI的接口
class ServiceInterface {
public:
// JNI注销接口给协议栈,mediaInterface不能为空,volumeInterface为空时不支持绝对音量
virtual void Init(MediaInterface* mediaInterface, VolumeInterface* volumeInterface) = 0;
// 注册/注销BIP服务,用于传输专辑封面信息
virtual void RegisterBipServer(int psm) = 0;
virtual void UnregisterBipServer() = 0;
// 连接/断开指定设备
virtual bool ConnectDevice(const RawAddress& bdaddr) = 0;
virtual bool DisconnectDevice(const RawAddress& bdaddr) = 0;
// 设置BIP客户端的状态
virtual void SetBipClientStatus(const RawAddress& bdaddr, bool connected) = 0;
virtual bool Cleanup() = 0;
protected:
virtual ~ServiceInterface() = default;
};
在之后的文章中不会专门描述旧接口相关的实现,主要集中在新的接口上,但旧接口和CT的接口是类似的。
文章来源:https://blog.csdn.net/qq_25370227/article/details/135088990
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!