当前位置: 首页 > article >正文

【Bluedroid】A2DP SINK播放流程源码分析

在Bluedroid协议栈中,A2DP(Advanced Audio Distribution Profile)SINK播放流程是一个复杂但有序的过程,它涉及多个层次和组件的交互。

一、概述

1.1. 初始化流程

在A2DP SINK播放之前,系统需要进行一系列初始化操作,以确保A2DP SINK服务能够正确运行。这些操作包括启动A2DP SINK服务、注册回调函数、初始化蓝牙接口等。

1.  启动A2DP SINK服务:

在Android系统中,A2DP SINK服务通常由A2dpSinkService类来启动和管理。这个服务在系统启动时或用户请求时启动。

  • 启动服务:当系统启动或用户请求启动A2DP SINK服务时,系统会调用A2dpSinkService类的start()方法。这个方法会负责创建并启动A2DP SINK服务的相关组件。

  • 创建状态机:在start()方法中,会调用A2dpSinkStateMachine.make()方法来创建一个A2DP SINK状态机实例。这个状态机用于管理A2DP SINK服务的各种状态转换,如连接状态、播放状态等。

  • 启动状态机:创建状态机后,需要调用其start()方法来启动状态机,使其开始监控和处理A2DP SINK服务的各种事件。

2.  注册回调函数:

在初始化过程中,需要注册一系列回调函数,以便在接收到蓝牙事件时能够及时处理。

  • 回调函数类型:这些回调函数通常包括连接状态改变回调、音频状态改变回调等。连接状态改变回调用于处理A2DP连接的建立和断开事件,音频状态改变回调用于处理音频数据的接收和播放状态变化事件。

  • 注册方法:这些回调函数会在classInitNative()initNative()等本地方法中注册到蓝牙协议栈中。这些方法通常是在Java层调用本地(Native)层代码时实现的,用于将Java层的回调函数指针传递给本地层的蓝牙协议栈。

3. 初始化蓝牙接口:

在初始化过程中,还需要获取蓝牙接口,并初始化A2DP SINK相关的蓝牙接口和回调函数。

  • 获取蓝牙接口:通过调用getBluetoothInterface()方法,可以获取到蓝牙系统的接口指针。这个接口指针提供了访问蓝牙系统各种服务和功能的方法。

  • 初始化A2DP SINK接口:通过调用get_profile_interface()等方法,可以获取到A2DP SINK相关的蓝牙接口指针。这些接口提供了A2DP SINK服务所需的各种方法和功能,如连接管理、音频数据传输等。

  • 设置回调函数:在获取到A2DP SINK接口后,需要设置一系列回调函数,以便在接收到蓝牙事件时能够及时处理。这些回调函数通常是在A2DP SINK接口初始化时设置的,用于处理连接状态变化、音频数据传输等事件。

4. 其他初始化操作:

除了上述操作外,A2DP SINK初始化流程还可能包括其他操作,如配置音频解码器、初始化音频播放系统等。这些操作的具体实现取决于Android系统的版本和配置,以及A2DP SINK服务的具体实现方式。

A2DP SINK初始化流程是一个复杂而有序的过程,涉及多个组件和层次的交互。通过正确执行这些初始化操作,可以确保A2DP SINK服务能够正常启动并运行,从而为用户提供高质量的音频播放体验。

1.2. 连接流程

在A2DP SINK设备初始化完成后,需要与A2DP SOURCE设备进行配对和连接。这个过程通常包括以下几个详细步骤:

1. 搜索设备:

  • 启用蓝牙功能:用户需要在A2DP SINK设备上启用蓝牙功能,通常通过设备的设置菜单或快捷按钮来完成。

  • 搜索蓝牙设备:一旦蓝牙功能被启用,A2DP SINK设备将开始搜索附近的蓝牙设备。这个过程可能涉及扫描周围的蓝牙信号,并列出所有可用的蓝牙设备。

2. 配对设备:

  • 选择目标设备:在搜索到的蓝牙设备列表中,用户需要选择要与A2DP SINK设备配对的A2DP SOURCE设备。

  • 输入配对码:配对过程通常需要输入一个配对码(如PIN码或密码)。这个配对码用于确保两个设备之间的安全连接。用户需要在A2DP SINK设备上输入在A2DP SOURCE设备上显示的配对码,或者相反。

  • 确认配对:一旦配对码被输入,两个设备将进行配对确认。如果配对成功,它们之间就可以建立蓝牙连接了。

