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

支持ALSA的嵌入式Linux运行C++音频程序出现1秒延迟的排查

ADSL路由器USB音频直通链路固定1秒延迟排查问题

问题背景

  • 目标设备为搭载Ralink rt63365 4核CPU(运行频率约400Hz)的ADSL路由器,运行Linux v2.6.22.15内核,ALSA驱动版本为1.0.14
  • 已基于同芯片路由器的GPL授权源码完成内核、根文件系统编译构建,为内核添加ALSA支持后成功部署到目标路由器
  • 调试目标:实现USB声卡采集信号直通播放端的功能,测量并优化音频链路延迟

已验证的现象与已完成调试

  • 已消除链路中所有抖动,尝试调整运行线程调度优先级、测试大量硬件参数组合,始终无法将延迟降低到1秒以下
  • 初始化设备时设置缓冲区为128帧、周期大小为1帧,单声道44100Hz采样率配置下延迟固定为1秒;切换为8000Hz采样率配置,延迟数值完全一致
  • 程序运行期间CPU、内存剩余资源充足,已关闭telnet、samba等可能干扰程序运行的非必要系统服务
  • 仅初始化播放设备播放本地PCM文件时不存在可感知延迟,程序启动后可立刻正常输出声音
  • 先后测试三款不同定位的USB音频设备:廉价USB声卡、支持USB声卡模式的Zoom H1录音机、Presonus 22vsl音频接口,测得的延迟完全一致
  • 同一份程序搭配上述所有声卡,在安装5.x以上版本内核、对应版本ALSA的PC上运行完全正常,无明显可感知延迟

核心程序代码

void setscheduler(void)
{
    struct sched_param sched_param;

    if (sched_getparam(0, &sched_param) < 0) {
        printf("Scheduler getparam failed...\n");
        return;
    }
    sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
    if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
        printf("Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority);
        fflush(stdout);
        return;
    }
    printf("!!!Scheduler set to Round Robin with priority %i FAILED!!!\n", sched_param.sched_priority);
}

int setup_alsa_handle(snd_pcm_t *handle, snd_pcm_stream_t stream)
{

  int err = 0;
  snd_pcm_hw_params_t *hw_params;
  snd_pcm_sw_params_t *sw_params;

  if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
  {
    fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n",
            snd_strerror(err));
    exit(1);
  }

  if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0)
  {
    fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n",
            snd_strerror(err));
    exit(1);
  }

  if ((err = snd_pcm_hw_params_set_access(handle, hw_params,
                                          SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
  {
    fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err));
    exit(1);
  }

  if ((err = snd_pcm_hw_params_set_format(handle, hw_params, AUDIO_FORMAT)) < 0)
  {
    fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err));
    exit(1);
  }

  unsigned int rate = SAMPLE_RATE;
  if ((err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rate, 0)) <
      0)
  {
    fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err));
    exit(1);
  }

  int dir;
  snd_pcm_uframes_t period_size = PERIOD_SIZE_IN_FRAMES;
  if ((err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, &dir)) <
      0)
  {
    fprintf(stderr, "cannot set period size (%s)\n", snd_strerror(err));
    exit(1);
  }

  int channels;
  if (stream == SND_PCM_STREAM_CAPTURE)
  {
    channels = CAPTURE_CHANNELS;
  }
  else
  {
    channels = PLAYBACK_CHANNELS;
  }
  if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, channels)) < 0)
  {
    fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err));
    exit(1);
  }

  if ((err = snd_pcm_hw_params(handle, hw_params)) < 0)
  {
    fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err));
    exit(1);
  }

  snd_pcm_hw_params_free(hw_params);

    if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {
        fprintf(stderr, "cannot allocate software parameters structure(%s)\n",
            snd_strerror(err));
        return err;
    }
    if ((err = snd_pcm_sw_params_current(handle, sw_params)) < 0) {
        fprintf(stderr, "cannot initialize software parameters structure(%s)\n",
            snd_strerror(err));
        return err;
    }
    if ((err = snd_pcm_sw_params_set_avail_min(handle, sw_params, FRAMES_PER_BUFFER)) < 0) {
        fprintf(stderr, "cannot set minimum available count(%s)\n",
            snd_strerror(err));
        return err;
    }
    if ((err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, 0U)) < 0) {
        fprintf(stderr, "cannot set start mode(%s)\n",
            snd_strerror(err));
        return err;
    }
    if ((err = snd_pcm_sw_params(handle, sw_params)) < 0) {
        fprintf(stderr, "cannot set software parameters(%s)\n",
            snd_strerror(err));
        return err;
    }
  return 0;
}

