接上一篇继承分析:【Bluedroid】A2DP Sink播放流程源码分析(一)_安卓a2dp sink播放流程-CSDN博客
AVDTP接收端(Sink)流变乱处置惩罚
bta_av_sink_data_cback 是 Bluedroid 中 A2DP Sink 脚色的 AVDTP 数据回调函数,负责处置惩罚接收端的音频数据变乱,将底层接收到的音频数据传递给上层模块。
bta_av_sink_data_cback
- packages/modules/Bluetooth/system/bta/av/bta_av_aact.cc
- /*******************************************************************************
- *
- * Function bta_av_sink_data_cback
- *
- * Description This is the AVDTP callback function for sink stream events.
- *
- * Returns void
- *
- ******************************************************************************/
- void bta_av_sink_data_cback(uint8_t handle, BT_HDR* p_pkt, uint32_t time_stamp,
- uint8_t m_pt) {
- int index = 0;
- tBTA_AV_SCB* p_scb;
- log::verbose(
- "avdt_handle: {} pkt_len=0x{:x} offset = 0x{:x} number of frames 0x{:x} "
- "sequence number 0x{:x}",
- handle, p_pkt->len, p_pkt->offset,
- *((uint8_t*)(p_pkt + 1) + p_pkt->offset), p_pkt->layer_specific);
-
- // 1. 查找流控制块(SCB)
- /* Get SCB and correct sep type */
- for (index = 0; index < BTA_AV_NUM_STRS; index++) {
- p_scb = bta_av_cb.p_scb[index]; // 存储流连接的状态(如角色、句柄、SEP 类型等)
- if ((p_scb->avdt_handle == handle) &&
- (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK)) {
- break;
- }
- }
- if (index == BTA_AV_NUM_STRS) {
- /* cannot find correct handler */
- osi_free(p_pkt);
- return;
- }
- // 2. 触发上层回调
- p_pkt->event = BTA_AV_SINK_MEDIA_DATA_EVT; // 设置事件类型为 Sink 媒体数据事件
- // 上层回调:通过 p_app_sink_data_cback 将数据传递给 A2DP Sink 应用层(如音频解码、播放模块),触发后续处理(如数据解析、缓冲区写入)
- p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(
- p_scb->PeerAddress(), BTA_AV_SINK_MEDIA_DATA_EVT, (tBTA_AV_MEDIA*)p_pkt);
- /* Free the buffer: a copy of the packet has been delivered */
- osi_free(p_pkt);
- }
复制代码 bta_av_sink_data_cback 用于处置惩罚蓝牙 AVDTP接收端(Sink)流变乱的回调函数。重要作用是在接收到相关的数据包后,根据数据包对应的句柄等信息找到对应的处置惩罚上下文(通过查找 SCB),然后对数据包进行一些必要的处置惩罚,如设置变乱类型,并调用相应的应用层回调函数将数据包及相关变乱信息传递给上层应用进行进一步处置惩罚,末了释放数据包所占用的内存,确保整个接收端流变乱处置惩罚流程的完整性以及内存资源的合理管理。
关键作用
- 数据通路桥梁:毗连 AVDTP 层与上层应用,将原始音频数据转换为 Sink 脚色可处置惩罚的变乱。
- 状态校验:通过 SCB 筛选确保仅处置惩罚 Sink 脚色的合法流毗连,避免跨脚色数据误处置惩罚。
- 错误隔离:未找到 SCB 时实时释放内存,防止无效指针操纵导致的崩溃。
bta_av_sink_media_callback (BTA_AV_SINK_MEDIA_DATA_EVT)
- packages/modules/Bluetooth/system/btif/src/btif_av.cc
- // TODO: All processing should be done on the JNI thread
- static void bta_av_sink_media_callback(const RawAddress& peer_address,
- tBTA_AV_EVT event,
- tBTA_AV_MEDIA* p_data) {
- log::verbose("event={}", event);
- switch (event) {
- case BTA_AV_SINK_MEDIA_DATA_EVT: { // 当接收到媒体数据时触发
- BtifAvPeer* peer = btif_av_sink_find_peer(peer_address);
- if (peer != nullptr && peer->IsActivePeer()) {
- int state = peer->StateMachine().StateId();
- if ((state == BtifAvStateMachine::kStateStarted) ||
- (state == BtifAvStateMachine::kStateOpened)) {
- uint8_t queue_len = btif_a2dp_sink_enqueue_buf((BT_HDR*)p_data);
- log::verbose("Packets in Sink queue {}", queue_len);
- }
- }
- break;
- }
- ...
- default:
- break;
- }
- }
复制代码 bta_av_sink_media_callback 是一个回调函数,重要用于处置惩罚蓝牙音频 / 视频(AV)接收端(Sink)相关的媒体变乱。依据接收到的不同媒体变乱类型来实行相应操纵,目的是协调蓝牙 A2DP 接收端在接收媒体数据时与系统内其他部分的交互,比如根据接收端状态决定是否将接收到的数据入队等操纵,以此保障蓝牙音频 / 视频接收流程能正常进行。
btif_a2dp_sink_enqueue_buf
- packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
- uint8_t btif_a2dp_sink_enqueue_buf(BT_HDR* p_pkt) {
- // 1. 加锁与刷新检查
- LockGuard lock(g_mutex);
- if (btif_a2dp_sink_cb.rx_flush) /* Flush enabled, do not enqueue */
- return fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue);
- log::verbose("+");
- // 2. 分配内存并复制数据包
- /* Allocate and queue this buffer */
- BT_HDR* p_msg =
- reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(*p_msg) + p_pkt->len));
- memcpy(p_msg, p_pkt, sizeof(*p_msg));
- p_msg->offset = 0;
- memcpy(p_msg->data, p_pkt->data + p_pkt->offset, p_pkt->len);
- // 将新的消息结构体添加到接收音频队列 rx_audio_queue 中
- fixed_queue_enqueue(btif_a2dp_sink_cb.rx_audio_queue, p_msg);
- // 3. 队列长度检查与处理
- if (fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue) ==
- MAX_INPUT_A2DP_FRAME_QUEUE_SZ) { // 队列已满
- osi_free(fixed_queue_try_dequeue(btif_a2dp_sink_cb.rx_audio_queue));
- uint8_t ret = fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue);
- return ret;
- }
- // 4. 解码启动检查
- // Avoid other checks if alarm has already been initialized.
- if (btif_a2dp_sink_cb.decode_alarm == nullptr &&
- fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue) >=
- MAX_A2DP_DELAYED_START_FRAME_COUNT) {
- log::verbose("Initiate decoding. Current focus state:{}",
- btif_a2dp_sink_cb.rx_focus_state);
- if (btif_a2dp_sink_cb.rx_focus_state == BTIF_A2DP_SINK_FOCUS_GRANTED) {
- btif_a2dp_sink_audio_handle_start_decoding();
- }
- }
- return fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue);
- }
复制代码 处置惩罚蓝牙 A2DP接收端(Sink)的数据入队操纵。通过合理的加锁机制保障多线程环境下资源访问的安全性,根据革新机制决定是否入队数据,在入队过程中进行内存分配、数据复制以及队列长度控制等操纵,同时还能根据特定条件触发音频解码相关操纵,确保接收端音频数据能够精确地缓存、管理以及适时地进行解码播放,对于维持整个蓝牙音频接收和播放流程的顺畅以及资源的合理利用有着重要意义。
fixed_queue_enqueue
- packages/modules/Bluetooth/system/osi/src/fixed_queue.cc
- void fixed_queue_enqueue(fixed_queue_t* queue, void* data) {
- CHECK(queue != NULL);
- CHECK(data != NULL);
- //等待入队信号量。如果队列已满(即入队信号量的计数为0),则当前线程将阻塞在这里,直到有其他线程从队列中移除了元素并调用了 semaphore_post(queue->enqueue_sem)
- semaphore_wait(queue->enqueue_sem);
- {
- std::lock_guard<std::mutex> lock(*queue->mutex);
- list_append(queue->list, data); // 将数据添加到队列的列表中
- }
- // 增加出队信号量的计数。表示队列中有新的元素可供出队操作,如果之前有线程在等待出队信号量(即队列为空且线程尝试从队列中取出元素),则这些线程现在可以继续执行
- semaphore_post(queue->dequeue_sem);
- }
复制代码 向一个固定队列(fixed_queue_t 类型的队列)中添加元素(通过 void* data 参数传入要添加的数据指针),在添加过程中,通过严酷的参数检查、合理的信号量控制以及互斥锁保护下的队列元素添加操纵,在多线程环境下实现了安全、可靠的队列入队功能。通过控制入队和出队信号量的期待与释放,还能有用地协调多个线程对队列的并发利用,避免出现数据竞争、不同等等并发题目,保障了整个固定队列数据结构在多线程系统中的正常运行,对于需要在并发环境下进行数据缓存、排队处置惩罚等应用场景有着重要的支持作用。
btif_a2dp_sink_audio_handle_start_decoding
- packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
- // Must be called while locked.
- static void btif_a2dp_sink_audio_handle_start_decoding() {
- log::info("");
- if (btif_a2dp_sink_cb.decode_alarm != nullptr)
- return; // Already started decoding
- #ifdef __ANDROID__
- BtifAvrcpAudioTrackStart(btif_a2dp_sink_cb.audio_track);
- #endif
- //创建一个新的周期性定时器,并将其地址存储在 btif_a2dp_sink_cb.decode_alarm 中
- btif_a2dp_sink_cb.decode_alarm = alarm_new_periodic("btif.a2dp_sink_decode");
- if (btif_a2dp_sink_cb.decode_alarm == nullptr) {
- log::error("unable to allocate decode alarm");
- return;
- }
- // 设置定时器的触发间隔为 BTIF_SINK_MEDIA_TIME_TICK_MS(20,定时器触发的毫秒间隔)
- // 当定时器触发时,将调用 btif_decode_alarm_cb 回调函数(传递 nullptr 作为参)
- alarm_set(btif_a2dp_sink_cb.decode_alarm, BTIF_SINK_MEDIA_TIME_TICK_MS,
- btif_decode_alarm_cb, nullptr);
- }
复制代码 负责启动蓝牙 A2DP(接收端(Sink)的音频解码相关操纵。在调用时要求处于加锁状态,以确保在多线程环境下操纵相关共享资源(如 btif_a2dp_sink_cb 结构体相关成员)的安全性。起首通过检查避免重复启动解码操纵,然后针对安卓平台进行相应的音频播放前置操纵(在__ANDROID__环境下),接着创建周期性的定时器(或称为“alarm”)并设置好相关参数和回调函数,以此来建立起一个定时实行音频解码任务的机制,保障音频数据能够在合适的时间间隔下连续地被解码处置惩罚,为后续的音频播放等流程提供解码后的数据支持。
下面分析SBC编码写入audiotrack的过程,别的编码格式的如法炮制。
btif_decode_alarm_cb
- packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
- static void btif_decode_alarm_cb(void* /* context */) {
- LockGuard lock(g_mutex);
- btif_a2dp_sink_cb.worker_thread.DoInThread(
- FROM_HERE, base::BindOnce(btif_a2dp_sink_avk_handle_timer));
- }
复制代码 btif_decode_alarm_cb 函数重要作为一个回调函数来利用,其核心功能是在被触发时,先通过加锁机制确保线程安全地访问共享资源,然后安排在特定的工作线程中实行另一个函数 btif_a2dp_sink_avk_handle_timer,以此实现一种异步的、线程安全的操纵触发机制,用于在特定的定时或变乱触发场景下,让相关的音频处置惩罚逻辑(由 btif_a2dp_sink_avk_handle_timer 函数具体实现)能够在合适的线程环境中有序实行,保障音频相关功能的精确运行。
btif_a2dp_sink_avk_handle_timer
- packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
- static void btif_a2dp_sink_avk_handle_timer() {
- // 1. 加锁操作
- LockGuard lock(g_mutex);
- // 2. 检查队列是否为空
- BT_HDR* p_msg; // 用于后续存储从队列中取出的数据包
- if (fixed_queue_is_empty(btif_a2dp_sink_cb.rx_audio_queue)) {
- log::verbose("empty queue");
- return;
- }
- // 3. 检查焦点状态
- /* Don't do anything in case of focus not granted */
- if (btif_a2dp_sink_cb.rx_focus_state == BTIF_A2DP_SINK_FOCUS_NOT_GRANTED) {
- log::verbose("skipping frames since focus is not present");
- return;
- }
- // 4. 检查刷新标志
- /* Play only in BTIF_A2DP_SINK_FOCUS_GRANTED case */
- if (btif_a2dp_sink_cb.rx_flush) {
- // 清空接收音频队列,并释放队列中每个元素的内存
- fixed_queue_flush(btif_a2dp_sink_cb.rx_audio_queue, osi_free);
- return;
- }
- // 5. 处理队列中的数据包
- log::verbose("process frames begin");
- while (true) { // 使用 while 循环不断从队列中取出数据包,直到队列为空(即 fixed_queue_try_dequeue 返回 NULL)
- p_msg = (BT_HDR*)fixed_queue_try_dequeue(btif_a2dp_sink_cb.rx_audio_queue);
- if (p_msg == NULL) {
- break;
- }
- log::verbose("number of packets in queue {}",
- fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue));
- /* Queue packet has less frames */
- btif_a2dp_sink_handle_inc_media(p_msg); // 处理取出的音频数据包
- osi_free(p_msg);
- }
- log::verbose("process frames end");
- }
复制代码 btif_a2dp_sink_avk_handle_timer 函数是一个定时器处置惩罚函数,用于处置惩罚 A2DP Sink 端接收到的音频数据包队列。在定时器触发时被调用,对 A2DP Sink 端的音频数据包队列进行检查和处置惩罚。根据队列状态、焦点状态和革新标志来决定是直接返回、清空队列还是逐个处置惩罚队列中的数据包,确保音频数据的处置惩罚符合系统的状态和要求。
btif_a2dp_sink_handle_inc_media
- packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
- // Must be called while locked.
- static void btif_a2dp_sink_handle_inc_media(BT_HDR* p_msg) {
- if ((btif_av_get_peer_sep() == AVDT_TSEP_SNK) ||
- (btif_a2dp_sink_cb.rx_flush)) {
- log::verbose("state changed happened in this tick");
- return;
- }
- CHECK(btif_a2dp_sink_cb.decoder_interface != nullptr);
- if (!btif_a2dp_sink_cb.decoder_interface->decode_packet(p_msg)) {
- log::error("decoding failed");
- }
- }
复制代码 确保当前状态答应的情况下,调用解码器接口来解码接收到的A2DP音频数据包。保障了音频数据在合适的条件下能够被合理地进行解码操纵,对于维持音频播放等后续功能的正常运行有着重要意义。
a2dp_sbc_decoder_decode_packet
- packages/modules/Bluetooth/system/stack/a2dp/a2dp_sbc_decoder.cc
- bool a2dp_sbc_decoder_decode_packet(BT_HDR* p_buf) {
- // 1. 输入数据准备
- uint8_t* data = p_buf->data + p_buf->offset;
- size_t data_size = p_buf->len;
- if (data_size == 0) {
- log::error("Empty packet");
- return false;
- }
- size_t num_frames = data[0] & 0xf; // 提取帧数量(低4位)
- data += 1; // 跳过帧数量字段,指向实际音频数据
- data_size -= 1;
- // 2. 逐帧解码
- const OI_BYTE* oi_data = data; // 转换为 OI 库所需的字节类型
- uint32_t oi_size = data_size; // 剩余待解码数据大小
- size_t out_avail = sizeof(a2dp_sbc_decoder_cb.decode_buf); // 输出缓冲区总大小
- int16_t* out_ptr = a2dp_sbc_decoder_cb.decode_buf; // 输出缓冲区指针(PCM 数据)
- for (size_t i = 0; i < num_frames; ++i) {
- uint32_t out_size = out_avail; // 当前帧解码所需的输出空间
- // 解码库调用
- OI_STATUS status = OI_CODEC_SBC_DecodeFrame(
- &a2dp_sbc_decoder_cb.decoder_context, // 解码上下文(包含编解码参数)
- &oi_data, // 输入数据指针(按帧递增)
- &oi_size, // 剩余输入数据大小(按帧递减)
- out_ptr, // 输出 PCM 数据指针
- &out_size // 实际解码出的 PCM 数据大小
- );
- if (!OI_SUCCESS(status)) {
- log::error("Decoding failure: {}", status);
- return false; // 解码失败,终止处理
- }
- out_avail -= out_size; // 更新剩余输出空间
- out_ptr += out_size / sizeof(*out_ptr); // 移动输出指针(按 int16 单位)
- }
- // 3. 解码结果回调
- size_t out_used = (out_ptr - a2dp_sbc_decoder_cb.decode_buf) * sizeof(*out_ptr);
- // 上层回调:通过 decode_callback 将解码后的 PCM 数据传递给上层(如音频播放模块),触发后续处理(如写入音频输出设备)
- a2dp_sbc_decoder_cb.decode_callback(
- reinterpret_cast<uint8_t*>(a2dp_sbc_decoder_cb.decode_buf), // PCM 数据起始地址
- out_used // 实际使用的字节数
- );
- }
复制代码 a2dp_sbc_decoder_decode_packet 是 A2DP Sink 端 SBC(Subband Coding,子带编码)编解码器的核心解码函数,负责将接收到的 SBC 格式音频数据包解码为 PCM 数据,供上层音频播放模块利用。其核心逻辑包括 数据包解析、逐帧解码 和 结果回调。
OI_CODEC_SBC_DecodeFrame
- packages/modules/Bluetooth/system/embdrv/sbc/decoder/srce/decoder-sbc.c
- OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT* context,
- const OI_BYTE** frameData,
- uint32_t* frameBytes, int16_t* pcmData,
- uint32_t* pcmBytes) {
- OI_STATUS status;
- OI_UINT framelen;
- uint8_t crc;
- TRACE(("+OI_CODEC_SBC_DecodeFrame"));
- // 1. 同步字查找(Syncword Detection)
- TRACE(("Finding syncword"));
- status = FindSyncword(context, frameData, frameBytes);
- if (!OI_SUCCESS(status)) {
- return status;
- }
- // 2. 头部解析与参数校验
- /* Make sure enough data remains to read the header. */
- if (*frameBytes < SBC_HEADER_LEN) {
- TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA"));
- return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA;
- }
- if (context->mSbcEnabled) {
- /*
- * There is no parameter embedded in mSBC's header as the parameters are
- * fixed unlike the general SBC. We only need the packet's crc for mSBC.
- */
- context->common.frameInfo.crc = (*frameData)[3]; // mSBC 仅需 CRC
- } else {
- TRACE(("Reading Header"));
- OI_SBC_ReadHeader(&context->common, *frameData); // 普通 SBC 解析完整头部
- }
- /*
- * Some implementations load the decoder into RAM and use overlays for 4 vs 8
- * subbands. We need
- * to ensure that the SBC parameters for this frame are compatible with the
- * restrictions imposed
- * by the loaded overlays.
- */
- // // 参数校验:子带数量、通道数、PCM 步长等
- if (context->limitFrameFormat &&
- (context->common.frameInfo.subbands != context->restrictSubbands)) {
- ERROR(("SBC parameters incompatible with loaded overlay"));
- return OI_STATUS_INVALID_PARAMETERS;
- }
- if (context->common.frameInfo.nrof_channels > context->common.maxChannels) {
- ERROR(
- ("SBC parameters incompatible with number of channels specified during "
- "reset"));
- return OI_STATUS_INVALID_PARAMETERS;
- }
- if (context->common.pcmStride < 1 || context->common.pcmStride > 2) {
- ERROR(("PCM stride not set correctly during reset"));
- return OI_STATUS_INVALID_PARAMETERS;
- }
- /*
- * At this point a header has been read. However, it's possible that we found
- * a false syncword,
- * so the header data might be invalid. Make sure we have enough bytes to read
- * in the
- * CRC-protected header, but don't require we have the whole frame. That way,
- * if it turns out
- * that we're acting on bogus header data, we don't stall the decoding process
- * by waiting for
- * data that we don't actually need.
- */
- framelen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo);
- if (*frameBytes < framelen) {
- TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA"));
- return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; // 数据不足
- }
- TRACE(("Calculating checksum"));
- // 3. 帧长度计算与数据完整性校验
- crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData);
- if (crc != context->common.frameInfo.crc) {
- TRACE(("CRC Mismatch: calc=%02x read=%02x\n", crc,
- context->common.frameInfo.crc));
- TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_CHECKSUM_MISMATCH"));
- return OI_CODEC_SBC_CHECKSUM_MISMATCH; // CRC 校验失败
- }
- // 4. 比特池合法性检查
- /*
- * Make sure the bitpool values are sane.
- */
- if ((context->common.frameInfo.bitpool < SBC_MIN_BITPOOL) &&
- !context->common.frameInfo.enhanced) {
- ERROR(("Bitpool too small: %d (must be >= 2)",
- context->common.frameInfo.bitpool));
- return OI_STATUS_INVALID_PARAMETERS;
- }
- if (context->common.frameInfo.bitpool >
- OI_SBC_MaxBitpool(&context->common.frameInfo)) {
- ERROR(("Bitpool too large: %d (must be <= %ld)",
- context->common.frameInfo.bitpool,
- OI_SBC_MaxBitpool(&context->common.frameInfo)));
- return OI_STATUS_INVALID_PARAMETERS;
- }
- // 5. 音频数据解码(核心逻辑)
- /*
- * Now decode the SBC data. Partial decode is not yet implemented for an SBC
- * stream, so pass FALSE to decode body to have it enforce the old rule that
- * you have to decode a whole packet at a time.
- */
- status = DecodeBody(context, *frameData + SBC_HEADER_LEN, pcmData, pcmBytes,
- FALSE);
- if (OI_SUCCESS(status)) {
- *frameData += framelen; // 移动数据指针到下一帧
- *frameBytes -= framelen; // 更新剩余数据大小
- }
- TRACE(("-OI_CODEC_SBC_DecodeFrame: %d", status));
- // 6. mSBC 特殊处理
- /* mSBC is designed with 8 bits of zeros at the end for padding. */
- if (context->mSbcEnabled) {
- *frameBytes -= 1;
- }
- return status;
- }
复制代码 OI_CODEC_SBC_DecodeFrame 是 SBC 音频解码的核心函数,负责对单个 SBC 音频帧进行解码,将编码后的音频数据转换为 PCM 格式。其核心流程包括 同步字查找、头部解析、参数校验、CRC 校验 和 音频数据解码,是 A2DP Sink 端音频解码的关键环节。兼顾了平凡 SBC 和 mSBC(Modified SBC,改进型SBC)两种模式,通过状态机和上下文管理实现高效的帧级解码。理解此函数有助于分析音频解码中的常见题目(如杂音、解码失败),并为编解码器优化(如提升解码速度、降低功耗)提供切入点。
继承回到a2dp_sbc_decoder_decode_packet分析回调处置惩罚。
btif_a2dp_sink_on_decode_complete
- packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
- static void btif_a2dp_sink_on_decode_complete(uint8_t* data, uint32_t len) {
- #ifdef __ANDROID__
- BtifAvrcpAudioTrackWriteData(btif_a2dp_sink_cb.audio_track,
- reinterpret_cast<void*>(data), len);
- #endif
- }
复制代码 btif_a2dp_sink_on_decode_complete 作为一个回调函数,用于在音频数据解码完成后进行后续的处置惩罚操纵。在 Android 平台下,调用BtifAvrcpAudioTrackWriteData函数,将解码后的音频数据写入到音频通路。
BtifAvrcpAudioTrackWriteData
- packages/modules/Bluetooth/system/btif/src/btif_avrcp_audio_track.cc
- int BtifAvrcpAudioTrackWriteData(void* handle, void* audioBuffer,
- int bufferLength) {
- // 1. 参数校验与初始化
- BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
- CHECK(trackHolder != NULL);
- CHECK(trackHolder->stream != NULL); // 确保音频流句柄有效
- aaudio_result_t retval = -1;
- // 2. 调试数据 Dump(可选)
- #if (DUMP_PCM_DATA == TRUE)
- if (outputPcmSampleFile) { // 将接收到的音频数据直接写入文件,用于调试阶段分析原始音频数据
- fwrite((audioBuffer), 1, (size_t)bufferLength, outputPcmSampleFile);
- }
- #endif
- // 3. 样本大小计算
- // 根据音频格式(如 16 位 PCM、浮点型)返回单个样本的字节大小。例如:
- // 16 位 PCM 立体声:每个样本 2 字节 × 2 通道 = 4 字节 / 帧。
- // 32 位浮点立体声:每个样本 4 字节 × 2 通道 = 8 字节 / 帧。
- size_t sampleSize = sampleSizeFor(trackHolder);
- // 4. 数据转码与分段写入
- int transcodedCount = 0;
- do {
- // 转码:将输入数据(如 SBC 解码后的 PCM)转为 AAudio 所需的浮点格式
- transcodedCount += transcodeToPcmFloat(
- ((uint8_t*)audioBuffer) + transcodedCount, // 输入数据指针(按转码进度递增)
- bufferLength - transcodedCount, // 剩余待转码数据长度
- trackHolder // 轨道句柄(含编码格式、通道数等信息)
- );
- // 写入 AAudio 流:参数为流句柄、目标缓冲区、样本数量、超时时间
- retval = AAudioStream_write(
- trackHolder->stream, // AAudio 流句柄
- trackHolder->buffer, // 转码后的浮点 PCM 缓冲区
- transcodedCount / (sampleSize * trackHolder->channelCount), // 样本数量(总字节数 / 单样本字节数)
- kTimeoutNanos // 写入超时时间(纳秒级)
- );
- log::verbose("Track.cpp: btWriteData len = {} ret = {}", bufferLength,
- retval);
- } while (transcodedCount < bufferLength);
- return transcodedCount;
- }
复制代码 BtifAvrcpAudioTrackWriteData 是 AVRCP 音频轨道的数据写入函数,负责将接收到的音频数据转换为可播放的 PCM 格式,并通过 AAudio 框架写入音频流,最终输出到音频装备。其核心逻辑包括 数据转码、AAudio 流写入 和 调试数据 Dump。
AAudio | Android NDK | Android Developers
流程图
a2dp sink起播和播放的流程图
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |