Android蓝牙协议栈fluoride(九) - 音乐播放(2)

角色(Sink/Source)

前面介绍了A2DP的角色和fluoride协议栈对外提供的API,接下来将介绍fluoride的实现。首先梳理一下btif中A2DP 角色和状态各个类之间的关系,如下图:
在这里插入图片描述

在btif层用BtifAvPeer表示一个对端设备,分为两个角色:BtifAvSink、BtifAvSoure。BtifAvPeer类的成员变量和方法如下:

class BtifAvPeer {
 public:
  // flag的取值
  enum {
    kFlagLocalSuspendPending = 0x1,
    kFlagRemoteSuspend = 0x2,
    kFlagPendingStart = 0x4,
    kFlagPendingStop = 0x8,
  };
  static constexpr uint64_t kTimeoutAvOpenOnRcMs = 2 * 1000;  // 2s
  // 构造/析构BtifAvPeer
  BtifAvPeer(const RawAddress& peer_address, uint8_t peer_sep, tBTA_AV_HNDL bta_handle, uint8_t peer_id);
  ~BtifAvPeer();
  // 初始化/清理BtifAvPeer
  bt_status_t Init();
  void Cleanup();
  // 检查当前设备是否可以删除
  bool CanBeDeleted() const;
  // 检查当前设备是否为active设备
  bool IsActivePeer() const { return (PeerAddress() == ActivePeerAddress()); }
  // 获取active设备的地址
  const RawAddress& ActivePeerAddress() const;
  // 获取当前设备地址
  const RawAddress& PeerAddress() const { return peer_address_; }
  // 当前设备是否是Source角色
  bool IsSource() const { return (peer_sep_ == AVDT_TSEP_SRC); }
  // 当前角色是否是Sink角色
  bool IsSink() const { return (peer_sep_ == AVDT_TSEP_SNK); }
  // 获取当前设备的角色
  uint8_t PeerSep() const { return peer_sep_; }
  // 获取当前设备角色的UUID
  uint16_t LocalUuidServiceClass();
  // 获取/设置BTA handle
  tBTA_AV_HNDL BtaHandle() const { return bta_handle_; }
  void SetBtaHandle(tBTA_AV_HNDL bta_handle) { bta_handle_ = bta_handle; }
  // 获取peer id
  uint8_t PeerId() const { return peer_id_; }
  // 获取当前设备的状态机
  BtifAvStateMachine& StateMachine() { return state_machine_; }
  const BtifAvStateMachine& StateMachine() const { return state_machine_; }
  //  
  alarm_t* AvOpenOnRcTimer() { return av_open_on_rc_timer_; }
  const alarm_t* AvOpenOnRcTimer() const { return av_open_on_rc_timer_; }
  // 是否支持EDR
  void SetEdr(tBTA_AV_EDR edr) { edr_ = edr; }
  bool IsEdr() const { return (edr_ != 0); }
  bool Is3Mbps() const { return ((edr_ & BTA_AV_EDR_3MBPS) != 0); }
  // 是否连接、是否正在播放、是否处于静音状态
  bool IsConnected() const;
  bool IsStreaming() const;
  bool IsInSilenceMode() const { return is_silenced_; }
  // 设置当前设备为静音
  void SetSilence(bool silence) { is_silenced_ = silence; }
  // AVDTP协议中的延迟上报的值
  void SetDelayReport(uint16_t delay) { delay_report_ = delay; }
  uint16_t GetDelayReport() const { return delay_report_; }
  // 编码器相关
  void SetMandatoryCodecPreferred(bool preferred);
  bool IsMandatoryCodecPreferred() const { return mandatory_codec_preferred_; }
  // flag 相关
  bool CheckFlags(uint8_t bitflags_mask);
  void SetFlags(uint8_t bitflags_mask);
  void ClearFlags(uint8_t bitflags_mask);
  void ClearAllFlags();
  std::string FlagsToString() const;
  // 是否支持本机发起连接
  bool SelfInitiatedConnection() const { return self_initiated_connection_; }
  void SetSelfInitiatedConnection(bool v) { self_initiated_connection_ = v; }

 private:
  const RawAddress peer_address_;  //设备地址
  const uint8_t peer_sep_;  // 角色:AVDT_TSEP_SNK、AVDT_TSEP_SRC
  tBTA_AV_HNDL bta_handle_; // bta句柄
  const uint8_t peer_id_; // peer id
  BtifAvStateMachine state_machine_; // 状态机
  alarm_t* av_open_on_rc_timer_;
  tBTA_AV_EDR edr_; //是否EDR相关
  uint8_t flags_;
  bool self_initiated_connection_; // 是否支持本机发起连接
  bool is_silenced_; // 是否处于静音状态
  uint16_t delay_report_; // 用于AVDTP延迟上报
  bool mandatory_codec_preferred_ = false;
};

