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

UDP传输WAV文件在Unix ALSA-lib下播放速度异常的排查请求

音频播放速度异常的问题分析与修复方案

嘿,我看了你的代码和问题描述,播放速度翻倍或者突然加速的问题根源其实在客户端的播放逻辑混乱,还有几个细节没处理好,咱们一步步来解决:

核心问题拆解

  1. UDP套接字读取方式错误
    你在客户端的播放循环里用了read(sockfd, buffer2, size)来读取数据,但UDP是数据报协议,应该用recvfrom来读取完整的数据包。read是给TCP这种字节流协议用的,用在UDP上会导致数据读取混乱——比如一次性读取多个数据包的内容,ALSA拿到超出预期的数据量,自然会加速播放。

  2. 嵌套循环逻辑完全脱节
    外层while(1)刚接收一个UDP包,就直接进入while(loops > 0)的播放循环,这个循环里反复读取套接字(还读错了方式),直到预先计算的loops值耗尽。但loops是根据25秒固定值算出来的,和实际从服务器接收的音频数据完全无关,这就导致播放节奏和实际数据脱节,要么提前跑完循环重复播放,要么数据堆积导致加速。

  3. WAV文件头未处理
    服务器直接把整个WAV文件(包括前面44字节的文件头)发出去了,而客户端把这些非PCM的头数据当成音频播放,不仅会有开头杂音,还可能干扰ALSA的播放节奏(虽然这不是速度快的主因,但必须修复)。

具体修复步骤

1. 服务器端:跳过WAV文件头再发送

WAV文件的前44字节是描述信息(采样率、位深等),客户端已经硬编码了这些参数,不需要发送。在发送循环前先跳过这部分:

// 跳过WAV文件头(固定44字节)
char wav_header[44];
read(0, wav_header, 44);

printf("Sending audio in 3, 2, 1.....\n");
while(1){
    int bytes_read = read(0, (void *)payload, PAYLOAD_SIZE);
    fprintf(stderr, "r");
    fflush(stderr);
    if(bytes_read < 1) break;
    // 去掉不必要的stdout写入,专注发送数据
    sendto(sockfd, (void *)payload, bytes_read, 0, (struct sockaddr *) &cliaddr, len);
    fprintf(stderr, "s");
    fflush(stderr);
}

2. 客户端:彻底重构播放逻辑

把错误的嵌套循环删掉,改成接收一个UDP包,就播放一个包的PCM数据,完全根据实际接收的数据驱动播放,去掉固定的loops限制:

// 保留ALSA初始化的所有代码(除了loops相关的计算)
printf("Waiting for audio!!!\n");

int len = sizeof(servaddr);
char *payload = (char *)malloc(PAYLOAD_SIZE*sizeof(char));

while(1){
    int bytes_read = recvfrom(sockfd, (void *)payload, PAYLOAD_SIZE, MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
    if(bytes_read <= 0) {
        fprintf(stderr, "\nAudio stream ended or error occurred\n");
        break;
    }
    fprintf(stderr,"r");
    fflush(stderr);

    // 直接将接收到的PCM数据写入ALSA播放
    int rc = snd_pcm_writei(handle, payload, frames); // frames=32,刚好对应128字节(32帧×2通道×2字节/采样)
    if (rc == -EPIPE) {
        fprintf(stderr, "\nunderrun occurred, resetting playback\n");
        snd_pcm_prepare(handle);
    } else if (rc < 0) {
        fprintf(stderr, "\nerror from writei: %s\n", snd_strerror(rc));
    } else if (rc != (int)frames) {
        fprintf(stderr, "\nshort write, only wrote %d frames\n", rc);
    }
}

// 播放结束后的清理工作
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(payload);
close(sockfd);
return 0;

额外优化建议

  • UDP可靠性问题:UDP本身是不可靠的,丢包、乱序都会导致播放卡顿。如果需要稳定的音频传输,可以考虑在UDP上实现简单的重传、排序机制,或者改用TCP(但TCP的延迟会更高)。
  • ALSA缓冲区调整:你当前设置的frames=32比较小,容易出现underrun(欠载),可以尝试调到64或128,提升播放稳定性。
  • 数据对齐检查:确保PAYLOAD_SIZE是4的整数倍(16位立体声每帧4字节),你当前的128是符合要求的,这点很好。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 19:17:48