如何分析tinyplay 播放音频和tinymix的过程?需要相应的工具来支持追查;

一、分析tinyplay和tinymix:

1.1 利用strace工具:

strace -o tinyplay.log tinyplay 1.wav

strace -o tinymixer.log tinymixer "SEC_MI2S_RX Audio Mixer MultiMedia1" 1

利用strace工具获取APP的log,从应用层往下看;

1.2 分析alsa-utils源码:

tiny工具源码在android/external/tinyalsa目录下;

二、tinyplay调用分析(tinyplay.log搜索设备节点“/dev/snd/pcmC0D0p”)

2.1 tinyplay的open过程:

snd_pcm_f_ops[0]是播放音频的file_operations,snd_pcm_f_ops[1]则是录音的file_operations:

 const struct file_operations snd_pcm_f_ops[] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};

我们从snd_pcm_playback_open函数开始向下分析:

 static int snd_pcm_playback_open(struct inode *inode, struct file *file)
{
struct snd_pcm *pcm;
int err = nonseekable_open(inode, file);
if (err < )
return err;
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);  //取得其私有数据并返回的
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
if (pcm)
snd_card_unref(pcm->card);      //减少设备对象的引用计数 snd_card_unref(card);
return err;
}

在下面调用了snd_pcm_open函数:

 static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
int err;
wait_queue_t wait; if (pcm == NULL) {
err = -ENODEV;
goto __error1;
}
err = snd_card_file_add(pcm->card, file);
if (err < )
goto __error1;
if (!try_module_get(pcm->card->module)) {
err = -EFAULT;
goto __error2;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&pcm->open_wait, &wait);
mutex_lock(&pcm->open_mutex);
while () {
err = snd_pcm_open_file(file, pcm, stream);  // 将操作该声卡card的应用程序添加到card->files_list
if (err >= )
break;
if (err == -EAGAIN) {
if (file->f_flags & O_NONBLOCK) {
err = -EBUSY;
break;
}
} else
break;
set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&pcm->open_mutex);
schedule();
mutex_lock(&pcm->open_mutex);
if (pcm->card->shutdown) {
err = -ENODEV;
break;
}
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
}
}
remove_wait_queue(&pcm->open_wait, &wait);
mutex_unlock(&pcm->open_mutex);
if (err < )
goto __error;
return err; __error:
module_put(pcm->card->module);
__error2:
snd_card_file_remove(pcm->card, file);
__error1:
return err;
}

再从snd_pcm_open_file继续向下看:

 static int snd_pcm_open_file(struct file *file,
struct snd_pcm *pcm,
int stream)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
int err; err = snd_pcm_open_substream(pcm, stream, file, &substream);    //打开substream结构体
if (err < )
return err; pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
if (pcm_file == NULL) {
snd_pcm_release_substream(substream);
return -ENOMEM;
}
pcm_file->substream = substream;
if (substream->ref_count == ) {
substream->file = pcm_file;
substream->pcm_release = pcm_release_private;
}
file->private_data = pcm_file; return ;
}
 int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
{
struct snd_pcm_substream *substream;
int err; err = snd_pcm_attach_substream(pcm, stream, file, &substream);
if (err < )
return err;
if (substream->ref_count > ) {
*rsubstream = substream;
return ;
} err = snd_pcm_hw_constraints_init(substream); //初始化substream结构体
if (err < ) {
snd_printd("snd_pcm_hw_constraints_init failed\n");
goto error;
} if ((err = substream->ops->open(substream)) < )
goto error; substream->hw_opened = ; err = snd_pcm_hw_constraints_complete(substream);
if (err < ) {
snd_printd("snd_pcm_hw_constraints_complete failed\n");
goto error;
} *rsubstream = substream;
return ; error:
snd_pcm_release_substream(substream);
return err;
}

snd_pcm_open_substream

在snd_pcm_open_substream函数中:

     if ((err = substream->ops->open(substream)) < )    // substream->ops : snd_pcm_ops结构体
goto error;

依次调用cpu_dai, dma, codec_dai, machine(三大模块)的open或startup函数;

msm_mi2s_snd_startup函数:

 struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_codec *codec = rtd->codec;