3. 建立A2DP连接:

  • 发起连接请求:在配对成功后,A2DP SINK设备需要向A2DP SOURCE设备发起A2DP连接请求。这个请求通常是通过AVDTP(Audio/Video Distribution Transport Protocol)协议来发送的。AVDTP是一个用于在蓝牙设备之间传输音视频数据的协议。

  • 协商配置:在连接请求被接受之前,两个设备可能需要协商一些连接参数,如音频编解码器、采样率、通道模式等。这些参数的选择通常基于两个设备共同支持的功能和用户需求。

  • 建立连接:一旦协商完成,并且A2DP SOURCE设备接受了连接请求,两个设备之间就会建立A2DP连接。这个连接将用于传输音频数据。

4. 开始音频传输:

  • 数据传输:一旦A2DP连接建立,A2DP SOURCE设备就可以开始将音频数据通过A2DP连接传输到A2DP SINK设备了。这个过程可能涉及音频数据的编码、封装和传输。

  • 解码和播放:A2DP SINK设备在接收到音频数据后,需要对其进行解码和播放。解码过程可能涉及将音频数据从编码格式转换为PCM(Pulse Code Modulation)或其他适合播放的格式。播放过程则涉及将解码后的音频数据传递给音频播放系统,如音频输出设备或扬声器。

5. 连接管理:

  • 监控连接状态:在A2DP连接建立后,A2DP SINK设备需要持续监控连接状态,以确保连接的稳定性和可靠性。如果连接断开或出现问题,A2DP SINK设备可能需要重新发起连接请求或采取其他恢复措施。

  • 处理连接事件:A2DP SINK设备还需要处理各种连接事件,如连接请求、连接断开、连接参数变化等。这些事件可能需要通过回调函数或其他机制来处理和响应。

A2DP SINK与A2DP SOURCE设备的连接流程是一个复杂而有序的过程,涉及多个步骤和组件的交互。通过正确执行这些步骤,可以确保两个设备之间能够建立稳定可靠的A2DP连接,从而为用户提供高质量的音频播放体验。

1.3. 播放流程

在A2DP连接建立后,A2DP SINK设备就可以开始接收并播放来自A2DP SOURCE设备的音频数据了。这个过程通常包括以下几个步骤:

1. 接收音频数据:

  • AVDTP协议传输:A2DP SINK设备会通过AVDTP协议接收来自A2DP SOURCE设备的音频数据。这些数据是以音频帧的形式进行传输的,每个音频帧都包含了编码后的音频数据以及相关的元数据(如时间戳、序列号等)。

  • 数据缓冲:为了确保音频数据的连续性和稳定性,A2DP SINK设备通常会在接收端设置一个数据缓冲区。这个缓冲区用于暂存接收到的音频数据,以便在解码和播放过程中进行平滑处理。

2. 解码音频数据:

  • 解码器处理:在接收到音频数据后,A2DP SINK设备会将其传递给蓝牙协议栈中的解码器进行处理。解码器会根据音频数据的编码格式(如SBC、AAC、aptX等)进行解码操作,将编码后的音频数据转换为PCM(Pulse Code Modulation)格式或其他适合播放的格式。

  • 格式转换:解码后的音频数据可能需要进行格式转换,以符合音频播放系统的输入要求。例如,如果音频播放系统要求输入PCM格式的音频数据,而解码器输出的是其他格式的音频数据,那么就需要进行格式转换操作。

3. 播放音频数据:

  • 音频播放系统:解码后的音频数据会被传递给音频播放系统(如Android的AudioFlinger)进行播放。音频播放系统负责将音频数据转换为模拟信号,并输出到扬声器或其他音频输出设备。

  • 播放控制:在播放过程中,A2DP SINK设备可以通过音频播放系统提供的API或控制接口来实现对播放过程的控制。这些控制操作包括音量调节、播放/暂停、停止、快进/快退等。

  • 同步处理:为了确保音频数据的同步播放,A2DP SINK设备可能需要与A2DP SOURCE设备进行时间同步操作。这通常涉及到音频时钟的同步、音频帧的同步等。

4. 监控和管理:

  • 连接状态监控:在播放过程中,A2DP SINK设备需要持续监控A2DP连接的状态。如果连接断开或出现问题,A2DP SINK设备需要采取相应的恢复措施或重新发起连接请求。

  • 错误处理:在播放过程中,可能会遇到各种错误情况,如音频数据丢失、解码失败等。A2DP SINK设备需要能够检测并处理这些错误情况,以确保播放过程的稳定性和可靠性。

A2DP SINK设备的播放流程是一个复杂而有序的过程,涉及多个步骤和组件的交互。通过正确执行这些步骤,可以确保A2DP SINK设备能够稳定、可靠地接收并播放来自A2DP SOURCE设备的音频数据。

