Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92

用原生SoundRecorder apk为例,以sample rate, channel及buffer size这些参数为线索来跟踪,,流程如下:
openRecord_l ->
    AudioSystem::getInputForAttr ->
        aps->getInputForAttr ->
            AudioPolicyManager::getInputForAttr ->
                getInputProfile ->    根据上apk给的profileSamplingRate, profileFormat以及profileChannelMask找到对应的profile
                mpClientInterface->openInput -> 上层的录音参数传递给HAL.
                    AudioFlinger::openInput_l ->
                        inHwHal->open_input_stream ->
                        adev_open_input_stream ->    audio_hw.c

static int adev_open_input_stream(struct audio_hw_device *dev,audio_io_handle_t handle,audio_devices_t devices,struct audio_config *config,    struct audio_stream_in **stream_in,    //传进来是新的audio_input_flags_t flags,const char *address __unused,audio_source_t source __unused)
{struct audio_device *adev = (struct audio_device *)dev;struct stream_in *in;int ret;*stream_in = NULL;//不管它传进来是哪种,直接定死为双声道.config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
#ifdef ALSA_IN_DEBUG//用来调试抓取pcm数据in_debug = fopen("/data/debug.pcm","wb");//please touch /data/debug.pcm first
#endif......in = (struct stream_in *)calloc(1, sizeof(struct stream_in));if (!in)return -ENOMEM;
......//SoundRecorder apk使用的是8kHz的采样率.in->requested_rate = config->sample_rate;
......//上面定死为双通道.in->channel_mask = config->channel_mask;
......//默认用的是pcm_config_in//struct pcm_config pcm_config_in = {//.channels = 2,//.rate = 44100,//.period_size = 16,//.period_count = 128,//.format = PCM_FORMAT_S16_LE,//};struct pcm_config *pcm_config = flags & AUDIO_INPUT_FLAG_FAST ?&pcm_config_in_low_latency : &pcm_config_in;in->config = pcm_config;//用pcm_config_in的配置来计算, audio_stream_in_frame_size(&in->stream)调用的是//in_get_format(),固定是AUDIO_FORMAT_PCM_16_BIT, audio_channel_count_from_out_mask()用来转换//channel宏定义为实际的number.不过这里有疑问的是这样不是多了一个pcm_config->channels了?//audio_stream_in_frame_size()已经包含了channel了啊!in->buffer = malloc(pcm_config->period_size * pcm_config->channels* audio_stream_in_frame_size(&in->stream));
......//当上层采样率和HAL配置的采样率不想等时,要resample,当然,数据最终也是从resample后的buffer中获取.if (in->requested_rate != pcm_config->rate) {//和正常非resample的获取kernel录音数据方式一样.in->buf_provider.get_next_buffer = get_next_buffer;    in->buf_provider.release_buffer = release_buffer;ALOGD("pcm_config->rate:%d,in->requested_rate:%d,in->channel_mask:%d",pcm_config->rate,in->requested_rate,audio_channel_count_from_in_mask(in->channel_mask));ret = create_resampler(pcm_config->rate,    //44.1kHzin->requested_rate,    //8kHzaudio_channel_count_from_in_mask(in->channel_mask),    //2RESAMPLER_QUALITY_DEFAULT,&in->buf_provider,&in->resampler);
......}
......
}

数据读取:
in_read():    audio_hw.c

static ssize_t in_read(struct audio_stream_in *stream, void* buffer,size_t bytes)
{
......//将要读取的字节数转换成frames, 一个frame=channel * format.size_t frames_rq = bytes / audio_stream_in_frame_size(stream);
......pthread_mutex_lock(&in->lock);if (in->standby) {pthread_mutex_lock(&adev->lock);//打开及设置录音ret = start_input_stream(in);pthread_mutex_unlock(&adev->lock);if (ret < 0)goto exit;in->standby = false;}//读取数据,读取的buffer和frame count是上层提供.ret = read_frames(in, buffer, frames_rq);
......
}

start_input_stream():

static int start_input_stream(struct stream_in *in)
{struct audio_device *adev = in->dev;//打印信息in_dump(in, 0);//路径相关,这里不关注route_pcm_open(getRouteFromDevice(in->device | AUDIO_DEVICE_BIT_IN));//打开录音设备,设置也在里面,调用tinyalsa库里面的代码.in->pcm = pcm_open(PCM_CARD, PCM_DEVICE, PCM_IN, in->config);
......//传进来的是8kHz,HAL配置的是44.1kHz,需要重采样,这里做复位初始化工作.if (in->resampler)in->resampler->reset(in->resampler);in->frames_in = 0;adev->input_source = in->input_source;adev->in_device = in->device;adev->in_channel_mask = in->channel_mask;
......
}

read_frames:

static ssize_t read_frames(struct stream_in *in, void *buffer, ssize_t frames)
{//记录累积读取到帧数.ssize_t frames_wr = 0;size_t frame_size = audio_stream_in_frame_size(&in->stream);//循环读完为止while (frames_wr < frames) {//剩余要读的帧数为总共读的帧数减掉已经读到的帧数size_t frames_rd = frames - frames_wr;if (in->resampler != NULL) {//用从采样的库函数来获取bufferin->resampler->resample_from_provider(in->resampler,//读一次,根据frames_wr的值偏移一下(int16_t *)((char *)buffer +frames_wr * frame_size),//实际一次读取到的帧数&frames_rd);} else {
......}
......//累计总共读取到的帧数.frames_wr += frames_rd;}return frames_wr;
}

