Odroid N2+持续录音程序卡顿问题求助(附Valgrind分析)
7×24小时声学录制卡顿问题排查求助
我用Odroid N2+搭配音频接口做7×24小时声学数据录制,录若干文件后程序卡顿。Valgrind检测堆内存发现明确泄漏,怀疑PortAudio库问题,自行编译后仍未解决。调试发现程序卡在recordCallback函数末尾,且该函数不再被PortAudio触发。以下是Valgrind堆摘要及录音程序代码,求协助解决卡顿问题:
Valgrind堆摘要信息
==1309067== HEAP SUMMARY: ==1309067== in use at exit: 42,461 bytes in 81 blocks ==1309067== total heap usage: 14,115 allocs, 14,034 frees, 1,489,720 bytes allocated ==1309067== ==1309067== 11 bytes in 1 blocks are definitely lost in loss record 3 of 72 ==1309067== at 0x4849D8C: malloc (in /usr/lib/aarch64-linux-gnu/valgrind/vgpreload_memcheck-arm64-linux.so) ==1309067== by 0x10A383: main (in /home/odroid/acoustic/Recorder/Recorder_N2plus_10min) ==1309067== ==1309067== 112 bytes in 1 blocks are definitely lost in loss record 54 of 72 ==1309067== at 0x484C0A4: calloc (in /usr/lib/aarch64-linux-gnu/valgrind/vgpreload_memcheck-arm64-linux.so) ==1309067== by 0x6243C2F: ??? ==1309067== by 0x4B4BD03: ??? (in /usr/lib/aarch64-linux-gnu/libasound.so.2.0.0) ==1309067== by 0x4B4C2F7: ??? (in /usr/lib/aarch64-linux-gnu/libasound.so.2.0.0) ==1309067== by 0x4B4F2CF: snd_pcm_open (in /usr/lib/aarch64-linux-gnu/libasound.so.2.0.0) ==1309067== by 0x48A6593: ??? (in /usr/lib/aarch64-linux-gnu/libportaudio.so.2.0.0) ==1309067== by 0x48A8B83: ??? (in /usr/lib/aarch64-linux-gnu/libportaudio.so.2.0.0) ==1309067== by 0x48AB60B: PaAlsa_Initialize (in /usr/lib/aarch64-linux-gnu/libportaudio.so.2.0.0) ==1309067== by 0x489F723: Pa_Initialize (in /usr/lib/aarch64-linux-gnu/libportaudio.so.2.0.0) ==1309067== by 0x10A4FB: main (in /home/odroid/acoustic/Recorder/Recorder_N2plus_10min) ==1309067== ==1309067== 1,360 bytes in 1 blocks are definitely lost in loss record 63 of 72 ==1309067== at 0x484C0A4: calloc (in /usr/lib/aarch64-linux-gnu/valgrind/vgpreload_memcheck-arm64-linux.so) ==1309067== by 0x62572AB: ??? ==1309067== by 0x4B4BD03: ??? (in /usr/lib/aarch64-linux-gnu/libasound.so.2.0.0) ==1309067== by 0x4B4C2F7: ??? (in /usr/lib/aarch64-linux-gnu/libasound.so.2.0.0) ==1309067== by 0x4B4F2CF: snd_pcm_open (in /usr/lib/aarch64-linux-gnu/libasound.so.2.0.0) ==1309067== by 0x48A6593: ??? (in /usr/lib/aarch64-linux-gnu/libportaudio.so.2.0.0) ==1309067== by 0x48A8B83: ??? (in /usr/lib/aarch64-linux-gnu/libportaudio.so.2.0.0) ==1309067== by 0x48AB60B: PaAlsa_Initialize (in /usr/lib/aarch64-linux-gnu/libportaudio.so.2.0.0) ==1309067== by 0x489F723: Pa_Initialize (in /usr/lib/aarch64-linux-gnu/libportaudio.so.2.0.0) ==1309067== by 0x10A4FB: main (in /home/odroid/acoustic/Recorder/Recorder_N2plus_10min) ==1309067== ==1309067== LEAK SUMMARY: ==1309067== definitely lost: 1,483 bytes in 3 blocks ==1309067== indirectly lost: 0 bytes in 0 blocks ==1309067== possibly lost: 0 bytes in 0 blocks ==1309067== still reachable: 40,978 bytes in 78 blocks ==1309067== suppressed: 0 bytes in 0 blocks ==1309067== Reachable blocks (those to which a pointer was found) are not shown. ==1309067== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==1309067== ==1309067== For lists of detected and suppressed errors, rerun with: -s ==1309067== ERROR SUMMARY: 160 errors from 23 contexts (suppressed: 0 from 0)
录音程序代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <portaudio.h> #include <sndfile.h> #include <time.h> #include <libconfig.h> #include <pthread.h> #define FRAMES_PER_BUFFER (512) #if 1 #define PA_SAMPLE_TYPE paFloat32 typedef float SAMPLE; #define SAMPLE_SILENCE (0.0f) #define PRINTF_S_FORMAT "%.8f" #elif 1 #elif 0 #endif const char * CurrentDateTime(); char* concat(char *s1, char *s2); char wavefilename[45]; char equip[15]; SNDFILE *outfileCB; SF_INFO sfinfo; char * FileNameHead; int filesize,NUM_CHANNELS,SAMPLE_RATE,duration; typedef struct { int frameIndex; int maxFrameIndex; SAMPLE *recordedSamples; } paTestData; static int recordCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { printf("Being RecordCallBack *************** \n"); int finished; static int totalRead = 0; (void)outputBuffer; (void)timeInfo; (void)statusFlags; (void)userData; finished = paContinue; if (totalRead * NUM_CHANNELS * 2 > filesize) { sf_close(outfileCB); char * BUFF_Time; BUFF_Time = CurrentDateTime(); char* s4=concat(FileNameHead,BUFF_Time); char* outfilename=concat(s4,".wav"); free(s4); if (!(outfileCB = sf_open(outfilename, SFM_WRITE, &sfinfo))) { puts(sf_strerror(NULL)); return 1; }; sleep(1); copyString(wavefilename,outfilename); free(outfilename); totalRead = 0; } int framesWritten = sf_writef_float(outfileCB, (const SAMPLE*)inputBuffer, framesPerBuffer); totalRead+= framesPerBuffer; return finished; } const char * CurrentDateTime() { time_t raw_time; raw_time=time(NULL); static char strbuffer[16]; strftime(strbuffer,16,"%m%d%Y_%H%M%S",localtime(&raw_time)); return strbuffer; } char* concat(char *s1, char *s2) { size_t len1 = strlen(s1); size_t len2 = strlen(s2); char *result = malloc(len1+len2+1); memcpy(result, s1, len1); memcpy(result+len1, s2, len2+1); return result; } int main(void); int main(void) { PaStreamParameters inputParameters; PaStream* stream; PaError err = paNoError; paTestData data; int i; int numSamples; int numBytes; int DeviceIdTarget; int isfocusrite; printf("patest_record.c\n"); fflush(stdout); config_t cfg; const char *EquipmentCC; const char *site; const char *area; filesize=0; NUM_CHANNELS=0; config_init(&cfg); if(! config_read_file(&cfg, "acoustic.cfg")) { fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); config_destroy(&cfg); return(EXIT_FAILURE); } filesize=SAMPLE_RATE*NUM_CHANNELS*duration*2; copyString(equip,EquipmentCC); data.maxFrameIndex = FRAMES_PER_BUFFER; data.frameIndex = 0; numSamples = FRAMES_PER_BUFFER * NUM_CHANNELS; numBytes = numSamples * sizeof(SAMPLE); SAMPLE recordedSamplesArr[numBytes]; // data.recordedSamples = recordedSamplesArr; data.recordedSamples = (SAMPLE *) malloc( numBytes ); if( data.recordedSamples == NULL ) { printf("Could not allocate record array.\n"); goto done; } for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0; memset(&sfinfo, 0, sizeof(sfinfo)); sfinfo.samplerate = SAMPLE_RATE; sfinfo.channels = NUM_CHANNELS; sfinfo.frames = 0; sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_16); char* Equipment=malloc(strlen(EquipmentCC)+1); strcpy(Equipment,EquipmentCC); char * BUFF_Time; BUFF_Time = CurrentDateTime(); char* s1 = concat(site, "_"); char* s2 = concat(s1,area); free(s1); char* s21 = concat(s2, "_"); free(s2); char* s3=concat(s21,Equipment); free(s21); FileNameHead=concat(s3,"_"); free(s3); char* s4=concat(FileNameHead,BUFF_Time); char* outfilename=concat(s4,".wav"); copyString(wavefilename,outfilename); free(s4); config_destroy(&cfg); if (!(outfileCB = sf_open(outfilename, SFM_WRITE, &sfinfo))) { puts(sf_strerror(NULL)); return 1; }; free(outfilename); err = Pa_Initialize(); if (err != paNoError) goto done; int numDevices =Pa_GetDeviceCount(); if (numDevices < 0) { err = numDevices; goto done; } DeviceIdTarget=1; const PaDeviceInfo *deviceInfo; const PaHostApiInfo *apiInfo; for (i = 0; i<numDevices; i++) { deviceInfo = Pa_GetDeviceInfo(i); apiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); isfocusrite=checkStringMatch((char *)deviceInfo->name, "Scarlett"); if(isfocusrite&(deviceInfo->maxInputChannels>1)) DeviceIdTarget=i; if(deviceInfo->maxInputChannels==1) DeviceIdTarget=i; } inputParameters.device = DeviceIdTarget; if (inputParameters.device == paNoDevice) { goto done; } inputParameters.channelCount = NUM_CHANNELS; inputParameters.sampleFormat = PA_SAMPLE_TYPE; inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; inputParameters.hostApiSpecificStreamInfo = NULL; err = Pa_OpenStream( &stream, &inputParameters, NULL, SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff, recordCallback, &data); if (err != paNoError) goto done; err = Pa_StartStream(stream); if (err != paNoError) goto done; fflush(stdout); getchar(); err = Pa_CloseStream(stream);// if (err != paNoError) goto done; sf_close(outfileCB); free(FileNameHead); cleanup: free(data.recordedSamples); done: Pa_Terminate(); if (err != paNoError) { err = 1; } return err; free(Equipment); }
问题分析与解决建议
1. 回调阻塞是卡顿核心原因
PortAudio的回调函数要求绝对不能阻塞,但你的代码在recordCallback中调用了sleep(1),直接阻塞音频流线程,最终导致PortAudio停止触发回调,出现卡顿。另外,回调中还执行了文件打开/关闭、内存分配等耗时操作,这些都是回调的禁忌——回调必须尽可能快完成,不能有任何阻塞或耗时逻辑。
修复:
- 把文件切换逻辑移到主线程,用线程安全的标志位通知主线程执行切换,不要在回调里做这些操作;
- 移除回调中的
sleep(1)和所有耗时IO、内存分配操作。
2. 内存泄漏修复
Valgrind显示的泄漏分两部分:
- 自己代码中的泄漏:比如
main函数里free(Equipment);在return err;之后,永远不会执行,调整代码顺序把free(Equipment);移到return之前; - ALSA/PortAudio库的小泄漏:如果是库本身问题,小泄漏在7×24小时场景下影响不大,优先解决自己代码的泄漏。
3. 其他潜在问题修复
- 补充
copyString和checkStringMatch函数的实现,当前代码直接调用未定义函数,会引发运行时错误; - 完善配置文件读取逻辑,用
config_lookup_string和config_lookup_int从acoustic.cfg中读取SAMPLE_RATE、NUM_CHANNELS等变量的值,当前这些变量初始值为0,会导致文件切换逻辑失效; - 统一采样格式:PortAudio用
paFloat32,但libsndfile写的是SF_FORMAT_PCM_16,要么PortAudio改用paInt16,要么libsndfile改用SF_FORMAT_FLOAT,避免音频数据损坏。
内容的提问来源于stack exchange,提问作者Susan
相关产品推荐
相关产品推荐