设置snd_soc_pcm_runtime的cpu、codec等模块;然后在snd_soc_pcm_runtime函数中对codec函数进行相应的设置,之后通过音频数据流通道播放出声音;

调用过程如下图:

2.2 tinyplay的ioctl过程:

同样也是snd_pcm_f_ops[0]结构体的file_operations:

 {
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},

从snd_pcm_playback_ioctl函数向下看:

 static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct snd_pcm_file *pcm_file; pcm_file = file->private_data;    //获取相应的私有数据 if ((((cmd >> ) & 0xff) != 'A') && (((cmd >> ) & 0xff) != 'C'))
return -ENOTTY; return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
(void __user *)arg);
}

snd_pcm_playback_ioctl1:

 static int snd_pcm_playback_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
if (snd_BUG_ON(!substream))
return -ENXIO;
if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
return -EINVAL;
//根据case不同,对播放进行相应的不同操作
switch (cmd) {
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
{
struct snd_xferi xferi;
struct snd_xferi __user *_xferi = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (put_user(, &_xferi->result))
return -EFAULT;
if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
return -EFAULT;
result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
__put_user(result, &_xferi->result);
return result < ? result : ;
}
case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
{
struct snd_xfern xfern;
struct snd_xfern __user *_xfern = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
void __user **bufs;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (runtime->channels > )
return -EINVAL;
if (put_user(, &_xfern->result))
return -EFAULT;
if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
return -EFAULT; bufs = memdup_user(xfern.bufs,
sizeof(void *) * runtime->channels);
if (IS_ERR(bufs))
return PTR_ERR(bufs);
result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
kfree(bufs);
__put_user(result, &_xfern->result);
return result < ? result : ;
}
case SNDRV_PCM_IOCTL_REWIND:
{
snd_pcm_uframes_t frames;
snd_pcm_uframes_t __user *_frames = arg;
snd_pcm_sframes_t result;
if (get_user(frames, _frames))
return -EFAULT;
if (put_user(, _frames))
return -EFAULT;
result = snd_pcm_playback_rewind(substream, frames);
__put_user(result, _frames);
return result < ? result : ;
}
case SNDRV_PCM_IOCTL_FORWARD:
{
snd_pcm_uframes_t frames;
snd_pcm_uframes_t __user *_frames = arg;
snd_pcm_sframes_t result;
if (get_user(frames, _frames))
return -EFAULT;
if (put_user(, _frames))
return -EFAULT;
result = snd_pcm_playback_forward(substream, frames);
__put_user(result, _frames);
return result < ? result : ;
}
}
return snd_pcm_common_ioctl1(file, substream, cmd, arg);
}

从snd_pcm_common_ioctl1继续分析,进入函数的prepare中:

当函数prepare完毕后,就一切准备就绪了,只等一个trigger;而trigger的执行会在上层的alsalib调用write的函数触发;prepare过程可以看下图,具体就不继续分析了:

下一节我们将来分析tinymixer的调用过程;

alsa声卡分析alsa-utils调用过程(二)-tinymixer

alsa声卡分析alsa-utils调用过程的更多相关文章

  1. alsa声卡分析alsa-utils调用过程(一)-tinyplay

    如何分析tinyplay 播放音频和tinymix的过程?需要相应的工具来支持追查: 一.分析tinyplay和tinymix: 1.1 利用strace工具: strace -o tinyplay. ...

  2. Spring源码分析之`BeanFactoryPostProcessor`调用过程

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...

  3. alsa声卡分析alsa-utils调用过程(二)-tinymixer

    继上一篇文章:http://www.cnblogs.com/linhaostudy/p/8515277.html 三.tinymixer调用分析:(tinymixer.log搜索节点:/dev/snd ...

  4. mybatis源码分析(方法调用过程)

    十一月月底,宿舍楼失火啦,搞得20多天没有网,目测直到放假也不会来了... 正题 嗯~,其实阅读源码不是为了应付面试,更重要的让你知道,大师是怎样去写代码的,同样是用Java,为啥Clinton Be ...

  5. Openstack Nova 源码分析 — RPC 远程调用过程

    目录 目录 Nova Project Services Project 的程序入口 setuppy Nova中RPC远程过程调用 nova-compute RPC API的实现 novacompute ...

  6. ALSA声卡07_分析调用过程_学习笔记

    1.编译新的strace工具分析aplay和amixer应用程序对声卡的调用过程 (1)因为旧的strace工具不能识别不能识别alsa声卡驱动程序里面的ioctrl. (2)编译过程参考http:/ ...

  7. ALSA声卡笔记1---ALSA驱动框架

    1.声卡驱动程序sound.c (1)入口函数里通过register_chrdev()函数注册file_operations 结构体 (2)file_operations 结构体,里面只有open函数 ...

  8. ALSA声卡驱动中的DAPM详解之七:dapm事件机制(dapm event)

    前面的六篇文章,我们已经讨论了dapm关于动态电源管理的有关知识,包括widget的创建和初始化,widget之间的连接以及widget的上下电顺序等等.本章我们准备讨论dapm框架中的另一个机制:事 ...

  9. ALSA声卡驱动中的DAPM详解之五:建立widget之间的连接关系

    前面我们主要着重于codec.platform.machine驱动程序中如何使用和建立dapm所需要的widget,route,这些是音频驱动开发人员必须要了解的内容,经过前几章的介绍,我们应该知道如 ...

