Android内核进阶之获取PCM状态snd_pcm_status:用法实例(八十三)
简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址: Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址: 多媒体系统工程师系列【原创干货持续更新中……】🚀
推荐1:车载系统实战课地址:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
推荐2:HIDL与AIDL实战课地址:Android14 Binder之HIDL与AIDL通信实战课 🚀
推荐3:Android15音效实战课地址:Android15快速自定义与集成音效实战课 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

🍉🍉🍉文章目录🍉🍉🍉
- 🌻1. 前言
- 🌻2. Android内核进阶之获取PCM状态snd_pcm_status介绍
- 🌻3. 代码实例
- 🌻3.1 获取播放状态计算AVSync偏移
- 🌻3.2 获取录音状态检测XRUN
- 🌻3.3 获取双向状态监控USB同步偏移
- 🌻3.4 用法总结
🌻1. 前言
本篇目的:Android内核进阶之获取PCM状态snd_pcm_status:用法实例
🌻2. Android内核进阶之获取PCM状态snd_pcm_status介绍
-
基本概念
snd_pcm_status用于在任意时刻获取substream运行快照,包括指针位置、延迟、状态、触发时间戳,是调试XRUN与计算AVSync的核心数据源。 -
功能
支持查询RUNNING、XRUN、DRAINING状态;可获取hw_ptr与appl_ptr差值;提供精确到微秒的时间戳;与mmap指针无缝配合;可在原子上下文调用。 -
使用限制
只能在打开的子流上调用;status结构需清零;查询耗时低于1微秒;不可在中断内嵌套;需要对应copy_to_user回传。 -
性能特性
无锁读取runtime字段;内存占用64字节;支持16路并发查询;延迟精度小于1帧;无JNI阻塞。 -
使用场景
Android AudioFlinger计算播放偏移、车载系统检测录音欠载、USB声卡监控同步偏移。
🌻3. 代码实例
🌻3.1 获取播放状态计算AVSync偏移
-
应用场景
Android机顶盒需要把音频播放相对视频PTS对齐。 -
用法实例
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>static void get_avsync_offset(struct snd_pcm_substream *s, int64_t *offset_us)
{struct snd_pcm_status status = {0};snd_pcm_status(s, &status);/* 计算hw_ptr相对appl_ptr延迟帧数 */snd_pcm_sframes_t delay = status.hw_ptr - status.appl_ptr;*offset_us = delay * 1000000 / status.rate;
}static int android_play_open(struct snd_pcm_substream *s)
{return 0;
}static struct snd_pcm_ops android_play_ops = {open = android_play_open,ioctl = snd_pcm_lib_ioctl,hw_params = android_play_hw_params,trigger = android_play_trigger,pointer = android_play_pointer,
};static int __init avsync_status_init(void)
{int err;struct snd_card *card;struct snd_pcm *pcm;err = snd_card_new(NULL, -1, "AVSyncCard", THIS_MODULE, 0, &card);if (err < 0)return err;err = snd_pcm_new(card, "AVSyncPlay", 0, 1, 0, &pcm);if (err < 0)goto fail;snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &android_play_ops);strcpy(pcm->name, "AVSync Play");err = snd_card_register(card);if (err < 0)goto fail;return 0;
fail:snd_card_free(card);return err;
}static void __exit avsync_status_exit(void)
{struct snd_card *card = snd_card_ref(-1);if (card)snd_card_free(card);
}
module_init(avsync_status_init);
module_exit(avsync_status_exit);
MODULE_LICENSE("GPL");
代码功能:驱动通过snd_pcm_status获取实时延迟,向上层提供微秒级AVSync偏移。
🌻3.2 获取录音状态检测XRUN
-
应用场景
车载语音指令不允许出现欠载,需在驱动层即时上报。 -
用法实例
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>static void monitor_cap_xrun(struct snd_pcm_substream *s)
{struct snd_pcm_status status = {0};snd_pcm_status(s, &status);if (status.state == SNDRV_PCM_STATE_XRUN)pr_info("VoiceCap XRUN at hw_ptr %ld\n", status.hw_ptr);
}static int voice_cap_trigger(struct snd_pcm_substream *s, int cmd)
{if (cmd == SNDRV_PCM_TRIGGER_START)monitor_cap_xrun(s);return 0;
}static struct snd_pcm_ops voice_cap_ops = {open = voice_cap_open,ioctl = snd_pcm_lib_ioctl,hw_params = voice_cap_hw_params,trigger = voice_cap_trigger,pointer = voice_cap_pointer,
};static int __init xrun_status_init(void)
{int err;struct snd_card *card;struct snd_pcm *pcm;err = snd_card_new(NULL, -1, "XRunCard", THIS_MODULE, 0, &card);if (err < 0)return err;err = snd_pcm_new(card, "VoiceCap", 0, 0, 1, &pcm);if (err < 0)goto fail;snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &voice_cap_ops);strcpy(pcm->name, "Voice Capture");err = snd_card_register(card);if (err < 0)goto fail;return 0;
fail:snd_card_free(card);return err;
}static void __exit xrun_status_exit(void)
{struct snd_card *card = snd_card_ref(-1);if (card)snd_card_free(card);
}
module_init(xrun_status_init);
module_exit(xrun_status_exit);
MODULE_LICENSE("GPL");
代码功能:触发时通过snd_pcm_status检测XRUN状态,即时上报车载ECU。
🌻3.3 获取双向状态监控USB同步偏移
-
应用场景
USB声卡需要周期对比播放与捕获时间戳,保持时钟同步。 -
用法实例
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>
#include <linux/workqueue.h>static struct snd_pcm *play_pcm, *cap_pcm;
static struct work_struct sync_work;static void usb_sync_monitor(struct work_struct *work)
{struct snd_pcm_status p_st, c_st;snd_pcm_status(play_pcm, &p_st);snd_pcm_status(cap_pcm, &c_st);pr_info("USB sync diff %lld us\n",(p_st.trigger_tstamp.tv_sec * 1000000LL + p_st.trigger_tstamp.tv_usec) -(c_st.trigger_tstamp.tv_sec * 1000000LL + c_st.trigger_tstamp.tv_usec));
}static int usb_duplex_open(struct snd_pcm_substream *s)
{schedule_work(&sync_work);return 0;
}static struct snd_pcm_ops usb_duplex_ops = {open = usb_duplex_open,ioctl = snd_pcm_lib_ioctl,hw_params = usb_duplex_hw_params,trigger = usb_duplex_trigger,pointer = usb_duplex_pointer,
};static int __init usb_sync_status_init(void)
{int err;struct snd_card *card;err = snd_card_new(NULL, -1, "USBSyncCard", THIS_MODULE, 0, &card);if (err < 0)return err;err = snd_pcm_new(card, "USBDup", 0, 1, 1, &play_pcm);if (err < 0)goto fail;cap_pcm = play_pcm; /* 同卡双设备示例 */snd_pcm_set_ops(play_pcm, SNDRV_PCM_STREAM_PLAYBACK, &usb_duplex_ops);snd_pcm_set_ops(play_pcm, SNDRV_PCM_STREAM_CAPTURE, &usb_duplex_ops);INIT_WORK(&sync_work, usb_sync_monitor);strcpy(play_pcm->name, "USB Sync");err = snd_card_register(card);if (err < 0)goto fail;return 0;
fail:snd_card_free(card);return err;
}static void __exit usb_sync_status_exit(void)
{struct snd_card *card = snd_card_ref(-1);if (card)snd_card_free(card);
}
module_init(usb_sync_status_init);
module_exit(usb_sync_status_exit);
MODULE_LICENSE("GPL");
代码功能:通过snd_pcm_status获取双向trigger时间戳,计算差值后调整USB反馈端点,保持时钟一致。
🌻3.4 用法总结
| 代码关键字 | 功能描述 | 典型应用 |
|---|---|---|
| snd_pcm_status hw_ptr delay | 获取延迟 | Android AVSync |
| snd_pcm_status state XRUN | 检测欠载 | 车载语音 |
| snd_pcm_status trigger_tstamp | 同步时钟 | USB声卡 |