resampler_resample_from_provider():

int resampler_resample_from_provider(struct resampler_itfe *resampler,int16_t *out,size_t *outFrameCount)
{struct resampler *rsmp = (struct resampler *)resampler;......size_t framesRq = *outFrameCount;if (framesRq != rsmp->frames_rq) {//换算成实际采样率需要采的帧率.rsmp->frames_needed = (framesRq * rsmp->in_sample_rate) / rsmp->out_sample_rate + 1;rsmp->frames_rq = framesRq;}size_t framesWr = 0;spx_uint32_t inFrames = 0;while (framesWr < framesRq) {if (rsmp->frames_in < rsmp->frames_needed) {// make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at// least the number of frames needed to produce the number of frames requested at// the output sampling rateif (rsmp->in_buf_size < rsmp->frames_needed) {//更新buffer sizersmp->in_buf_size = rsmp->frames_needed;//分配bufferrsmp->in_buf = (int16_t *)realloc(rsmp->in_buf,rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t));}struct resampler_buffer buf;//每次想获取的frame count.buf.frame_count = rsmp->frames_needed - rsmp->frames_in;//获取buffer的核心函数rsmp->provider->get_next_buffer(rsmp->provider, &buf);if (buf.raw == NULL) {break;}//拷贝buf.frame_count大小(这个值可能在get_next_buffer()中被修改)到rsmp->in_buf,//偏移没* sizeof(int16_t)是因为in_buf是int16类型.memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count,buf.raw,buf.frame_count * rsmp->channel_count * sizeof(int16_t));//记录总共读取的帧数rsmp->frames_in += buf.frame_count;//用好就释放掉,实际只是更新了in->frames_in,这样前面拿过的数据不会再会重复拿了.rsmp->provider->release_buffer(rsmp->provider, &buf);}//spx_uint32_t outFrames = framesRq - framesWr;//一次要处理的帧数inFrames = rsmp->frames_in;if (rsmp->channel_count == 1) {
......} else {//inFrames会被改成实际能处理的帧数//outFrames返回世界写到out buffer中的帧数speex_resampler_process_interleaved_int(rsmp->speex_resampler,rsmp->in_buf,&inFrames,out + framesWr * rsmp->channel_count,&outFrames);}//累计更新实际处理帧数framesWr += outFrames;//如果输入帧数全部处理完,那么rsmp->frames_in和inFrames的值应该是是一样.rsmp->frames_in -= inFrames;//当上面说到的两个值都不一致,也就是输入和输出的实际值都不是我们想要达到的值时//就会提示警告信息.ALOGW_IF((framesWr != framesRq) && (rsmp->frames_in != 0),"ReSampler::resample() remaining %zu frames in and %zu frames out",rsmp->frames_in, (framesRq - framesWr));}//最后把未能处理的帧数直接copy到in_buf开头,等到下一次再处理.if (rsmp->frames_in) {memmove(rsmp->in_buf,rsmp->in_buf + inFrames * rsmp->channel_count,rsmp->frames_in * rsmp->channel_count * sizeof(int16_t));}//更新处理掉的帧*outFrameCount = framesWr;return 0;
}

get_next_buffer():

static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,struct resampler_buffer* buffer)
{struct stream_in *in;size_t i,size;
......in = (struct stream_in *)((char *)buffer_provider -offsetof(struct stream_in, buf_provider));
......//start_input_stream()中设置为0,第一次进来if (in->frames_in == 0) {//size好像没用到???size = pcm_frames_to_bytes(in->pcm,pcm_get_buffer_size(in->pcm));//调用tinyalsa接口,从kernel读取录音数据.in->read_status = pcm_read(in->pcm,//放在之前adev_open_input_stream()中申请的in->buffer中,//之前的疑问是in->buffer申请多乘了一个channel,这里应证了,因为//pcm_frames_to_bytes()就是period_size*channel*format(void*)in->buffer,pcm_frames_to_bytes(in->pcm, in->config->period_size));
......//看这个参数貌似用来保存帧大小in->frames_in = in->config->period_size;
......}//buffer->frame_count为要获取的帧数,非resample时就是上层一次要读取的总frame//resample时会经过一个sample rate的比例转换. 得到的结果表明实际需要读取的字节数,//如果in->frames_in比buffer->frame_count,那么我们只需要buffer->frame_count大小就够了.buffer->frame_count = (buffer->frame_count > in->frames_in) ?in->frames_in : buffer->frame_count;//调试结果是in->config->period_size 和in->frames_in一直一样,因此上面if语句里已经赋值了,//那就一直是从buffer开头存放没有再次利用了. 当in->frames_in比buffer->frame_count大的时候//release_buffer()会改变in->frames_in的值,偏移段前面的内容表示已经释放掉了.buffer->i16 = in->buffer +(in->config->period_size - in->frames_in) *audio_channel_count_from_in_mask(in->channel_mask);return in->read_status;
}

