zoukankan      html  css  js  c++  java
  • alsa声卡分析alsa-utils调用过程(一)-tinyplay

    如何分析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 }
     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
    ");
    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
    ");
    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

     在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的调用过程;

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

  • 相关阅读:
    Hibernate 5.x 生成 SessionFactory 源码跟踪分析
    编译iftop
    Too many open files
    ffmpeg指定网卡
    abrt-hook-ccpp使用CPU太多
    ffplay播放时显示信息的意义
    Windows换行符和Linux换行符的替换
    directshow在WIN10下的一个BUG
    使用mirrordriver截屏
    mac xmind 激活
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/8515277.html
Copyright © 2011-2022 走看看