BtifAVSource类的成员变量和方法如下:

class BtifAvSource {
 public:
  // peerId用作BTA_AvRegister()的AppId
  static constexpr uint8_t kPeerIdMin = 0;
  static constexpr uint8_t kPeerIdMax = BTA_AV_NUM_STRS;
  BtifAvSource();
  ~BtifAvSource();
  // JNI初始化Souce,注册回调和编码器配置等
  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);
  void Cleanup();
  // 获取回调函数
  btav_source_callbacks_t* Callbacks() { return callbacks_; }
  // 是否使能
  bool Enabled() const { return enabled_; }
  bool A2dpOffloadEnabled() const { return a2dp_offload_enabled_; }
  // 查找BtifAvPeer
  BtifAvPeer* FindPeer(const RawAddress& peer_address);
  BtifAvPeer* FindPeerByHandle(tBTA_AV_HNDL bta_handle);
  BtifAvPeer* FindPeerByPeerId(uint8_t peer_id);
  // 未查找到BtifAvPeer则创建
  BtifAvPeer* FindOrCreatePeer(const RawAddress& peer_address, tBTA_AV_HNDL bta_handle);
  // 检查是否允许连接到对端设备,会考虑已连接对端设备的最大数量
  bool AllowedToConnect(const RawAddress& peer_address) const;
  // 删除一个对端设备
  bool DeletePeer(const RawAddress& peer_address);
  // 删除所有状态为Idle并可以删除的对端设备,刚创建/初始化的设备不能删除
  void DeleteIdlePeers();
  // 获取为active的对端设备
  const RawAddress& ActivePeer() const { return active_peer_; }
  // 设置对端设备为active
  bool SetActivePeer(const RawAddress& peer_address, std::promise<void> peer_ready_promise);
  // 检查对端设备是否是静音状态
  bool IsPeerSilenced(const RawAddress& peer_address);
  // 设置对端设备的静音状态
  bool SetSilencePeer(const RawAddress& peer_address, const bool silence);
  // 更新编码器配置
  void UpdateCodecConfig(const RawAddress& peer_address, const std::vector<btav_a2dp_codec_config_t>& codec_preferences, std::promise<void> peer_ready_promise);
  const std::map<RawAddress, BtifAvPeer*>& Peers() const { return peers_; }
  // 注册/注销所有peer id
  void RegisterAllBtaHandles();
  void DeregisterAllBtaHandles();
  void BtaHandleRegistered(uint8_t peer_id, tBTA_AV_HNDL bta_handle);

 private:
  void CleanupAllPeers();
  btav_source_callbacks_t* callbacks_; // JNI注册的回调
  bool enabled_; // 是否使能
  bool a2dp_offload_enabled_;
  int max_connected_peers_; // 最大连接数
  std::map<RawAddress, BtifAvPeer*> peers_; //已连接对端设备
  std::set<RawAddress> silenced_peers_; // 为静音状态的设备
  RawAddress active_peer_; // 为active的设备
  std::map<uint8_t, tBTA_AV_HNDL> peer_id2bta_handle_; // peer id对应的bta handle
};

BtifAVSink类的成员变量和方法如下:

class BtifAvSink {
 public:
  // peerId用作BTA_AvRegister()的AppId
  static constexpr uint8_t kPeerIdMin = 0;
  static constexpr uint8_t kPeerIdMax = BTA_AV_NUM_STRS;
  BtifAvSink();
  ~BtifAvSink();
  // JNI初始化Sink,注册回调函数
  bt_status_t Init(btav_sink_callbacks_t* callbacks);
  void Cleanup();
  // 获取JNI注册的回调函数
  btav_sink_callbacks_t* Callbacks() { return callbacks_; }
  // 是否使能
  bool Enabled() const { return enabled_; }
  // 查找对端设备
  BtifAvPeer* FindPeer(const RawAddress& peer_address);
  BtifAvPeer* FindPeerByHandle(tBTA_AV_HNDL bta_handle);
  BtifAvPeer* FindPeerByPeerId(uint8_t peer_id);
  // 未查找到则创建
  BtifAvPeer* FindOrCreatePeer(const RawAddress& peer_address, tBTA_AV_HNDL bta_handle);
  //检查是否允许连接到对端设备,会考虑已连接对端设备的最大数量
  bool AllowedToConnect(const RawAddress& peer_address) const;
  // 删除一个对端设备
  bool DeletePeer(const RawAddress& peer_address);
  // 删除所有状态为Idle并可以删除的对端设备,刚创建/初始化的设备不能删除
  void DeleteIdlePeers();
  // 获取为active的对端设备
  const RawAddress& ActivePeer() const { return active_peer_; }
  // 设置对端设备为active
  bool SetActivePeer(const RawAddress& peer_address, std::promise<void> peer_ready_promise);
  // 获取对端设备列表
  const std::map<RawAddress, BtifAvPeer*>& Peers() const { return peers_; }
  // 注册/注销peer id
  void RegisterAllBtaHandles();
  void DeregisterAllBtaHandles();
  void BtaHandleRegistered(uint8_t peer_id, tBTA_AV_HNDL bta_handle);

 private:
  void CleanupAllPeers();
  btav_sink_callbacks_t* callbacks_; // JNI注册的回调
  bool enabled_; // 是否使能
  int max_connected_peers_; // 最大连接数
  std::map<RawAddress, BtifAvPeer*> peers_; // 已连接的对端设备列表
  RawAddress active_peer_; // 为active的设备
  std::map<uint8_t, tBTA_AV_HNDL> peer_id2bta_handle_; // peer id对应的bta handle
};

在初始化(BtifAvSource::InitBtifAvSink::Init)时,调用BTA_AvEnable使能A2DP Profile,同时注册事件回调(bta_av_source_callbackbta_av_sink_callback),然后调用BTA_AvRegister 注册peer id获取bta handle。之后A2DP相关事件都会通过注册的回调函数上报的btif,btif集中到btif_av_handle_bta_av_event处理。在btif_av_handle_bta_av_event中根据peer_address或者bta_handle查找peer,然后进入peer的状态机处理事件。

Sink和Source两个角色是互斥的,即对端设备为Sink,则本机为Source,反之对端为Source,本机为Sink,在BtifAvSource::FindOrCreatePeerBtifAvSink::FindOrCreatePeer函数中可以看出,BtifAvSource中创建peer时设置的SEP为AVDT_TSEP_SNKBtifAvSink中创建peer时设置的SEP为AVDT_TSEP_SRC
流程如下:
在这里插入图片描述

注:xxx表示Sink或Source。

状态机

有五个状态:StateIdle、StateOpening、StateOpened、StateStarted、StateClosing,它们通过BtifAvStateMachine状态机管理,状态转换关系如下:
在这里插入图片描述
其中,Idle状态是初始状态,表示A2DP profile处于未连接状态,Opened状态表示profile已连接但未播放音乐的状态,Started状态表示正在播放音乐的状态,Opening状态表示profile连接过程中的中间状态,Closing表示profile断开连接的中间状态。

Idle状态

进入Idle状态时,先判断当前的对端设备是否是active,如果是则停止音频流,然后判断该设备是否可以删除,如果可以删除则将active设备设置为空,之后删除idle设备。判断是否可以删除的依据:如果该设备是刚刚完成创建或初始化则不删除,如果是经过状态转换到Idle状态则删除,删除Idle状态的设备可以节省资源。流程如下:
在这里插入图片描述
对于不可删除的设备,进入Idle状态,收到事件后会进行事件处理,包括以下事件:

  • BTIF_AV_STOP_STREAM_REQ_EVT
  • BTIF_AV_SUSPEND_STREAM_REQ_EVT
  • BTIF_AV_ACL_DISCONNECTED
    收到以上事件执行状态转化,切换到Idle状态,此时该设备可删除(发生状态转换切换到Idle状态的设备可删除)。
  • BTIF_AV_DISCONNECT_REQ_EVT
    调用BTA_AvClose断开断开音频通路,如果是Source设备还需要断开AVRCP连接BTA_AvCloseRc。然后转换到Idle状态。
  • BTIF_AV_CONNECT_REQ_EVT
  • BTA_AV_PENDING_EVT
    收到连接请求时首先判断对端设备是否允许连接(AllowedToConnect()),如果不允许则发出断开连接的请求BTIF_AV_DISCONNECT_REQ_EVT,之后执行的动作参考BTIF_AV_DISCONNECT_REQ_EVT处理,如果收到的事件是BTIF_AV_CONNECT_REQ_EVT则将连接请求放入队列,稍后主动发起连接。如果允许连接,则调用BTA_AvOpen连接音频通路,并切换到Opening状态。
  • BTA_AV_OPEN_EVT
    收到该状态时,首先判断open状态是否成功,如果成功则判断是否允许连接,如果不允许说明是异常状态,发出断开链接的请求BTIF_AV_DISCONNECT_REQ_EVT,进入Idle状态然后删除该设备,如果允许连接则上报BTAV_CONNECTION_STATE_CONNECTED状态到JNI,切换到Opened状态。如果open状态不是成功,则上报BTAV_CONNECTION_STATE_DISCONNECTED到JNI,切换到Idle状态。
  • AVRCP事件
    在AVRCP章节介绍。