[RK3288][Android6.0] Audio录音HAL层的数据读取流程分析相关推荐

  1. [RK3288][Android6.0] Audio录音frame rate设置流程小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 HAL: 默认的配置在 hardware/rockchip/audio/tinyalsa_hal/ ...

  2. [RK3288][Android6.0] Audio的音量设置流程小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 说明一: AudioManager提供了两个调节音量接口 adjustSuggestedStrea ...

  3. 声道切换 android,[RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 播放音乐是单声道,硬件用的是双声道. AudioFlinger::PlaybackThread:: ...

  4. [RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 播放音乐是单声道,硬件用的是双声道. AudioFlinger::PlaybackThread:: ...

  5. [RK3288][Android6.0] 设置中通过Sensor旋转显示画面小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 Settings -> Display有个选项控制旋转屏幕时内容是否跟着旋转 这个功能是通过 ...

  6. [RK3288][Android6.0] 调试笔记 --- WiFi芯片AP6356S添加

    Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 背景: RK3288 Android6.0平台对AP6356S的支持不是很完善,直接修改wifi ch ...

  7. [RK3288][Android6.0] 调试笔记 --- touch无法获取坐标点

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 有网友遇到调试touch的时候能触发中断,但无法获取坐标点 具体可参考文章 [RK3288][An ...

  8. [RK3288][Android6.0] 调试笔记 --- AndroidTool低格无效问题

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 现象: 之前文章 [RK3288][Android6.0] 调试笔记 - AndroidTool两 ...

  9. [RK3288][Android6.0] 调试笔记 --- 电池电量一直显示100%

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 之前文章[RK3288][Android6.0] 调试笔记 - 伪电池驱动添加 阐述了如何添加一个 ...

  10. mysql 8.0 一条insert语句的具体执行流程分析(二)

    继续上一篇文章:mysql 8.0 一条insert语句的具体执行流程分析(一)_一缕阳光的博客-CSDN博客 由于最近换工作一直在试用期内,在拼命的学习.总结中,因此没有时间写文章,今天转正了腾出来 ...

最新文章

  1. SQL获取当月天数的几种方法
  2. Alpha版本项目展示要求
  3. 科沃斯机器人双十一全渠道成交额超7亿 两大爆款单品携手破亿
  4. cometD离线消息
  5. 【ruoyi若依】echarts图表跑版
  6. RxSwift之深入解析场景特征序列的使用和底层实现
  7. HALCON示例程序measure_pump.hdev螺纹孔位置与尺寸测量
  8. 计算机网络cr什么意思,现在网络上的“CR”是指什么意思
  9. 转发网络《iOS网络编程与云端应用最佳实践》微博转发送书了
  10. java第七章第九题_Java2程序设计基础第七章课后习题
  11. 在centos6.5上编译安装httpd-2.4和2.4版本特性介绍
  12. 高质量程序好文分享之王垠《编程的智慧》
  13. python之常用快捷键
  14. 知识产权服务代理行业税收政策
  15. 谷歌地图 图片保存_Google如何在地图上跟踪并保存您的一举一动
  16. 模式匹配与正则表达式(一)
  17. (C语言编程)PTA里“三天打鱼两天晒网”
  18. 计算机机房必须用七氟丙烷灭火器吗,释放七氟丙烷气体会不会使人窒息?
  19. 互联网常用的网络用语
  20. 国仁老猫:视频号“混剪玩法”还能玩吗?有人已经月入2W。

热门文章

  1. 嘉兴 机器人仓库 菜鸟_菜鸟智能机器人仓库启用 助力包裹分得又快又好
  2. 应用变量(原名-型号伪装)
  3. 易基因综述:表观遗传学和表观育种在作物品种改良的重要作用(水稻+玉米+番茄+大豆+油菜)
  4. 关键词推荐工具中的用户引导机制之三:相关搜索query技术
  5. SOAP Action介绍
  6. action = “store_true
  7. thinkphp5.1接入银联支付
  8. 【usb】安卓usb网络共享(RNDIS)
  9. 分布式光伏运维服务器,分布式光伏电站专业运维管理
  10. EXCEL学会两个函数你能打败90%的人,countifs和vlookup