[RK3288][Android6.0] Audio中的HW Params设置流程
Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92
这部分内容比较繁琐,,先从初始化开始了解里面参数意义,以period size
这个参数为线索来追终代码流程.
pcm_native.c
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_rulenew = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
......}//填充rules,下面的值会以snd_pcm_hw_constraints_init()调用中的实际值为例.c = &constrs->rules[constrs->rules_num];//为0c->cond = cond;//后面会回调snd_pcm_hw_rule_mulkdiv()来计算period size.c->func = func;//SNDRV_PCM_HW_PARAM_PERIOD_SIZEc->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)) {//取出对应的intervalstruct snd_interval *i = param_to_interval(p, n);//赋值最大最小都是config->period_sizei->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++) {//取出对应的maskm = hw_param_mask(params, k);//每个mask使用了两个字节总共64位//如果一个都没设置,那就不处理参数了,当前正常流程不会发生.if (snd_mask_empty(m))return -EINVAL;//rmask用于存每个interval对应的位,如果没设置就不能往下走.if (!(params->rmask & (1 << k)))continue;//调试信息.
#ifdef RULES_DEBUGprintk(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_DEBUGprintk("%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;}//依次处理每个intervalfor (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {//取出i = hw_param_interval(params, k);
......//替换旧的intervalchanged = 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_mulkdivchanged = 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;} elsec->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
[RK3288][Android6.0] Audio中的HW Params设置流程相关推荐
- 声道切换 android,[RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 播放音乐是单声道,硬件用的是双声道. AudioFlinger::PlaybackThread:: ...
- [RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 播放音乐是单声道,硬件用的是双声道. AudioFlinger::PlaybackThread:: ...
- [RK3288][Android6.0] Audio录音frame rate设置流程小结
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 HAL: 默认的配置在 hardware/rockchip/audio/tinyalsa_hal/ ...
- [RK3288][Android6.0] Skia中的编解码小结
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 编解码器注册: SKIA的编解码部分通过一个模板类来实现,这样做的为了实现不同类型的codec兼容 ...
- [RK3288][Android6.0] Audio的音量设置流程小结
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 说明一: AudioManager提供了两个调节音量接口 adjustSuggestedStrea ...
- android6.0 wifi流程,[RK3288][Android6.0] WiFi之从Linkspeed看获取流程
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 WiFi的(Link speed)连接速度可以从Settings里查看 从连接速度来看下获取WiF ...
- [RK3288][Android6.0] 设置中通过Sensor旋转显示画面小结
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 Settings -> Display有个选项控制旋转屏幕时内容是否跟着旋转 这个功能是通过 ...
- Android系统(127)---Android6.0存储中加入总内存和系统内存项和在西语下把,换成.
Android6.0存储中加入总内存和系统内存项 阅读数:651 平台下patches/packages/apps/Settings/里面 1.存储中加入总内存和系统内存项 在 res/values- ...
- [RK3288][Android6.0] 调试笔记 --- WiFi芯片AP6356S添加
Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 背景: RK3288 Android6.0平台对AP6356S的支持不是很完善,直接修改wifi ch ...
- [RK3288][Android6.0] 调试笔记 --- touch无法获取坐标点
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 有网友遇到调试touch的时候能触发中断,但无法获取坐标点 具体可参考文章 [RK3288][An ...
最新文章
- 如何 SQL Server 2005 实例之间传输登录和密码
- 通过Webservice查询手机号码归属地
- 2019北京高考分数分布一览表(成绩分布统计)
- 【快乐水题】747. 至少是其他数字两倍的最大数
- C++字符串类型和数字之间的转换
- [html] 在主框架下引入的iframe,如果检测这个iframe是否能打开,如果打不开则跳到404页面
- Xamarin:安卓通过“第三发应用打开”实现文件跨应用传输
- Java 并发:第三部分 - 同步锁
- android 横盘方向传感器,横盘震荡选择方向!
- 什么是软件著作权,怎么申请软件著作权
- ISO27001认证适用领域及认证流程
- VGG-16网络结构详解
- CTA策略01_dualThrust
- 计算机转换外界信息原理,高级文秘及办公自动化教程-计算机基础
- [****ViewController scrollViewDidScroll:]: message sent to deallocated instance 0x12d6c22f0
- php获取小米手环数据,小米运动App数据提取
- kso经验积累 -- c#发送邮件
- android socket通讯
- 互联网与达尔文进化论的结合
- 解决Android 模拟机开机黑屏问题、npm内存溢出问题
热门文章
- 【信息系统项目管理师】干系人管理
- 标题 多媒体计算机的标准是什么,多媒体技术习题答案
- java将Word转换成PDF
- Linux led子系统分析之三 led设备驱动与ledtrigger驱动实现
- mini2440 LED设备驱动开发源代码(宋宝华框架)
- zynq之ps端开发
- PR导入视频失败、没有音频解决方法
- 推荐玩游戏的计算机及型号,想买个3000左右的台式电脑,主要玩游戏,求推荐品牌还有型号。...
- 两年数据对比柱形图_对比数据还在用千篇一律的柱形图?试试wifi对比图,让人过目不忘...
- centOS7搭建DNS服务器配置详解