1.4. 断开连接和清理资源

在播放完成后或用户请求断开连接时,A2DP SINK设备需要与A2DP SOURCE设备断开连接,并清理相关资源。这个过程通常包括以下几个步骤:

1. 发送断开连接请求:

  • AVDTP协议发送:A2DP SINK设备通过AVDTP协议向A2DP SOURCE设备发送断开连接请求。这个请求是通知A2DP SOURCE设备,A2DP SINK设备即将结束音频数据的接收和播放,并请求断开两者之间的A2DP连接。

  • 等待确认:在发送断开连接请求后,A2DP SINK设备需要等待A2DP SOURCE设备的确认响应。这个响应表示A2DP SOURCE设备已经接收到断开连接请求,并同意断开连接。

2. 释放资源:

  • 蓝牙接口释放:一旦确认断开连接,A2DP SINK设备需要释放与蓝牙连接相关的资源。这包括关闭蓝牙接口、释放蓝牙连接所占用的内存和处理器资源等。

  • 音频播放系统释放:同时,A2DP SINK设备还需要释放与音频播放系统相关的资源。这包括停止音频播放、释放音频缓冲区、关闭音频解码器等。

  • 其他资源清理:除了蓝牙接口和音频播放系统外,A2DP SINK设备还需要清理其他与连接相关的资源,如网络连接(如果使用了网络传输辅助蓝牙连接)、定时器等。

3. 更新状态:

  • 状态机更新:在释放资源后,A2DP SINK设备需要更新其状态机中的状态。这包括将连接状态从“已连接”更改为“未连接”,并清除与连接相关的任何临时数据或状态信息。

  • 用户界面更新:如果A2DP SINK设备具有用户界面(如显示屏或指示灯),则还需要更新用户界面以反映当前已经断开连接的事实。例如,可以显示一个断开连接的提示信息或熄灭指示灯。

4. 后续处理:

  • 错误处理:如果在断开连接过程中遇到任何错误情况(如无法发送断开连接请求、无法接收到确认响应等),A2DP SINK设备需要能够检测并处理这些错误情况。这可能包括重试断开连接请求、记录错误信息以供后续分析或采取其他适当的恢复措施。

  • 日志记录:为了跟踪和诊断问题,A2DP SINK设备还可以在断开连接和清理资源的过程中记录相关的日志信息。这些日志信息可以包括断开连接的时间、原因、释放的资源类型等。

A2DP SINK设备与A2DP SOURCE设备断开连接和清理资源的过程是一个重要的步骤,它确保了资源的有效利用和系统的稳定性。通过正确执行这些步骤,可以确保A2DP SINK设备在断开连接后能够恢复到初始状态,并准备进行下一次连接和播放操作。

A2DP SINK播放流程是一个复杂但有序的过程,它涉及多个层次和组件的交互。在源码分析过程中,需要仔细理解每个步骤的实现原理和细节,以确保能够正确地实现A2DP SINK播放功能。

二、源码分析

在Bluedroid协议栈中,作为A2DPSINK(即音频接收端),设备能够接收来自A2DP源(如手机或音乐播放器)的音频流。本文从处理AV流相关事件的处理函数bta_av_proc_stream_evt开始的A2DPSINK播放流程源码分析。

2.1.bta_av_proc_stream_evt(AVDT_START_IND_EVT)

bta_av_proc_stream_evt 函数作为整个 A2DP SINK 播放流程中处理 AV 流相关事件的关键入口点。它接收各种类型的事件以及相关的数据信息作为参数,然后依据不同的事件类型进行不同的处理分支判断,就像是一个调度中心,将不同的事件导向对应的具体处理逻辑,以此来驱动整个 A2DP SINK 播放流程的运转,确保在不同的事件触发场景下(比如音频流开始传输、暂停、恢复、结束等情况),系统能够做出正确的响应并维持音频播放的正常秩序。

packages/modules/Bluetooth/system/bta/av/bta_av_aact.cc
/*******************************************************************************
 *
 * Function         bta_av_proc_stream_evt
 *
 * Description      Utility function to compose stream events.
 *
 * Returns          void
 *
 ******************************************************************************/
