[RK3288][Android6.0] Audio中的HW Params设置流程
来源:互联网 发布:js移除onclick 编辑:程序博客网 时间:2024/06/06 16:39
Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92
snd_pcm_hw_constraints_init():
用户空间对period size的配置:
ioctl ->
snd_pcm_common_ioctl1 ->
snd_pcm_hw_params_user ->
snd_pcm_hw_params ->
snd_pcm_hw_refine //参数处理都在这里完成
snd_pcm_hw_refine():
参考:
http://blog.csdn.net/u012769691/article/details/46727543
http://blog.chinaunix.net/uid-20564848-id-74213.html
http://blog.csdn.net/crycheng/article/details/7095899
OS: Android 6.0
Kernel: 3.10.92
这部分内容比较繁琐,,先从初始化开始了解里面参数意义,以period size
这个参数为线索来追终代码流程.
snd_pcm_hw_constraints_init():
int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; //ASOC对应hw params有参数大小限制,一定要在它的范围内,否则设置无效. //相关信息都保存在hw_constraints中. struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; int k, err;...... //每个hw参数规则都会被添加进去来作追踪. err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, snd_pcm_hw_rule_mulkdiv, (void*) 8, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);......}
struct snd_pcm_hw_constraints { //mask用来表示后面的intervals是否能访问,没有设置是不行的. struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; //保存所有hw param值. struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; //目前已有规则总数 unsigned int rules_num; //存规则势必要分配空间,当rules_num达到rules_all的时候, //会每次分配16个存储空间,而不是一次分配一个,这样可以提高效率, //因此rules_all总是>=rules_num. unsigned int rules_all; //所有规则用此结构表示. struct snd_pcm_hw_rule *rules;};snd_pcm_hw_rule_add():
int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, int var, snd_pcm_hw_rule_func_t func, void *private, int dep, ...){ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; struct snd_pcm_hw_rule *c;...... //每添加一个interval,constrs->rules_num就会随着增加, //当超过constrs->rules_all,就需要分配新空间用于存储interval. if (constrs->rules_num >= constrs->rules_all) { struct snd_pcm_hw_rule *new; //基于效率,每次直接分配16个. unsigned int new_rules = constrs->rules_all + 16; //分配的结构是struct snd_pcm_hw_rule new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);...... } //填充rules,下面的值会以snd_pcm_hw_constraints_init()调用中的实际值为例. c = &constrs->rules[constrs->rules_num]; //为0 c->cond = cond; //后面会回调snd_pcm_hw_rule_mulkdiv()来计算period size. c->func = func; //SNDRV_PCM_HW_PARAM_PERIOD_SIZE c->var = var; //为8,后面计算period size会当系数用. c->private = private; k = 0; while (1) {...... //SNDRV_PCM_HW_PARAM_PERIOD_BYTES和SNDRV_PCM_HW_PARAM_FRAME_BITS //存到deps数组中,后面通过这两个来重计算period size. c->deps[k++] = dep;..... constrs->rules_num++;......}有些参数如period count, channel等,一开始它的值会根据硬件限制定好大范围了,如rk_pcm.c
static const struct snd_pcm_hardware rockchip_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE, //最少双通道,最大八通道. .channels_min = 2, .channels_max = 8, .buffer_bytes_max = 2*1024*1024,/*128*1024,*/ .period_bytes_min = 64, .period_bytes_max = 512*1024,/*32*1024,//2048*4,///PAGE_SIZE*2,*/ .periods_min = 3, .periods_max = 128, .fifo_size = 16,};然后会被保存到hw interval中:
int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream){...... err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, hw->rate_min, hw->rate_max);......}
int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, unsigned int min, unsigned int max){ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; struct snd_interval t; t.min = min; t.max = max; t.openmin = t.openmax = 0; t.integer = 0; //新的配置更新到旧的interval中. return snd_interval_refine(constrs_interval(constrs, var), &t);}这样默认就有个范围值了,这样,当用户空间设置时,就不能超过这个硬件设置范围,否则就会出错了!
用户空间对period size的配置:
struct pcm_config pcm_config_in = { .channels = 2, .rate = 44100, .period_size = 16, .period_count = 32, .format = PCM_FORMAT_S16_LE,};然后会被传到pcm_open()中
struct pcm *pcm_open(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config){...... //config->period_size被传到了这里. //函数用来设置period size的最小值 //然后会通过下面的ioctl传给kernel看是否超出范围了. param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);...... if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { oops(pcm, errno, "cannot set hw params"); goto fail_close; } //返回设置可以设置的值. config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);......}param_set_min():
static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val){ //判断当前SNDRV_PCM_HW_PARAM_PERIOD_SIZE是否在interval列表范围内,肯定成立. if (param_is_interval(n)) { //取出对应的interval struct snd_interval *i = param_to_interval(p, n); //赋值最大最小都是config->period_size i->min = val; i->max = val; i->integer = 1; }}看ioctl进入kernel
ioctl ->
snd_pcm_common_ioctl1 ->
snd_pcm_hw_params_user ->
snd_pcm_hw_params ->
snd_pcm_hw_refine //参数处理都在这里完成
snd_pcm_hw_refine():
int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params){ unsigned int k; struct snd_pcm_hardware *hw; struct snd_interval *i = NULL; struct snd_mask *m = NULL; struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; unsigned int rstamps[constrs->rules_num]; unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];...... //先对mask做处理,这里不太明白mask和后面提到的interval有什么联系? for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { //取出对应的mask m = hw_param_mask(params, k); //每个mask使用了两个字节总共64位 //如果一个都没设置,那就不处理参数了,当前正常流程不会发生. if (snd_mask_empty(m)) return -EINVAL; //rmask用于存每个interval对应的位,如果没设置就不能往下走. if (!(params->rmask & (1 << k))) continue; //调试信息.#ifdef RULES_DEBUG printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]); printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);#endif //更新旧的mask信息 changed = snd_mask_refine(m, constrs_mask(constrs, k));#ifdef RULES_DEBUG printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);#endif //如果前后有变化,则标记. if (changed) params->cmask |= 1 << k; if (changed < 0) return changed; } //依次处理每个interval for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { //取出 i = hw_param_interval(params, k);...... //替换旧的interval changed = snd_interval_refine(i, constrs_interval(constrs, k));...... }...... do { again = 0; for (k = 0; k < constrs->rules_num; k++) { struct snd_pcm_hw_rule *r = &constrs->rules[k];...... //执行之前注册的回调函数,这里是snd_pcm_hw_rule_mulkdiv changed = r->func(params, r);...... } } while (again);......}snd_pcm_hw_rule_mulkdiv():
static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ struct snd_interval t; //参数一对应SNDRV_PCM_HW_PARAM_PERIOD_BYTES的配置 //参数二的是值是8 //参数三对应SNDRV_PCM_HW_PARAM_FRAME_BITS的配置 snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), (unsigned long) rule->private, hw_param_interval_c(params, rule->deps[1]), &t); //更新配置 return snd_interval_refine(hw_param_interval(params, rule->var), &t);}SNDRV_PCM_HW_PARAM_FRAME_BITS()的设置在snd_pcm_hw_constraints_init()中snd_pcm_hw_constraints_init() {...... snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));......}而具体的值是在HAL层设置的:
struct pcm *pcm_open(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config){...... //对应pcm_config_in的配置,为16*2 = 32. param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, pcm_format_to_bits(config->format) * config->channels);......}而SNDRV_PCM_HW_PARAM_PERIOD_BYTES的值是在
int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream){...... //也就是rockchip_pcm_hardware变量中的值. err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, hw->period_bytes_min, hw->period_bytes_max);......}snd_interval_mulkdiv():
void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, const struct snd_interval *b, struct snd_interval *c){...... //c->min = a->min * k / b->max; c->min = muldiv32(a->min, k, b->max, &r); c->openmin = (r || a->openmin || b->openmax); if (b->min > 0) { c->max = muldiv32(a->max, k, b->min, &r); if (r) { c->max++; c->openmax = 1; } else c->openmax = (a->openmax || b->openmin); } else { c->max = UINT_MAX; c->openmax = 0; } c->integer = 0;}
参考:
http://blog.csdn.net/u012769691/article/details/46727543
http://blog.chinaunix.net/uid-20564848-id-74213.html
http://blog.csdn.net/crycheng/article/details/7095899
1 0
- [RK3288][Android6.0] Audio中的HW Params设置流程
- [RK3288][Android6.0] Audio录音frame rate设置流程小结
- [RK3288][Android6.0] Audio的音量设置流程小结
- [RK3288][Android6.0] Audio的DMA调用实例流程
- [RK3288][Android6.0] Audio中的period_size使用规则
- [RK3288][Android6.0] Audio中的录音重采样小结
- [RK3288][Android6.0] Audio中的放音重采样小结
- [RK3288][Android6.0] Audio中的混音过程小结
- [RK3288][Android6.0] Audio录音HAL层的初始化流程分析
- [RK3288][Android6.0] Audio录音HAL层的数据读取流程分析
- [RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程
- [RK3288][Android6.0] Camera驱动初始化流程
- [RK3288][Android6.0] Camera HAL流程小结
- [RK3288][Android6.0] StageFright解码流程小结
- [RK3288][Android6.0] 串口驱动流程小结
- [RK3288][Android6.0] ION 驱动流程小结
- [RK3288][Android6.0] Camera HAL流程小结
- [RK3288][Android6.0] Audio的ASOC驱动及数据结构
- Unity3D基础篇----Shader学习笔记(1)
- 继承的一些细节,笔记
- POJ 1188 Gleaming the Cubes 笔记
- Android读取工程根目录下文件内容
- c++ 数据结构排序技术
- [RK3288][Android6.0] Audio中的HW Params设置流程
- Ubuntu16.04桥接下如何配置固定IP
- MySQL给一个字段递增赋值
- jquery ajax PHP 取得选中多选项值并传递到PHP进行删除
- Redis基础操作
- windows下安装pyv8,并在pycharm中引用
- c++中vector的用法详解
- pgsql:删除表中重复数据保留其中的一条
- JavaScript获取非行间样式的方法