You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 06:33:09