void bta_av_proc_stream_evt(uint8_t handle, const RawAddress& bd_addr,
                            uint8_t event, tAVDT_CTRL* p_data,
                            uint8_t scb_index) {
  CHECK_LT(scb_index, BTA_AV_NUM_STRS); //确保索引在有效范围内
  tBTA_AV_SCB* p_scb = bta_av_cb.p_scb[scb_index];
  uint16_t sec_len = 0;

  log::verbose(
      "peer_address: {} avdt_handle: {} event=0x{:x} scb_index={} p_scb={}",
      ADDRESS_TO_LOGGABLE_CSTR(bd_addr), handle, event, scb_index,
      fmt::ptr(p_scb));

  if (p_data) {
    // 安全数据的长度sec_len,确保它不超过BTA_AV_SECURITY_MAX_LEN(400)
    if (event == AVDT_SECURITY_IND_EVT) {
      sec_len = (p_data->security_ind.len < BTA_AV_SECURITY_MAX_LEN)
                    ? p_data->security_ind.len
                    : BTA_AV_SECURITY_MAX_LEN;
    } else if (event == AVDT_SECURITY_CFM_EVT && p_data->hdr.err_code == 0) {
      sec_len = (p_data->security_cfm.len < BTA_AV_SECURITY_MAX_LEN)
                    ? p_data->security_cfm.len
                    : BTA_AV_SECURITY_MAX_LEN;
    }
  }

  if (p_scb) {
    tBTA_AV_STR_MSG* p_msg =
        (tBTA_AV_STR_MSG*)osi_malloc(sizeof(tBTA_AV_STR_MSG) + sec_len);

    /* copy event data, bd addr, and handle to event message buffer */
    p_msg->hdr.offset = 0;

    p_msg->bd_addr = bd_addr;
    p_msg->scb_index = scb_index;
    log::verbose("stream event bd_addr: {} scb_index: {}",
                 ADDRESS_TO_LOGGABLE_CSTR(p_msg->bd_addr), scb_index);

    if (p_data != NULL) {
      memcpy(&p_msg->msg, p_data, sizeof(tAVDT_CTRL));
      /* copy config params to event message buffer */
      switch (event) {
        case AVDT_CONFIG_IND_EVT:
          p_msg->cfg = *p_data->config_ind.p_cfg;
          break;

        case AVDT_SECURITY_IND_EVT:
          p_msg->msg.security_ind.p_data = (uint8_t*)(p_msg + 1);
          memcpy(p_msg->msg.security_ind.p_data, p_data->security_ind.p_data,
                 sec_len);
          break;

        case AVDT_SECURITY_CFM_EVT:
          p_msg->msg.security_cfm.p_data = (uint8_t*)(p_msg + 1);
          if (p_data->hdr.err_code == 0) {
            memcpy(p_msg->msg.security_cfm.p_data, p_data->security_cfm.p_data,
                   sec_len);
          }
          break;

        case AVDT_SUSPEND_IND_EVT:
          p_msg->msg.hdr.err_code = 0;
          break;

        case AVDT_CONNECT_IND_EVT:
          p_scb->recfg_sup = true;
          p_scb->suspend_sup = true;
          break;

        default:
          break;
      }
    } else {
      p_msg->msg.hdr.err_code = 0;
    }

    /* look up application event */
    if ((p_data == NULL) || (p_data->hdr.err_code == 0)) {
      p_msg->hdr.event = bta_av_stream_evt_ok[event];
    } else {
      p_msg->hdr.event = bta_av_stream_evt_fail[event];
    }

    p_msg->initiator = false;
    if (event == AVDT_SUSPEND_CFM_EVT) p_msg->initiator = true;

    log::verbose("bta_handle:0x{:x} avdt_handle:{}", p_scb->hndl, handle);
    p_msg->hdr.layer_specific = p_scb->hndl;
    p_msg->handle = handle;
    p_msg->avdt_event = event;
    bta_sys_sendmsg(p_msg);
  }

  if (p_data) {
    bta_av_conn_cback(handle, bd_addr, event, p_data, scb_index);
  } else {
    log::error("p_data is null");
  }
}

bta_av_proc_stream_evt 的主要作用是处理与音频 / 视频(AV)流相关的事件。它通过接收事件相关的各种参数,如句柄、设备地址、事件类型等,根据不同的事件情况进行相应的数据处理和消息构造,最后将构造好的消息发送出去,并在某些情况下调用相关的回调函数,以实现对 AV 流事件的完整处理流程。

  • 消息分配与填充
    • 如果p_scb非空,分配一个tBTA_AV_STR_MSG结构体,其大小等于结构体本身加上之前计算的安全数据长度sec_len

    • 接着,将事件数据、远程设备地址、流句柄和SCB索引复制到新分配的消息缓冲区中。

    • 根据事件类型,将p_data中的相关数据复制到消息缓冲区中的相应位置。

  • 事件处理
    • 根据p_data是否为空以及p_data->hdr.err_code的值,设置消息头中的事件类型。如果p_data为空或错误代码为0,则使用成功事件;否则,使用失败事件。

    • 对于挂起确认(AVDT_SUSPEND_CFM_EVT)事件,将消息的发起者标志设置为true

  • 消息发送:设置消息头中的层特定信息和句柄,然后调用bta_sys_sendmsg函数发送消息。
  • 回调函数调用
    • 如果p_data非空,则调用bta_av_conn_cback函数,传入相关参数。这个函数用于通知应用程序或进行其他连接相关的处理。

    • 如果p_data为空,则记录一个错误日志。

