如何分析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:
1 const struct file_operations snd_pcm_f_ops[2] = { 2 { 3 .owner = THIS_MODULE, 4 .write = snd_pcm_write, 5 .aio_write = snd_pcm_aio_write, 6 .open = snd_pcm_playback_open, 7 .release = snd_pcm_release, 8 .llseek = no_llseek, 9 .poll = snd_pcm_playback_poll, 10 .unlocked_ioctl = snd_pcm_playback_ioctl, 11 .compat_ioctl = snd_pcm_ioctl_compat, 12 .mmap = snd_pcm_mmap, 13 .fasync = snd_pcm_fasync, 14 .get_unmapped_area = snd_pcm_get_unmapped_area, 15 }, 16 { 17 .owner = THIS_MODULE, 18 .read = snd_pcm_read, 19 .aio_read = snd_pcm_aio_read, 20 .open = snd_pcm_capture_open, 21 .release = snd_pcm_release, 22 .llseek = no_llseek, 23 .poll = snd_pcm_capture_poll, 24 .unlocked_ioctl = snd_pcm_capture_ioctl, 25 .compat_ioctl = snd_pcm_ioctl_compat, 26 .mmap = snd_pcm_mmap, 27 .fasync = snd_pcm_fasync, 28 .get_unmapped_area = snd_pcm_get_unmapped_area, 29 } 30 };
我们从snd_pcm_playback_open函数开始向下分析:
1 static int snd_pcm_playback_open(struct inode *inode, struct file *file)
2 {
3 struct snd_pcm *pcm;
4 int err = nonseekable_open(inode, file);
5 if (err < 0)
6 return err;
7 pcm = snd_lookup_minor_data(iminor(inode),
8 SNDRV_DEVICE_TYPE_PCM_PLAYBACK); //取得其私有数据并返回的
9 err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
10 if (pcm)
11 snd_card_unref(pcm->card); //减少设备对象的引用计数 snd_card_unref(card);
12 return err;
13 }
在下面调用了snd_pcm_open函数:
1 static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) 2 { 3 int err; 4 wait_queue_t wait; 5 6 if (pcm == NULL) { 7 err = -ENODEV; 8 goto __error1; 9 } 10 err = snd_card_file_add(pcm->card, file); 11 if (err < 0) 12 goto __error1; 13 if (!try_module_get(pcm->card->module)) { 14 err = -EFAULT; 15 goto __error2; 16 } 17 init_waitqueue_entry(&wait, current); 18 add_wait_queue(&pcm->open_wait, &wait); 19 mutex_lock(&pcm->open_mutex); 20 while (1) { 21 err = snd_pcm_open_file(file, pcm, stream); // 将操作该声卡card的应用程序添加到card->files_list 22 if (err >= 0) 23 break; 24 if (err == -EAGAIN) { 25 if (file->f_flags & O_NONBLOCK) { 26 err = -EBUSY; 27 break; 28 } 29 } else 30 break; 31 set_current_state(TASK_INTERRUPTIBLE); 32 mutex_unlock(&pcm->open_mutex); 33 schedule(); 34 mutex_lock(&pcm->open_mutex); 35 if (pcm->card->shutdown) { 36 err = -ENODEV; 37 break; 38 } 39 if (signal_pending(current)) { 40 err = -ERESTARTSYS; 41 break; 42 } 43 } 44 remove_wait_queue(&pcm->open_wait, &wait); 45 mutex_unlock(&pcm->open_mutex); 46 if (err < 0) 47 goto __error; 48 return err; 49 50 __error: 51 module_put(pcm->card->module); 52 __error2: 53 snd_card_file_remove(pcm->card, file); 54 __error1: 55 return err; 56 }
再从snd_pcm_open_file继续向下看:
1 static int snd_pcm_open_file(struct file *file, 2 struct snd_pcm *pcm, 3 int stream) 4 { 5 struct snd_pcm_file *pcm_file; 6 struct snd_pcm_substream *substream; 7 int err; 8 9 err = snd_pcm_open_substream(pcm, stream, file, &substream); //打开substream结构体 10 if (err < 0) 11 return err; 12 13 pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); 14 if (pcm_file == NULL) { 15 snd_pcm_release_substream(substream); 16 return -ENOMEM; 17 } 18 pcm_file->substream = substream; 19 if (substream->ref_count == 1) { 20 substream->file = pcm_file; 21 substream->pcm_release = pcm_release_private; 22 } 23 file->private_data = pcm_file; 24 25 return 0; 26 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, 2 struct file *file, 3 struct snd_pcm_substream **rsubstream) 4 { 5 struct snd_pcm_substream *substream; 6 int err; 7 8 err = snd_pcm_attach_substream(pcm, stream, file, &substream); 9 if (err < 0) 10 return err; 11 if (substream->ref_count > 1) { 12 *rsubstream = substream; 13 return 0; 14 } 15 16 err = snd_pcm_hw_constraints_init(substream); //初始化substream结构体 17 if (err < 0) { 18 snd_printd("snd_pcm_hw_constraints_init failed\n"); 19 goto error; 20 } 21 22 if ((err = substream->ops->open(substream)) < 0) 23 goto error; 24 25 substream->hw_opened = 1; 26 27 err = snd_pcm_hw_constraints_complete(substream); 28 if (err < 0) { 29 snd_printd("snd_pcm_hw_constraints_complete failed\n"); 30 goto error; 31 } 32 33 *rsubstream = substream; 34 return 0; 35 36 error: 37 snd_pcm_release_substream(substream); 38 return err; 39 }
在snd_pcm_open_substream函数中:
1 if ((err = substream->ops->open(substream)) < 0) // substream->ops : snd_pcm_ops结构体 2 goto error;
依次调用cpu_dai, dma, codec_dai, machine(三大模块)的open或startup函数;
msm_mi2s_snd_startup函数:
1 struct snd_soc_pcm_runtime *rtd = substream->private_data; 2 struct snd_soc_card *card = rtd->card; 3 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 4 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:
1 { 2 .owner = THIS_MODULE, 3 .write = snd_pcm_write, 4 .aio_write = snd_pcm_aio_write, 5 .open = snd_pcm_playback_open, 6 .release = snd_pcm_release, 7 .llseek = no_llseek, 8 .poll = snd_pcm_playback_poll, 9 .unlocked_ioctl = snd_pcm_playback_ioctl, 10 .compat_ioctl = snd_pcm_ioctl_compat, 11 .mmap = snd_pcm_mmap, 12 .fasync = snd_pcm_fasync, 13 .get_unmapped_area = snd_pcm_get_unmapped_area, 14 },
从snd_pcm_playback_ioctl函数向下看:
1 static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd, 2 unsigned long arg) 3 { 4 struct snd_pcm_file *pcm_file; 5 6 pcm_file = file->private_data; //获取相应的私有数据 7 8 if ((((cmd >> 8) & 0xff) != 'A') && (((cmd >> 8) & 0xff) != 'C')) 9 return -ENOTTY; 10 11 return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd, 12 (void __user *)arg); 13 }
snd_pcm_playback_ioctl1:
1 static int snd_pcm_playback_ioctl1(struct file *file, 2 struct snd_pcm_substream *substream, 3 unsigned int cmd, void __user *arg) 4 { 5 if (snd_BUG_ON(!substream)) 6 return -ENXIO; 7 if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) 8 return -EINVAL; 9 //根据case不同,对播放进行相应的不同操作 10 switch (cmd) { 11 case SNDRV_PCM_IOCTL_WRITEI_FRAMES: 12 { 13 struct snd_xferi xferi; 14 struct snd_xferi __user *_xferi = arg; 15 struct snd_pcm_runtime *runtime = substream->runtime; 16 snd_pcm_sframes_t result; 17 if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 18 return -EBADFD; 19 if (put_user(0, &_xferi->result)) 20 return -EFAULT; 21 if (copy_from_user(&xferi, _xferi, sizeof(xferi))) 22 return -EFAULT; 23 result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); 24 __put_user(result, &_xferi->result); 25 return result < 0 ? result : 0; 26 } 27 case SNDRV_PCM_IOCTL_WRITEN_FRAMES: 28 { 29 struct snd_xfern xfern; 30 struct snd_xfern __user *_xfern = arg; 31 struct snd_pcm_runtime *runtime = substream->runtime; 32 void __user **bufs; 33 snd_pcm_sframes_t result; 34 if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 35 return -EBADFD; 36 if (runtime->channels > 128) 37 return -EINVAL; 38 if (put_user(0, &_xfern->result)) 39 return -EFAULT; 40 if (copy_from_user(&xfern, _xfern, sizeof(xfern))) 41 return -EFAULT; 42 43 bufs = memdup_user(xfern.bufs, 44 sizeof(void *) * runtime->channels); 45 if (IS_ERR(bufs)) 46 return PTR_ERR(bufs); 47 result = snd_pcm_lib_writev(substream, bufs, xfern.frames); 48 kfree(bufs); 49 __put_user(result, &_xfern->result); 50 return result < 0 ? result : 0; 51 } 52 case SNDRV_PCM_IOCTL_REWIND: 53 { 54 snd_pcm_uframes_t frames; 55 snd_pcm_uframes_t __user *_frames = arg; 56 snd_pcm_sframes_t result; 57 if (get_user(frames, _frames)) 58 return -EFAULT; 59 if (put_user(0, _frames)) 60 return -EFAULT; 61 result = snd_pcm_playback_rewind(substream, frames); 62 __put_user(result, _frames); 63 return result < 0 ? result : 0; 64 } 65 case SNDRV_PCM_IOCTL_FORWARD: 66 { 67 snd_pcm_uframes_t frames; 68 snd_pcm_uframes_t __user *_frames = arg; 69 snd_pcm_sframes_t result; 70 if (get_user(frames, _frames)) 71 return -EFAULT; 72 if (put_user(0, _frames)) 73 return -EFAULT; 74 result = snd_pcm_playback_forward(substream, frames); 75 __put_user(result, _frames); 76 return result < 0 ? result : 0; 77 } 78 } 79 return snd_pcm_common_ioctl1(file, substream, cmd, arg); 80 }
从snd_pcm_common_ioctl1继续分析,进入函数的prepare中:
当函数prepare完毕后,就一切准备就绪了,只等一个trigger;而trigger的执行会在上层的alsalib调用write的函数触发;prepare过程可以看下图,具体就不继续分析了:
下一节我们将来分析tinymixer的调用过程;