简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址: Audio工程师进阶系列原创干货持续更新中……】🚀
Android多媒体专栏地址: 多媒体系统工程师系列原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

欢迎关注Android系统攻城狮

🌻1. 前言

本篇目的:Android tinyalsa 深度解析之 pcm_mmap_avail 调用流程与实战。


🌻2. 用法与应用场景

pcm_mmap_availtinyalsa 在内存映射(MMAP)模式下用于查询缓冲区可用空间的核心函数。它通过计算内核共享的指针状态,告知用户当前有多少帧空间可以写入(播放)或有多少帧数据可以读取(录音)。

  • 用法int pcm_mmap_avail(struct pcm *pcm);
  • 返回值:返回当前可用的帧数(Frames)。若发生错误或设备未就绪,返回 0 或负值。
  • 应用场景
  1. 防溢出/欠载保护:在执行 pcm_mmap_writepcm_mmap_read 前,先调用此函数确认是否有足够的空间,避免因缓冲区满或空导致的阻塞。
  2. 高性能调度:在 Audio HAL 的 FastMixer 线程中,根据可用空间大小动态决定单次处理的数据块大小,以维持极低延迟。
  3. 流状态监控:实时观察缓冲区的消耗速率,辅助调试音频卡顿(Stutter)问题。

🌻3. 调用流程剖析

3.1 核心步骤
  1. 同步内核指针:函数内部首先执行指针同步逻辑。通过读取 mmap_status 结构体(或发起 SYNC_PTR ioctl),获取最新的硬件指针()和应用指针()
  2. 区分流方向
  • 播放模式(Playback):计算公式为

这代表总缓冲区容量减去“已提交但硬件尚未播完”的数据量。

  • 录音模式(Capture):计算公式为

这代表“硬件已采集好”但“应用层尚未取走”的数据量。

  1. 边界处理(Boundary Handling):由于 ALSA 内核指针是单调递增的长整型(逻辑指针),函数会自动处理指针在到达 后的回绕逻辑,确保计算出的可用帧数始终在 范围内。
  2. 返回结果:直接返回计算后的无符号帧数。
3.2 涉及核心时序图
MMAP Shared Memory Kernel ALSA Core tinyalsa (pcm_mmap_avail) Audio HAL / Mixer MMAP Shared Memory Kernel ALSA Core tinyalsa (pcm_mmap_avail) Audio HAL / Mixer alt [播放模式] [录音模式] 调用 pcm_mmap_avail(pcm) 1. SYNC_PTR (强制刷新内核指针) 更新 hw_ptr 和 appl_ptr 2. 读取最新指针快照 计算 BufferSize - (appl_ptr - hw_ptr) 计算 hw_ptr - appl_ptr 返回可用帧数 (Frames)

🌻4. 实战应用案例

此案例展示了如何在 MMAP 写入循环中,通过 pcm_mmap_avail 实现一个非阻塞的流量控制逻辑。

#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <unistd.h>

/**
 * 演示如何利用可用空间进行智能写入
 */
void smart_mmap_playback_loop(struct pcm *pcm, void *data, unsigned int total_frames) {
    if (!pcm || !pcm_is_ready(pcm)) return;

    unsigned int frames_written = 0;
    unsigned int period_size = pcm_get_config(pcm)->period_size;

    while (frames_written < total_frames) {
        /* 1. 核心调用:查询当前硬件缓冲区还有多少空位 */
        int avail = pcm_mmap_avail(pcm);

        if (avail < 0) {
            fprintf(stderr, "HAL: 获取可用空间失败,可能发生 Xrun\n");
            break;
        }

        // 2. 策略逻辑:只有当空位足够处理一个周期时才写入
        if (avail >= (int)period_size) {
            unsigned int to_write = (avail > (total_frames - frames_written)) ? 
                                     (total_frames - frames_written) : avail;
            
            // 3. 执行写入操作
            if (pcm_mmap_write(pcm, (char*)data + pcm_frames_to_bytes(pcm, frames_written), to_write) == 0) {
                frames_written += to_write;
                printf("HAL: 写入了 %u 帧,剩余待传 %u 帧\n", to_write, total_frames - frames_written);
            }
        } else {
            // 4. 空间不足时,短时间休眠或让出 CPU,避免无效轮询
            usleep(2000); 
        }
    }
}

int main() {
    struct pcm_config config = {
        .channels = 2,
        .rate = 48000,
        .period_size = 1024,
        .period_count = 4,
        .format = PCM_FORMAT_S16_LE,
        .start_threshold = 1024,
    };

    struct pcm *pcm = pcm_open(0, 0, PCM_OUT | PCM_MMAP, &config);
    if (pcm_is_ready(pcm)) {
        pcm_prepare(pcm);
        
        char dummy_audio[16384] = {0}; // 模拟一些数据
        smart_mmap_playback_loop(pcm, dummy_audio, 4096);

        pcm_close(pcm);
    }
    return 0;
}


🌻5. 用法总结

特性 详情描述
执行开销 极低。虽然涉及内核指针同步,但在共享内存模式下其效率远高于常规 ioctl
方向差异 双向支持。自动根据播放或录音模式切换计算公式。
精度保证 单帧精度。返回的是实时的硬件剩余/积压帧数。
依赖条件 PCM_MMAP。必须在内存映射模式下调用,否则返回 0。
核心地位 流控基石。是实现自定义非阻塞音频引擎、环形缓冲区控制逻辑的最重要查询接口。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