通过这个函数,bluedroid能够响应来自远程设备的AVDTP请求,如配置、安全指示、安全确认、挂起指示等,并维护流的状态和配置。 

1. bta_sys_sendmsg 

packages/modules/Bluetooth/system/bta/sys/bta_sys_main.cc
/*******************************************************************************
 *
 * Function         bta_sys_sendmsg
 *
 * Description      Send a GKI message to BTA.  This function is designed to
 *                  optimize sending of messages to BTA.  It is called by BTA
 *                  API functions and call-in functions.
 *
 *                  TODO (apanicke): Add location object as parameter for easier
 *                  future debugging when doing alarm refactor
 *
 *
 * Returns          void
 *
 ******************************************************************************/
void bta_sys_sendmsg(void* p_msg) {
  if (do_in_main_thread(
          FROM_HERE,
          base::BindOnce(&bta_sys_event, static_cast<BT_HDR_RIGID*>(p_msg))) !=
      BT_STATUS_SUCCESS) {
    log::error("do_in_main_thread failed");
  }
}

bta_sys_sendmsg 的主要作用是将一个 GKI(General Kernel Interface,特定的内核接口相关)消息发送给 BTA(Bluetooth Application Layer)。是为了优化向 BTA 发送消息的过程而设计的,通常被 BTA 的 API 函数和回调函数调用。

2. bta_sys_event

packages/modules/Bluetooth/system/bta/sys/bta_sys_main.cc
/*******************************************************************************
 *
 * Function         bta_sys_event
 *
 * Description      BTA event handler; called from task event handler.
 *
 *
 * Returns          void
 *
 ******************************************************************************/
static void bta_sys_event(BT_HDR_RIGID* p_msg) {
  bool freebuf = true;

  log::verbose("Event 0x{:x}", p_msg->event);

  /* get subsystem id from event */
  uint8_t id = (uint8_t)(p_msg->event >> 8);

  /* verify id and call subsystem event handler */
  if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL)) {
    freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);
  } else {
    log::info("Ignoring receipt of unregistered event id:{}[{}]",
              BtaIdSysText(static_cast<tBTA_SYS_ID>(id)), id);
  }

  if (freebuf) {
    osi_free(p_msg);
  }
}

bta_sys_event 是一个 BTA的事件处理函数,它会在任务事件处理程序中被调用,主要负责接收并处理传入的事件消息。通过对事件消息的分析,确定所属子系统,然后调用相应子系统的事件处理程序进行处理,最后根据处理结果决定是否释放事件消息所占用的内存。

  • 获取子系统 ID:通过 uint8_t id = (uint8_t)(p_msg->event >> 8); 从事件的 event 成员值中提取出子系统 ID。将事件值右移 8 位并转换为 uint8_t 类型,得到的结果即为子系统 ID,用于后续确定该事件所属的子系统。
  • 调用相应子系统的事件处理程序:通过 if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id]!= NULL)) 条件判断来检查提取出的子系统 ID 是否在有效范围内(小于 BTA_ID_MAX)并且对应的子系统注册信息(bta_sys_cb.reg[id])是否不为空。如果这两个条件都满足,说明该子系统已注册且当前事件属于该子系统的处理范围:
    • bta_sys_cb 是一个全局的回调函数数组,用于存储不同子系统的事件处理器指针。
    • 此时调用 (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg) 函数,通过注册信息中的事件处理程序指针(evt_hdlr)来调用相应子系统的事件处理程序,并将事件消息指针 p_msg 传入,以便该子系统的事件处理程序能够根据事件消息中的具体信息进行相应的处理操作。将该调用的返回值赋给 freebuf 变量,用于后续判断是否释放事件消息的内存。
  • 处理未注册事件的情况:如果不满足上述条件,即子系统 ID 不在有效范围内或者对应的子系统注册信息为空,说明接收到的是一个未注册的事件:
    • 进行日志记录,然后直接返回,不再进行后续的针对该事件的特定处理操作,因为未注册事件可能不需要或者无法进行进一步的常规处理。
  • 根据处理结果释放内存:如果 freebuf 为 true,则调用 osi_free 函数释放消息缓冲区 p_msg