随机推荐

  1. plus、max、Pro、Edge

    plus.max.Pro.Edge等后缀到底什么意思? Plus:比好更好.比牛X更牛X 译成中文是:加.和.正的-的意思.比如oneplus中文名就是一加.用于手机命名表示配置更加牛X,最早是由iP ...

  2. [Android] Toast问题深度剖析(一)

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 题记 Toast 作为 Android 系统中最常用的类之一,由于其方便的api设计和简洁的交互体验,被我们所广泛采用.但是,伴随着我们开发的深 ...

  3. Jmeter之性能测试类型

    pipe-clean test 在测试环境预先跑 确定脚本准确性 单用户跑 获取baseline   容量测试 经典的性能测试类型 获取系统稳定运行时最大的吞吐量/并发数/响应时间时 尽量模拟真实情况 ...

  4. 吴恩达深度学习笔记(deeplearning.ai)之卷积神经网络(一)

    Padding 在卷积操作中,过滤器(又称核)的大小通常为奇数,如3x3,5x5.这样的好处有两点: 在特征图(二维卷积)中就会存在一个中心像素点.有一个中心像素点会十分方便,便于指出过滤器的位置. ...

  5. Spring对远程服务的支持

    Java程序有以下的远程调用技术选择: 远程过程调用(RPC)是同步的,客户端在服务器端返回结果之前将一直被阻塞. 各种技术适用的场景如下: 典型的RMI开发的过程如下: 定义一个接口,用于客户端和服 ...

  6. bzoj 4826: [Hnoi2017]影魔 [主席树 单调栈]

    4826: [Hnoi2017]影魔 题意:一个排列,点对\((i,j)\),\(p=max(i+1,j-1)\),若\(p<a_i,a_j\)贡献p1,若\(p\)在\(a_1,a_2\)之间 ...

  7. bzoj 3509: [CodeChef] COUNTARI] [分块 生成函数]

    3509: [CodeChef] COUNTARI 题意:统计满足\(i<j<k, 2*a[j] = a[i] + a[k]\)的个数 \(2*a[j]\)不太好处理,暴力fft不如直接暴 ...

  8. Selenium_WebDriver_控制浏览器

    版权声明:本文为博主原创文章,转载请注明出处.  浏览器环境: ①GoogleChrome:60 ②chromedriver:2.30: 访问 操作 方法 示例 访问页面 void get(java. ...

  9. jquery对象和DOM对象的区别和转换

    jquery对象和DOM对象的区别和转换 在使用jquery时,我们直接通过jq的选择器获取元素,然后对元素进行操作,用jq选择器获取到的对象是一个jq对象,jq对象能够使用jq提供的方法,但是不能用 ...

  10. 使用VIM将文件的其中的连续几行注释删除或者给其中的连续几行添加注释

    一.使用VIM将文件的其中的连续几行注释删除 1.用VIM打开一个文件,比如打开sshd_config文件,以该文件的下面几行为例: #vim  sshd_config 2.此时,按ctrl+v键,使 ...