Opening状态

进入Opening状态是向JNI上报BTAV_CONNECTION_STATE_CONNECTING状态。该状态是从Idle状态到Opened状态的过渡状态,处理的事件如下:

  • BTIF_AV_ACL_DISCONNECTED
  • BTA_AV_REJECT_EVT
  • BTA_AV_CLOSE_EVT
  • BTIF_AV_DISCONNECT_REQ_EVT
    收到该事件时首先向JNI上报BTAV_CONNECTION_STATE_DISCONNECTED状态,然后切换到Idle状态,如果允许本机发起连接(在Idle状态下BTIF_AV_CONNECT_REQ_EVT事件中设置),则将连接请求加入到队列,稍后主动发起连接。
  • BTA_AV_OPEN_EVT
    参考Idle状态下的BTA_AV_OPEN_EVT事件处理流程。
  • BTIF_AV_SINK_CONFIG_REQ_EVT
    向JNI上报对端audio配置变化的事件
  • AVRCP事件
    在AVRCP章节介绍。

Opened状态

进入Opened状态时,如果对端设备为Source,则本机的active设备为空,则将对端设备设置为active设备。事件处理如下:

  • BTIF_AV_START_STREAM_REQ_EVT
    调用BTA_AvStart响应请求,设置kFlagPendingStart flag。
  • BTA_AV_START_EVT
    如果对端是Sink设备,则响应Start命令,并启动音频流,如果是Source设备且为active则丢掉所有接收的音频帧,然后准备接收音频流,最后切换到Started状态。
  • BTIF_AV_DISCONNECT_REQ_EVT
    调用BTA_AvClose端口音频通路,向JNI上报BTAV_CONNECTION_STATE_DISCONNECTING状态,切换到Closing状态。
  • BTA_AV_CLOSE_EVT
    如果对端为active,则停止音频流,向JNI上报BTAV_CONNECTION_STATE_DISCONNECTED,切换到Idle状态。
  • BTA_AV_RECONFIG_EVT
    如果对端设备为active,则启动session,如果kFlagPendingStartflag被设置,则调用BTA_AvStart
  • AVRCP事件
    在AVRCP章节介绍。

Started状态

进入Started状态后,丢掉接收到的音频帧,然后向JNI上报audio状态BTAV_AUDIO_STATE_STARTED。事件处理如下:

  • BTIF_AV_START_STREAM_REQ_EVT
    如果对端是Sink,则只需要ack本地的请求。
  • BTIF_AV_STOP_STREAM_REQ_EVT
  • BTIF_AV_SUSPEND_STREAM_REQ_EVT
    如果对端是Sink且为active设备,或者本机未准备好接收音频流,对于BTIF_AV_STOP_STREAM_REQ_EVT,停止音频流,对于BTIF_AV_SUSPEND_STREAM_REQ_EVT,丢掉所有发送的音频帧。如果对端时Source或者不是active设备,则停止音频流。最后调用BTA_AvStop响应请求。
  • BTIF_AV_DISCONNECT_REQ_EVT
    收到事件后,首先调用BTA_AvClose断开音频流传输通路,上报BTAV_CONNECTION_STATE_DISCONNECTING状态,然后切换到Closing状态。
  • BTA_AV_SUSPEND_EVT
    对端是active设备,则通知本机停止编解码器,如果是对端暂停,则上报BTAV_AUDIO_STATE_REMOTE_SUSPEND,如果是本机暂停,则上报BTAV_AUDIO_STATE_STOPPED,然后切换到Opened状态。
  • BTA_AV_STOP_EVT
    对端是active设备,则通知本机停止编解码器,上报BTAV_AUDIO_STATE_STOPPED,然后切换到Opened状态。
  • BTA_AV_CLOSE_EVT
    对端是active设备,则通知本机停止编解码器,上报BTAV_CONNECTION_STATE_DISCONNECTED,切换到Idle状态。
  • AVRCP事件
    在AVRCP章节介绍。