void start_stream(snd_pcm_t *_playback_handle, snd_pcm_t *_capture_handle,
                  void (*stream_callback)(const void *, void *, unsigned long,
                                          void *),

                  void (*controls_callback)(void *), void *data)
{

  audio_sample *in_buffer;
  audio_sample *out_buffer;

  FILE *fout = NULL;

  int in_result = 0;
  int out_result = 0;
  int temp_n = 0;

  int buffer_frames = FRAMES_PER_BUFFER;
  int frame_size_in_bytes = snd_pcm_format_width(AUDIO_FORMAT) / 8;

  int in_buffer_frames = buffer_frames * CAPTURE_CHANNELS;
  in_buffer = (audio_sample *)malloc(in_buffer_frames * frame_size_in_bytes);

  int out_buffer_frames = buffer_frames * PLAYBACK_CHANNELS;
  out_buffer = (audio_sample *)malloc(out_buffer_frames * frame_size_in_bytes);

  memset(in_buffer, SAMPLE_SILENCE, in_buffer_frames * frame_size_in_bytes);
  memset(out_buffer, SAMPLE_SILENCE, out_buffer_frames * frame_size_in_bytes);

    int err;

  while (1)
  {
    int avail;

        if ((err = snd_pcm_wait(_playback_handle, 1000)) < 0) {
            fprintf(stderr, "poll failed(%s)\n", strerror(errno));
            break;
        }         


        avail = snd_pcm_avail_update(_capture_handle);
    fprintf(stderr, "1 avail (%d)\n", avail);
        if (avail > 0) {
            if (avail > FRAMES_PER_BUFFER)
                avail = FRAMES_PER_BUFFER;

            snd_pcm_readi(_capture_handle, in_buffer, avail);
        }

        avail = snd_pcm_avail_update(_playback_handle);
    fprintf(stderr, "2 avail (%d)\n", avail);
        if (avail > 0) {
            if (avail > FRAMES_PER_BUFFER)
                avail = FRAMES_PER_BUFFER;

            snd_pcm_writei(_playback_handle, in_buffer, avail);
        }

  }
}

/**
 * Main
 */
int main(int argc, char *argv[])
{
  setscheduler();
  
  if ((err = snd_pcm_open(&capture_handle, CAPTURE_AUDIO_DEVICE, SND_PCM_STREAM_CAPTURE, 0)) < 0)
  {
    fprintf(stderr, "cannot open audio device '%s'. Error: %s\n", CAPTURE_AUDIO_DEVICE, snd_strerror(err));
    exit(1);
  }
  setup_alsa_handle(capture_handle, SND_PCM_STREAM_CAPTURE);

  // Init playback device
  if ((err = snd_pcm_open(&playback_handle, PLAYBACK_AUDIO_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
  {
    fprintf(stderr, "cannot open audio device '%s'. Error: %s\n", PLAYBACK_AUDIO_DEVICE, snd_strerror(err));
    exit(1);
  }
  setup_alsa_handle(playback_handle, SND_PCM_STREAM_PLAYBACK);
    
    if ((err = snd_pcm_start(capture_handle)) < 0) {
        fprintf(stderr, "cannot prepare audio interface for use(%s)\n",
             snd_strerror(err));
        return err;
    }

    if ((err = snd_pcm_prepare(playback_handle)) < 0) {
        fprintf(stderr, "cannot prepare audio interface for use(%s)\n",
             snd_strerror(err));
        return err;
    }

  // Start stream
  start_stream(playback_handle, capture_handle, audio_processing_callback, controls_callback, &data, write_to_file, read_from_file);

  return 0;
}

待解决问题

已尝试所有可想到的排查方向,需要明确可落地的测试步骤,定位该固定1秒延迟的产生根源。


内容的提问来源于stack exchange,提问作者pepinodelasuerte

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:06:21