platform:px30(rk平台)
OS:Android8.1
问题描述:在使用系统录音机以及其他录音软件录音时,会出现apk 录制回来的音频会出现部分数据丢失的情况,导致录制的音频会出现节奏跳动等异常现象!
后经过排查,发现是因为audioflinger 获取音频数据时出现了overrun 的情况导致数据丢失!
贴上overrun 报错部分代码(frameworks/av/services/audioflinger/Threads.cpp):
// Read from HAL to keep up with fastest client if multiple active tracks, not slowest one.
// Only the client(s) that are too slow will overrun. But if even the fastest client is too
// slow, then this RecordThread will overrun by not calling HAL read often enough.
// If destination is non-contiguous, first read past the nominal end of buffer, then
// copy to the right place. Permitted because mRsmpInBuffer was over-allocated.
int32_t rear = mRsmpInRear & (mRsmpInFramesP2 - 1);
ssize_t framesRead;
// If an NBAIO source is present, use it to read the normal capture's data
if (mPipeSource != 0) {
size_t framesToRead = mBufferSize / mFrameSize;
framesToRead = min(mRsmpInFramesOA - rear, mRsmpInFramesP2 / 2);
framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
framesToRead);
ALOGW("framesRead :%zd framesToRead :%zd \n",framesRead,framesToRead);
// since pipe is non-blocking, simulate blocking input by waiting for 1/2 of
// buffer size or at least for 20ms.
size_t sleepFrames = max(
min(mPipeFramesP2, mRsmpInFramesP2) / 2, FMS_20 * mSampleRate / 1000);
if (framesRead <= (ssize_t) sleepFrames) {
sleepUs = (sleepFrames * 1000000LL) / mSampleRate;
}
if (framesRead < 0) {
status_t status = (status_t) framesRead;
switch (status) {
case OVERRUN:
ALOGW("overrun on read from pipe");
framesRead = 0;
break;
case NEGOTIATE:
ALOGE("re-negotiation is needed");
framesRead = -1; // Will cause an attempt to recover.
break;
default:
ALOGE("unknown error %d on read from pipe", status);
break;
}
}
// otherwise use the HAL / AudioStreamIn directly
}
通过代码可以看见当framesToRead参数,也就是获取一帧数据的缓冲区大小不够接收由底层获取到的数据时,read 函数就会返回OVERRUN,出现overrun 时,缓冲区就被置0,这一帧的数据也就相对的丢失了。
分析了原因后,现提出解决办法,首先需要确认audio hal 设置的帧数据缓冲区,代码位置hardware/rockchip/audio/tinyalsa_hal/audio_hw.h
struct pcm_config pcm_config_in = {
.channels = 4,
.rate = 48000,//44100,
#ifdef SPEEX_DENOISE_ENABLE
.period_size = 1024,
#else
.period_size = 480,//256,
#endif
.period_count = 8,
.format = PCM_FORMAT_S16_LE,
.flag = HW_PARAMS_FLAG_LPCM,
};
period_size 和period_count 这两个参数就是缓冲区大小,这儿我设置的480 和8.
确定了这儿设置的参数,改起来就很简单了,在上面的Threads.cpp中修改:
framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
3840);//framesToRead);
3840是period_size * period_count的大小,在读取音频数据时,hal 传输的数据大小,与audioflinger 获取到的数据大小一致,则能保证缓冲区不会因为数据读取过短,或者hal 层数据传输过快,导致缓冲区溢出。
这顺便分享遇见音频问题的一般调试办法:
录音问题:一般先确定驱动录制的音频没有问题,可以使用Androidsdk 里提供的tinycap 工具录制从硬件mic 直接传输回的数据,tinycap 源码位于/external/tinyalsa/ 下,可以设置多种采样率,采样位数,通道数,录制wav 文件,使用audacity软件分析音频,如音频异常,需要检查音频驱动,平台端和codec 端,问题大多出在clk 不同步。
底层获取到的音频数据没问题时,就往上排查,到audio hal ,hal 是调用/external/tinyalsa/pcm.c 里面的接口获取数据,在hal 层可抓取debug 音频,在audio_hw.c 中添加
#define ALSA_IN_DEBUG
#ifdef ALSA_IN_DEBUG
FILE *in_debug;
#endif
#ifdef ALSA_IN_DEBUG
in_debug = fopen("/data/debug.pcm","wb");//please touch /data/debug.pcm first
#endif
#ifdef ALSA_IN_DEBUG
fwrite(buffer,frames_wr * frame_size,1,in_debug);
#endif
这儿给的示例,具体fwrite参数需要根据你自定义的参数写入,不然写入的数据会是一堆乱码,这儿不知道怎么给参数的,欢迎私信!
我这儿排查到audioflinger 的overrun 问题,也是在驱动层和hal层抓取的debug音频文件没问题,才查到audioflinger ,
最后就是排查audioflinger ,这一层涉及到的代码量较大,建议说根据具体的log 报错位置去排查!