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

使用Curl下载MP3后用SDL2_mixer播放时音频被截断问题求助

问题描述

我在树莓派4上开发C/C++程序,流程是用Curl从服务器下载MP3文件,再通过SDL2_mixer驱动音频插孔连接的扬声器播放。目前遇到的核心问题是播放时音频总会被截断:不同音频的截断时长不一样,但同一个文件每次下载后播放的截断位置完全一致。

试过用system(mpg123...)命令播放,结果相同。但存在几个特殊现象:

  • PC浏览器从服务器下载的MP3是完整的
  • 通过VSCode远程SSH查看树莓派上的文件,在VSCode内播放是完整的,但无法将该文件从树莓派下载到PC本地

相关代码如下:

// temporary file to store the response
string rf = program_id + "-" + dt + ".mp3";
FILE *rfptr = fopen(rf.c_str(), "wb");

// setup curl
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, rfptr);

// perform request
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
    cerr << "Error: " << dt << " Curl failed" << endl;
    return -1;
}

// play mp3 file using sdl2-mixer
if (PlaySound(rf, dt))
{
    cerr << "Error: " << dt << " Could not play mp3 file" << endl;
    return -1;
}

// cleanup
curl_easy_cleanup(curl);
// remove(rf.c_str());
fclose(rfptr);

int PlaySound(string rf, string dt)
{
    if (SDL_Init(SDL_INIT_AUDIO) < 0)
    {
        cerr << "Error: " << dt << " Could not initialize SDL" << endl;
        return -1;
    }
    if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
    {
        cerr << "Error: " << dt << " Could not open audio" << endl;
        return -1;
    }
    Mix_Music *music = Mix_LoadMUS(rf.c_str());
    if (music == NULL)
    {
        cerr << "Error: " << dt << " Could not load music" << endl;
        return -1;
    }
    Mix_PlayMusic(music, 1);
    while (Mix_PlayingMusic())
    {
        SDL_Delay(100);
    }
    Mix_FreeMusic(music);
    Mix_CloseAudio();
    SDL_Quit();
    return 0;
}
问题排查与解决

1. 优先确保文件完全写入磁盘

你的代码中,fclose(rfptr)是在播放完成后才执行的,但Curl写入文件时,数据可能还停留在内存缓冲区,并未真正同步到磁盘。此时播放读取的是不完整的文件,必然会出现截断。

修复方案:将文件关闭操作移到播放之前,同时强制刷新缓冲区:

// perform request
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
    cerr << "Error: " << dt << " Curl failed" << endl;
    fclose(rfptr); // 出错时也要关闭文件
    return -1;
}

// 强制刷新缓冲区并关闭文件,确保数据完全写入磁盘
fflush(rfptr);
fclose(rfptr);

// 再执行播放逻辑
if (PlaySound(rf, dt))
{
    cerr << "Error: " << dt << " Could not play mp3 file" << endl;
    curl_easy_cleanup(curl);
    return -1;
}

curl_easy_cleanup(curl);
// remove(rf.c_str());

2. 检查write_data函数的正确性

代码中使用了自定义的write_data作为Curl的写入回调,但未提供实现。如果该函数未正确返回实际写入的字节数,Curl会判定写入失败,提前终止下载,导致文件不完整。

正确的write_data实现应该是:

size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
    return fwrite(ptr, size, nmemb, stream);
}

确保返回fwrite的结果,让Curl确认写入状态,完成完整的文件下载。

3. 验证文件完整性

下载完成后,用ls -l查看树莓派上MP3文件的大小,和服务器端文件大小对比(可通过curl -I <目标URL>查看Content-Length字段):

  • 如果大小不一致:优先排查Curl配置和write_data函数的问题
  • 如果大小一致:再检查SDL2_mixer的音频参数是否匹配MP3的实际参数(比如采样率、声道数)

关于无法下载文件到PC的问题

这大概率是因为原代码中播放时文件未关闭,处于被进程占用的状态,导致SFTP等传输工具无法读取完整文件。解决文件关闭的问题后,即可正常下载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 01:14:54