Closing状态

进入Closing状态时对掉正在发送或接收到音频帧,然后进行事件处理:

  • BTA_AV_STOP_EVT
  • BTIF_AV_STOP_STREAM_REQ_EVT
    通知本机停止编解码器
  • BTA_AV_CLOSE_EVT
    上报BTAV_CONNECTION_STATE_DISCONNECTED,然后切换到Idle状态。
  • AVRCP事件
    在AVRCP章节介绍。
  • 15
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux 系统中有几种常见的蓝牙协议栈: 1. BlueZ:这是 Linux 系统中默认的蓝牙协议栈,它由 Linux 基金会开发并维护。BlueZ 提供了蓝牙的高级功能,并且可以让用户通过命令行或脚本来控制蓝牙设备。 2. Bluedroid:这是 Android 系统中的蓝牙协议栈,也可以在其他基于 Linux 的系统中使用。Bluedroid 提供了丰富的蓝牙功能,包括蓝牙协议栈、配对、连接、传输数据等。 3. BlueZ for Android:这是一个基于 BlueZ 的蓝牙协议栈,专门为 Android 系统开发。它兼容 BlueZ 命令行工具,可以让用户在 Android 系统中使用 BlueZ 命令行来控制蓝牙设备。 4. hciattach:这是 Linux 中用于驱动蓝牙硬件的工具,它可以将蓝牙硬件附加到蓝牙协议栈中,使蓝牙硬件可以与其他蓝牙设备通信。 总的来说,Linux 系统中的蓝牙协议栈主要有 BlueZ 和 Bluedroid 两种,其中 BlueZ 是 Linux 系统中的默认蓝牙协议栈,Bluedroid 则是 Android 系统中的蓝牙协议栈。 ### 回答2: Linux操作系统中有两种蓝牙协议栈可供选择。 1. BlueZ协议栈:BlueZ是Linux操作系统上最常用的蓝牙协议栈。它提供API和工具,用于在Linux设备上实现蓝牙功能。BlueZ支持几乎所有的蓝牙协议,包括传统的蓝牙核心规范(Bluetooth Core Specification)和最新的蓝牙低功耗(Bluetooth Low Energy)规范。BlueZ协议栈被广泛应用于各种Linux设备,包括智能手机、平板电脑、智能手表等。 2. Affix协议栈:Affix是一个开源的Linux蓝牙协议栈。它提供了蓝牙通信的基本功能,并可以在嵌入式系统上运行。Affix协议栈支持蓝牙核心规范,但对于蓝牙低功耗规范的支持相对有限。它适用于资源受限的嵌入式设备,如物联网设备、传感器等。 总结起来,Linux操作系统上有两种常用的蓝牙协议栈:BlueZ协议栈和Affix协议栈。其中,BlueZ协议栈是最常用和功能最全面的蓝牙协议栈,被广泛应用于各种Linux设备。而Affix协议栈则适用于资源受限的嵌入式设备。 ### 回答3: Linux操作系统支持多种蓝牙协议栈,以下是其中一些常见的蓝牙协议栈: 1. BlueZ:BlueZ是Linux操作系统上最为广泛使用的开源蓝牙协议栈。它提供了一套完整的蓝牙协议栈实现,包括蓝牙核心协议、蓝牙硬件驱动和一系列用户空间的工具和库。BlueZ支持众多的蓝牙协议和功能,如蓝牙文件传输、蓝牙耳机、蓝牙键盘等。 2. Affix:Affix是一种轻量级的蓝牙协议栈,旨在提供更小的内存占用和更低的功耗。它适用于嵌入式设备和资源受限的系统。Affix支持核心蓝牙协议,但不支持所有BlueZ的高级功能。 3. Bluedroid:Bluedroid最初是由Android平台使用的蓝牙协议栈,后来也被移植到了Linux操作系统中。Bluedroid基于BlueZ协议栈,但做了一些定制化的修改和优化,以适应移动设备的需求。 4. FluorideFluoride是谷歌开发的蓝牙协议栈,最初用于Android平台,后来也支持了Linux操作系统。Fluoride提供了一套用于开发蓝牙应用的API和工具,并且与BlueZ相比有一些特有的优化和功能。 这些蓝牙协议栈都有各自的特点和适用场景,开发者可以根据不同的需求选择合适的协议栈来进行蓝牙应用程序的开发和调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值