3. bta_av_hdl_event

packages/modules/Bluetooth/system/bta/av/bta_av_main.cc
/*******************************************************************************
 *
 * Function         bta_av_hdl_event
 *
 * Description      Advanced audio/video main event handling function.
 *
 *
 * Returns          bool
 *
 ******************************************************************************/
bool bta_av_hdl_event(const BT_HDR_RIGID* p_msg) {
  if (p_msg->event > BTA_AV_LAST_EVT) {
    return true; /* to free p_msg */
  }
  if (p_msg->event >= BTA_AV_FIRST_NSM_EVT) {
    log::verbose("AV nsm event=0x{:x}({})", p_msg->event,
                 bta_av_evt_code(p_msg->event));
    bta_av_non_state_machine_event(p_msg->event, (tBTA_AV_DATA*)p_msg);
  } else if (p_msg->event >= BTA_AV_FIRST_SM_EVT &&
             p_msg->event <= BTA_AV_LAST_SM_EVT) {
    log::verbose("AV sm event=0x{:x}({})", p_msg->event,
                 bta_av_evt_code(p_msg->event));
    /* state machine events */
    bta_av_sm_execute(&bta_av_cb, p_msg->event, (tBTA_AV_DATA*)p_msg);
  } else {
    log::verbose("bta_handle=0x{:x}", p_msg->layer_specific);
    /* stream state machine events */
    bta_av_ssm_execute(bta_av_hndl_to_scb(p_msg->layer_specific), p_msg->event,
                       (tBTA_AV_DATA*)p_msg);
  }
  return true;
}

bta_av_hdl_event 是用于处理高级音频 / 视频(AV)相关事件的主要函数。它根据接收到的事件消息中的事件类型,将事件分发给不同的子处理函数进行针对性处理,最后返回 true,用于指示可以释放相关的消息资源(如内等)。

a. bta_av_ssm_execute
packages/modules/Bluetooth/system/bta/av/bta_av_ssm.cc
/*******************************************************************************
 *
 * Function         bta_av_ssm_execute
 *
 * Description      Stream state machine event handling function for AV
 *
 *
 * Returns          void
 *
 ******************************************************************************/
void bta_av_ssm_execute(tBTA_AV_SCB* p_scb, uint16_t event,
                        tBTA_AV_DATA* p_data) {
  if (p_scb == NULL) {
    /* this stream is not registered */
    log::verbose("AV channel not registered");
    return;
  }

  bta_av_better_stream_state_machine(p_scb, event, p_data);
}

bta_av_ssm_execute 作为音频 / 视频(AV)流状态机事件的处理函数,主要负责对传入的 AV 流相关的状态机事件进行初步判断,并在确认相关流状态机结构体指针有效后,调用另一个函数来进一步处理具体的流状态机事件以及与之相关的数据,以此推动 AV 流相关状态的转换和处理。 

如果流控制块指针有效,将调用 bta_av_better_stream_state_machine来处理该流状态机事件。

b. bta_av_better_stream_state_machine
packages/modules/Bluetooth/system/bta/av/bta_av_ssm.cc
static void bta_av_better_stream_state_machine(tBTA_AV_SCB* p_scb,
                                               uint16_t event,
                                               tBTA_AV_DATA* p_data) {
  uint8_t previous_state = p_scb->state;
  tBTA_AV_ACT event_handler1 = nullptr;
  tBTA_AV_ACT event_handler2 = nullptr;
  switch (p_scb->state) {
    case BTA_AV_INIT_SST:
      switch (event) {
        case BTA_AV_API_OPEN_EVT:
          p_scb->state = BTA_AV_OPENING_SST;
          event_handler1 = &bta_av_do_disc_a2dp;
          break;
    ...
    case BTA_AV_OPEN_SST:
      switch (event) {
        ...
        case BTA_AV_STR_START_OK_EVT:
          event_handler1 = &bta_av_start_ok;
          break;
        ...
  }

  if (previous_state != p_scb->state) {
    log::info(
        "peer {} p_scb={:#x}({}) AV event=0x{:x}({}) state={}({}) -> {}({})",
        ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), p_scb->hndl,
        fmt::ptr(p_scb), event, bta_av_evt_code(event), previous_state,
        bta_av_sst_code(previous_state), p_scb->state,
        bta_av_sst_code(p_scb->state));

  } else {
    log::verbose("peer {} p_scb={:#x}({}) AV event=0x{:x}({}) state={}({})",
                 ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), p_scb->hndl,
                 fmt::ptr(p_scb), event, bta_av_evt_code(event), p_scb->state,
                 bta_av_sst_code(p_scb->state));
  }

  if (event_handler1 != nullptr) {
    event_handler1(p_scb, p_data);
  }
  if (event_handler2 != nullptr) {
    event_handler2(p_scb, p_data);
  }
}

