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

发布时间:2023年12月22日

角色(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章节介绍。
文章来源:https://blog.csdn.net/qq_25370227/article/details/135136784
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。