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

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

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

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

欢迎关注Android系统攻城狮

🌻1. 前言

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

🌻2. 用法与应用场景

pcm_stoptinyalsa 库中用于紧急停止音频流传输的接口。它会将音频接口的状态从 RUNNING 强制切换回 SETUP 状态。不同于“排干”(Drain)模式,pcm_stop 是立即动作,不会等待缓冲区中剩余的音频数据播放完毕。

  • 用法int pcm_stop(struct pcm *pcm);
  • 返回值:成功返回 0;失败返回负数,错误详情可通过 pcm_get_error 获取。
  • 应用场景
  1. 即时停止播放:用户点击“停止”按钮或应用突然退出,需要立刻切断声音输出。
  2. 错误处理:当上层检测到严重的同步错误或硬件异常时,调用此函数强制复位 DMA 指针。
  3. 音频通路快速切换:在需要立即释放硬件资源以供其他高优先级流(如电话铃声)使用时。

🌻3. 调用流程剖析

3.1 核心步骤
  1. 有效性验证:检查 pcm 句柄和文件描述符 fd 是否合法。如果设备本就处于非运行状态,通常会直接返回。
  2. 执行核心 IOCTL:发起系统调用 ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP)。注意,在 ALSA 驱动层,DROP 命令代表丢弃当前所有 Buffer 并停止。
  3. 硬件 Trigger 停止:内核 ALSA 核心层接收指令后,会下发 trigger 回调给底层音频控制器(SoC 侧),关闭 DMA 传输引擎。
  4. 状态机回退:硬件停止工作后,流状态从 RUNNING 被重置为 SETUP
  5. 数据清理:与 pcm_drain 不同,该操作不会处理缓冲区中的残余数据,指针会直接归零或重置,准备迎接下一次 pcm_prepare 或重新开启。

关键技术:立即性 (Immediacy)
pcm_stop 调用的是内核的 DROP 指令而非 DRAIN。这意味着音频会产生“咔哒”一声(Pop noise)的风险(因为波形被强行切断),但在 Android HAL 中,这是处理流异常中断和快速销毁的首选方案。

3.2 涉及核心时序图
Hardware Driver (DMA) Kernel ALSA Core tinyalsa (pcm_stop) Audio HAL / App Hardware Driver (DMA) Kernel ALSA Core tinyalsa (pcm_stop) Audio HAL / App 调用 pcm_stop(pcm) 1. 检查当前是否为 RUNNING 状态 2. 发起 SNDRV_PCM_IOCTL_DROP 3. 执行硬件 Trigger (Stop) DMA 传输已立即终止 4. 更新流状态为 SETUP 返回 成功 返回 0

🌻4. 实战应用案例

此案例展示了在 Android Audio HAL 的输出流销毁或异常处理中,如何正确使用 pcm_stop 来强制释放链路。

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

/**
 * 模拟 Android HAL 层的流紧急停止逻辑
 */
void force_stop_audio_stream(struct pcm *pcm) {
    if (!pcm || !pcm_is_ready(pcm)) {
        return;
    }

    /* * 在调用 pcm_close 之前,如果需要确保硬件 DMA 立即停止,
     * 而不是等待 Buffer 播放完,则显式调用 pcm_stop。
     */
    printf("HAL: Emergency stop requested. Dropping all buffers...\n");

    int ret = pcm_stop(pcm);

    if (ret == 0) {
        printf("HAL: Stream stopped immediately and reset to SETUP state.\n");
    } else {
        fprintf(stderr, "HAL: Failed to stop pcm: %s\n", pcm_get_error(pcm));
    }
}

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

    struct pcm *out = pcm_open(0, 0, PCM_OUT, &config);
    if (!pcm_is_ready(out)) {
        if (out) pcm_close(out);
        return -1;
    }

    // 假设此处音频正在 RUNNING 状态播放
    // ...

    // 模拟由于某种中断需要立即停止播放
    force_stop_audio_stream(out);

    // 停止后可以安全关闭
    pcm_close(out);

    return 0;
}


🌻5. 用法总结

特性 详情描述
状态机迁移 强制回退。将状态从 RUNNING 直接打回至 SETUP。
执行速度 极快。直接操作硬件寄存器停止 DMA,不考虑数据完整性。
数据安全性 丢弃模式。Buffer 中尚未发送到 Codec 的数据会被全部丢弃。
对比 Drain pcm_stop 是立即切断;pcm_drain 是等待数据播放完后再优雅停止。
后续动作 调用后若需重新播放,必须重新执行 pcm_prepare 或直接写数据触发隐式启动。
Logo

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

更多推荐