使用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
相关产品推荐
相关产品推荐