bta_av_better_stream_state_machine 是处理音频 / 视频(AV)流状态机具体逻辑的核心函数。它依据当前流状态机结构体(p_scb)所处的状态以及传入的事件类型(event),通过状态机的方式确定对应的事件处理函数,然后根据状态是否发生变化进行相应的日志记录,最后调用确定好的事件处理函数来执行与 AV 流相关的具体操作,以此推动 AV 流在不同事件下的状态转换和业务处理。 

 调用事件处理函数:如果 event_handler1 不为 nullptr,则调用它来处理事件。

2.2. bta_av_start_ok 

packages/modules/Bluetooth/system/bta/av/bta_av_aact.cc
/*******************************************************************************
 *
 * Function         bta_av_start_ok
 *
 * Description      Stream started.
 *
 * Returns          void
 *
 ******************************************************************************/
void bta_av_start_ok(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
  bool initiator = false;
  bool suspend = false;
  uint8_t new_role = p_scb->role;
  BT_HDR_RIGID hdr;
  tHCI_ROLE cur_role;
  uint8_t local_tsep = p_scb->seps[p_scb->sep_idx].tsep;

  log::info("peer {} bta_handle:0x{:x} wait:0x{:x} role:0x{:x} local_tsep:{}",
            ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), p_scb->hndl,
            p_scb->wait, p_scb->role, local_tsep);

  p_scb->started = true; //表示流已成功启动

  if (local_tsep == AVDT_TSEP_SRC) {
    // The RTP Header marker bit for the A2DP Source encoder
    A2dpCodecConfig* codec_config =
        bta_av_get_a2dp_peer_current_codec(p_scb->PeerAddress());
    CHECK(codec_config != nullptr);
    p_scb->use_rtp_header_marker_bit = codec_config->useRtpHeaderMarkerBit();
  }

  if (p_scb->sco_suspend) {
    p_scb->sco_suspend = false;
  }

  if (new_role & BTA_AV_ROLE_START_INT) initiator = true;

  /* for A2DP SINK we do not send get_caps */
  if ((p_scb->avdt_handle == p_scb->seps[p_scb->sep_idx].av_handle) &&
      (local_tsep == AVDT_TSEP_SNK)) {
    p_scb->wait &= ~(BTA_AV_WAIT_ACP_CAPS_ON);
    log::verbose("local SEP type is SNK new wait is 0x{:x}", p_scb->wait);
  }
  if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_FAILED) {
    /* role switch has failed */
    log::error(
        "peer {} role switch failed: bta_handle:0x{:x} wait:0x{:x}, "
        "role:0x{:x}",
        ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), p_scb->hndl,
        p_scb->wait, p_scb->role);
    p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_FAILED;
    p_data = (tBTA_AV_DATA*)&hdr;
    hdr.offset = BTA_AV_RS_FAIL;
  }
  log::verbose("peer {} wait:0x{:x} use_rtp_header_marker_bit:{}",
               ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), p_scb->wait,
               (p_scb->use_rtp_header_marker_bit) ? "true" : "false");

  if (p_data && (p_data->hdr.offset != BTA_AV_RS_NONE)) {
    p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
    if (p_data->hdr.offset == BTA_AV_RS_FAIL) {
      bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->PeerAddress());
      tBTA_AV bta_av_data = {
          .start =
              {
                  .chnl = p_scb->chnl,
                  .hndl = p_scb->hndl,
                  .status = BTA_AV_FAIL_ROLE,
                  .initiator = initiator,
                  .suspending = false,
              },
      };
      (*bta_av_cb.p_cback)(BTA_AV_START_EVT, &bta_av_data);
      return;
    }
  }

  if (!bta_av_link_role_ok(p_scb, A2DP_SET_ONE_BIT))
    p_scb->q_tag = BTA_AV_Q_TAG_START;
  else {
    /* The wait flag may be set here while we are already central on the link */
    /* this could happen if a role switch complete event occurred during
     * reconfig */
    /* if we are now central on the link, there is no need to wait for the role
     * switch, */
    /* complete anymore so we can clear the wait for role switch flag */
    p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
  }

  if (p_scb->wait &
      (BTA_AV_WAIT_ROLE_SW_RES_OPEN | BTA_AV_WAIT_ROLE_SW_RES_START)) {
    p_scb->wait |= BTA_AV_WAIT_ROLE_SW_STARTED;
    p_scb->q_tag = BTA_AV_Q_TAG_START;
  }

  if (p_scb->wait) {
    log::error("peer {} wait:0x{:x} q_tag:{} not started",
               ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), p_scb->wait,
               p_scb->q_tag);
    /* Clear first bit of p_scb->wait and not to return from this point else
     * HAL layer gets blocked. And if there is delay in Get Capability response
     * as
     * first bit of p_scb->wait is cleared hence it ensures bt_av_start_ok is
     * not called
     * again from bta_av_save_caps.
     */
    p_scb->wait &= ~BTA_AV_WAIT_ACP_CAPS_ON;
  }

  /* tell role manager to check M/S role */
  bta_sys_conn_open(BTA_ID_AV, p_scb->app_id, p_scb->PeerAddress());

  bta_sys_busy(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->PeerAddress());

  if (p_scb->media_type == AVDT_MEDIA_TYPE_AUDIO) {
    /* in normal logic, conns should be bta_av_cb.audio_count - 1,
     * However, bta_av_stream_chg is not called to increase
     * bta_av_cb.audio_count yet.
     * If the code were to be re-arranged for some reasons, this number may need
     * to be changed
     */
    p_scb->co_started = bta_av_cb.audio_open_cnt;
  }

  /* clear the congestion flag */
  p_scb->cong = false;

  if (new_role & BTA_AV_ROLE_START_INT) {
    new_role &= ~BTA_AV_ROLE_START_INT;
  } else if ((new_role & BTA_AV_ROLE_AD_ACP) &&
             (new_role & BTA_AV_ROLE_SUSPEND_OPT)) {
    suspend = true;
  }

  if (!suspend) {
    p_scb->q_tag = BTA_AV_Q_TAG_STREAM;
    bta_av_stream_chg(p_scb, true);
  }

  {
    /* If sink starts stream, disable sniff mode here */
    if (!initiator) {
      /* If source is the central role, disable role switch during streaming.
       * Otherwise allow role switch, if source is peripheral.
       * Because it would not hurt source, if the peer device wants source to be
       * central.
       * disable sniff mode unconditionally during streaming */
      if ((BTM_GetRole(p_scb->PeerAddress(), &cur_role) == BTM_SUCCESS) &&
          (cur_role == HCI_ROLE_CENTRAL)) {
        BTM_block_role_switch_and_sniff_mode_for(p_scb->PeerAddress());
      } else {
        BTM_block_sniff_mode_fo

http://www.kler.cn/a/406912.html

相关文章:

  • 【Unity服务】如何集成Unity广告(Legacy)
  • PyTorch图像预处理:计算均值和方差以实现标准化
  • 核间通信-Linux下RPMsg使用与源码框架分析
  • 微知-ib_write_bw的各种参数汇总(-d -q -s -R --run_infinitely)
  • 【课堂笔记】隐私计算实训营第四期:“隐语”可信隐私计算开源框架
  • 滑动窗口篇——如行云流水般的高效解法与智能之道(1)
  • Python 开发工具 -- PyCharm 简介
  • Cmakelist.txt之Liunx-rabbitmq
  • 【海思Hi3519DV500】双目网络相机套板硬件规划方案
  • ansible playbook安装nacos
  • 华为HCCDA云技术认证--分布式云架构
  • 【论文笔记】LLaVA-o1: Let Vision Language Models Reason Step-by-Step
  • FastApi教程
  • 力扣 76. 最小覆盖子串
  • Java项目部署的三个阶段:java -jar、Docker和Kubernetes
  • 【H2O2|全栈】JS进阶知识(六)ES6(2)
  • HAL库的简单介绍以及环境搭建
  • 《生成式 AI》课程 作业6 大语言模型(LLM)的训练微调 Fine Tuning -- part2
  • 【环境配置】ubuntu下的保持程序一直运行
  • 【工具变量】上市公司企业信贷可得性数据(2000-2022年)
  • Unity图形学之CubeMap立方体贴图
  • 装饰器模式 (Decorator Pattern)
  • 设计模式-创建型-单例模式
  • ssm面向品牌会员的在线商城小程序
  • 【SQL Server】华中农业大学空间数据库实验报告 实验四 完整性约束
  • IDEA2024 maven构建跳过测试