Android 录音数据传输
今天来看看Android中的录音数据是怎么来的。
从AudioRecord开始看吧。
AudioRecord中可以取得录音数据的接口是:AudioRecord::read。
首先调用函数obtainBuffer取得录音数据的地址。
然后用memcpy将录音数据copy出来。
看样子,数据来源是obtainBuffer函数了。
来看看函数AudioRecord::obtainBuffer。
其主要功能就是对传入的audioBuffer进行赋值。
audioBuffer是Buffer* 类型。
看看Buffer类:
class Buffer
{
public:
enum {
MUTE = 0x00000001
};
uint32_t flags;
int channelCount;
int format;
size_t frameCount;
size_t size;
union {
void* raw;
short* i16;
int8_t* i8;
};
};
其中保存数据的是下面这块东东:
union {
void* raw;
short* i16;
int8_t* i8;
};
函数AudioRecord::obtainBuffer中对这块东东赋值的代码如下:
audioBuffer->raw = (int8_t*)cblk->buffer(u);
cblk的来历:
audio_track_cblk_t* cblk = mCblk;
mCblk的赋值在函数AudioRecord::openRecord中被赋值:
mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); // 可见mCblk头部保存的是结构体的信息,后面跟的是数据
函数audio_track_cblk_t::buffer的实现:
void* audio_track_cblk_t::buffer(uint64_t offset) const
{
return (int8_t *)this->buffers + (offset - userBase) * this->frameSize;
}
可见数据就保存在audio_track_cblk_t结构体中。
是什么地方往结构体audio_track_cblk_t中写的数据呢?
发现函数AudioRecord::obtainBuffer中,在获取buffer地址时,首先会调用函数audio_track_cblk_t::framesReady来判断有多少数据准备好了。
想起来在播放数据的时候,使用audio_track_cblk_t中的数据时,也调用了函数audio_track_cblk_t::framesReady。
而往audio_track_cblk_t中写数据时,调用了函数audio_track_cblk_t::framesAvailable。
录音肯定也是这样的了。
也就是说,找到调用函数audio_track_cblk_t::framesAvailable的地方,也就找到了往audio_track_cblk_t中写数据的地方。
录音相关,调用audio_track_cblk_t::framesAvailable的地方如下:
AudioFlinger::RecordThread::RecordTrack::getNextBuffer函数。
函数AudioFlinger::RecordThread::RecordTrack::getNextBuffer的主要作用是给传入的AudioBufferProvider::Buffer赋值。
AudioBufferProvider::Buffer结构体类型:
struct Buffer {
union {
void* raw;
short* i16;
int8_t* i8;
};
size_t frameCount;
};
保存数据的是下面这块东东:
union {
void* raw;
short* i16;
int8_t* i8;
};
函数AudioFlinger::RecordThread::RecordTrack::getNextBuffer对这块东东赋值的代码如下:
buffer->raw = getBuffer(s, framesReq);
函数AudioFlinger::ThreadBase::TrackBase::getBuffer返回了一个int8_t型指针bufferStart。
对bufferStart赋值的代码如下:
int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase)*cblk->frameSize;
mBuffer的赋值在AudioFlinger::ThreadBase::TrackBase::TrackBase的构造函数中:
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
mCblk 的来历:
mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); 或 new(mCblk) audio_track_cblk_t();
这个在研究播放音频流的时候已经看过,这儿就不重复了。
肯定是哪个地方调用了函数AudioFlinger::RecordThread::RecordTrack::getNextBuffer,获取一段buffer,然后将录音数据写入到这个buffer。
搜搜调用函数AudioFlinger::RecordThread::RecordTrack::getNextBuffer的地方,录音相关的调用有以下:
AudioFlinger::RecordThread::threadLoop函数
AudioFlinger::RecordThread::threadLoop函数中,调用函数AudioFlinger::RecordThread::RecordTrack::getNextBuffer获取了buffer。
然后将buffer赋值给了dst:
int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) * mActiveTrack->mCblk->frameSize;
下面要分两种情况来讨论了:
+++++++++++++++++++++++++++++++++++++++不需要resampling的情况-start++++++++++++++++++++++++++++++++++++++++++++++
往dst写数据的有以下两个地方:
while (framesIn--) {
*dst16++ = *src16;
*dst16++ = *src16++;
}
或:
while (framesIn--) {
*dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1);
src16 += 2;
}
也就是说数据来源是src了,看看src的由来。
int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize;
对mRsmpInBuffer赋值的地方在函数AudioFlinger::RecordThread::readInputParameters中:
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
这儿只是new了个buffer,寻找数据来源,还要看哪儿往里面写数据了。
往 mRsmpInBuffer中写数据的地方也在AudioFlinger::RecordThread::threadLoop函数中:
mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
----------------------------------------不需要resampling的情况-end-----------------------------------------------
+++++++++++++++++++++++++++++++++++++++需要resampling的情况-start++++++++++++++++++++++++++++++++++++++++++++++
往dst写数据的地方:
while (framesOut--) {
*dst++ = (int16_t)(((int32_t)*src + (int32_t)*(src + 1)) >> 1);
src += 2;
}
也就是说数据来源是src了,看看src的由来。
int16_t *src = (int16_t *)mRsmpOutBuffer;
对mRsmpInBuffer赋值的地方在函数AudioFlinger::RecordThread::readInputParameters中:
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
这儿只是new了个buffer,寻找数据来源,还要看哪儿往里面写数据了。
往 mRsmpInBuffer中写数据的地方在AudioFlinger::RecordThread::getNextBuffer函数中:
mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
下面看看对AudioFlinger::RecordThread::getNextBuffer函数的调用:
首先函数AudioFlinger::RecordThread::threadLoop中调用了函数AudioResamplerOrder1::resample。
mResampler->resample(mRsmpOutBuffer, framesOut, this);
函数AudioResamplerOrder1::resample调用了函数AudioResamplerOrder1::resampleMono16:
resampleMono16(out, outFrameCount, provider);
函数AudioResamplerOrder1::resampleMono16中调用了函数AudioFlinger::RecordThread::getNextBuffer:
provider->getNextBuffer(&mBuffer);
mResampler的赋值在函数AudioFlinger::RecordThread::readInputParameters中:
mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
函数AudioResampler::create根据参数,返回不同的resampler:
switch (quality) {
default:
case LOW_QUALITY:
LOGV("Create linear Resampler");
resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
break;
case MED_QUALITY:
LOGV("Create cubic Resampler");
resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
break;
case HIGH_QUALITY:
LOGV("Create sinc Resampler");
resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
break;
}
从上面对函数AudioFlinger::RecordThread::getNextBuffer的调用可知,mRsmpOutBuffer最终作为参数out传给了函数AudioResamplerOrder1::resampleMono16。
最终往mRsmpOutBuffer中写数据的地方是在函数AudioResamplerOrder1::resampleMono16中:
// handle boundary case
while (inputIndex == 0) {
// LOGE("boundary case\n");
int32_t sample = Interp(mX0L, in[0], phaseFraction);
out[outputIndex++] += vl * sample;
out[outputIndex++] += vr * sample;
Advance(&inputIndex, &phaseFraction, phaseIncrement);
if (outputIndex == outputSampleCount)
break;
}
或:
while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
int32_t sample = Interp(in[inputIndex-1], in[inputIndex],
phaseFraction);
out[outputIndex++] += vl * sample;
out[outputIndex++] += vr * sample;
Advance(&inputIndex, &phaseFraction, phaseIncrement);
}
in的来历:
int16_t *in = mBuffer.i16;
mBuffer的赋值:
provider->getNextBuffer(&mBuffer);
回到了刚次说的对函数AudioFlinger::RecordThread::getNextBuffer的调用。
函数AudioFlinger::RecordThread::getNextBuffer中首先调用函数AudioStreamInALSA::read获取数据指针。
mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
然后将数据地址赋值给传入的AudioBufferProvider::Buffer指针:
buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount;
下面看看resampling对数据的处理。
int32_t sample = Interp(in[inputIndex-1], in[inputIndex],
phaseFraction);
phaseFraction的来源:
uint32_t phaseFraction = mPhaseFraction;
phaseFraction作为参数传给了函数Advance:
Advance(&inputIndex, &phaseFraction, phaseIncrement);
函数Advance中对phaseFraction进行了赋值:
static inline void Advance(size_t* index, uint32_t* frac, uint32_t inc) {
*frac += inc;
*index += (size_t)(*frac >> kNumPhaseBits);
*frac &= kPhaseMask;
}
常量的定义:
// number of bits for phase fraction - 28 bits allows nearly 8x downsampling
static const int kNumPhaseBits = 28;
// phase mask for fraction
static const uint32_t kPhaseMask = (1LU<<kNumPhaseBits)-1;
再看看Interp函数:
static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) {
return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits);
}
常量定义:
// number of bits used in interpolation multiply - 15 bits avoids overflow
static const int kNumInterpBits = 15;
// bits to shift the phase fraction down to avoid overflow
static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
再看vl和vr:
int32_t vl = mVolume[0];
int32_t vr = mVolume[1];
mVolume在函数AudioResampler::setVolume中被赋值:
void AudioResampler::setVolume(int16_t left, int16_t right) {
// TODO: Implement anti-zipper filter
mVolume[0] = left;
mVolume[1] = right;
}
函数AudioResampler::setVolume在函数AudioFlinger::RecordThread::readInputParameters中被调用:
mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
常量定义:
static const uint16_t UNITY_GAIN = 0x1000;
----------------------------------------需要resampling的情况-end-----------------------------------------------
可以,无论是否resampling,都是通过调用函数AudioStreamInALSA::read来获取数据。
函数AudioStreamInALSA::read中调用了ALSA Lib中的函数snd_pcm_mmap_readi或函数snd_pcm_readi来取得数据:
if (mHandle->mmap)
n = snd_pcm_mmap_readi(mHandle->handle, buffer, frames);
else
n = snd_pcm_readi(mHandle->handle, buffer, frames);
Android 录音数据传输相关推荐
- iphone8 android传文件,如何从iPhone或Android将数据传输到新的iPhone 8
第1部分:如何将数据传输到新的iPhone 8? Wondershare MobileTrans提供了一种快速可靠的方式将数据从一个设备移动到另一个设备.它具有直接的电话到电话传输功能,支持所有主要数 ...
- Android录音转为MP2的实现
Android录音转为MP2的实现 利用Android提供的AudioRecord类以及开源编码库twolame,实现了android手机边录音边编码,录音完成直接得到MP2音频文件.由于Androi ...
- Android录音下————AudioRecord源码分析
Android录音下----AudioRecord源码分析 文章目录 Android录音下----AudioRecord源码分析 一.概述 1.主要分析点 2.储备知识 二.getMinBufferS ...
- android 录音的格式,Android录音mp3格式实例详解
Android录音支持的格式有amr.aac,但这两种音频格式在跨平台上表现并不好. MP3显然才是跨平台的最佳选择. 项目地址 实现思路概述 在分析代码前,我们需要明确几个问题 1. 如何最终生成M ...
- Android音频录制方案,Android录音,录制其他App播放的声音
Android录音,录制其他App播放的声音 从Android10(SDK 29)版本开始,可以设置录音App的源为其他App,这样就可以录制其他App播放的声音 此方案有以下注意几点 设置了源为其他 ...
- Android录音amr实时转成MP3格式
文章目录 MP3 录音使用说明 步骤一:下载NDK,并配置(Mac) 步骤二:修改C代码相关路径,编译成so库 步骤三:应用层代码代码调用系统AudioRecord类开始录音 开始录音 start() ...
- Android录音并进行本地转码为MP3
** Android录音并进行本地转码 ** 通过安卓手机进行录音, 录音后,使用lame进行转码操作 开发中需要使用这个功能,只是一个简单的进行转码的工具,具体的代码信息如下 项目的基本结构图 1. ...
- Android录音-SoundTouch移植到Android
Android录音-SoundTouch移植到Android 文章目录 Android录音-SoundTouch移植到Android 一.SoundTouch介绍 二.移植SoundTouch(And ...
- Androidの录音实现
Androidの录音实现 1. 录音功能需要使用android.media.MediaRecorder来完成,这里我们列出几个重要的使用方法,也是最常用的录音接口. 1.MediaRecorder r ...
最新文章
- 数据质量和模型调优哪个更重要?
- Mybatis【一对多、多对一、多对多】知识要点
- 软件测试真实项目_企业中软件测试的项目流程
- 自学python清单-我的2018学习清单
- centos进入单用户模式
- MathType中的一些精彩技巧
- POJ 3259 SPFA
- how is SAP UI5 Model.setProperty implemented
- 克制懒惰之飞鸽传书版
- Costco的中国门徒已经参透了零售成功秘笈
- git与svn, tfs等源代码管理器的协同
- u3d一个GameObject绑定两个AudioSource
- stm32读取驾驶模拟器数据 stm32F407读取joystick数据
- js禁止退出当前页面
- shell应用之监测电脑内存、负载和磁盘的使用情况
- 3.Ubuntu/Deepin下安装Monaco/Menlo字体
- sqlplus linux 连接数据库,sqlplus连接Oracle
- 关于奥威亚自动录播系统的设置使用小笔记
- df满足条件的值修改_python – 如何根据其他列中的条件将pandas df列中的多个值更改为np.nan?...
- spacy实体关系抽取_使用spacy从Wikipedia文章中命名实体识别
热门文章
- 学计算机去银行工作有什么要求,进入银行工作需要什么条件
- Elasticsearch/Kibana/X-pack/Logstash 6.x版本的安装
- Linux怎么接移动热点,Linux网络配置:手提在连接WIFI热点情况下,Ubuntu16.04中怎样配置网络?...
- Uniapp之使用h5+打开地图导航
- Python学习笔记九10:shelve模块
- 直方图均衡化 matlab代码,基于matlab的直方图均衡化代码
- 2022-2028全球疤痕护理产品行业调研及趋势分析报告
- 虚拟世界和现实世界的未来
- java通过麒麟实现开机自启动,京东四面:说说Tomcat 在 SpringBoot 中是如何启动的!...
- 实践:uniapp中webview内置网页与app实时通讯