FFmpeg随机出现av_interleaved_write_frame(): Broken pipe错误的解决咨询
我用FFmpeg将文件或流中的音频数据转换为PCM,再转成特殊OGG/OPUS格式推流至专用设备,转码过程在独立线程中运行。有时转换正常,但多数情况下会在转码刚启动时失败。已尝试修改缓冲区大小,但无任何改善。请问如何避免Broken pipe错误并提升转码稳定性?
报错信息
av_interleaved_write_frame(): Broken pipe
Error writing trailer of pipe:: Broken pipe
Stopped ffmpeg with error code=256...
相关代码
FILE *ffmpeg_decode_audio_start_skip(const char *input_source, size_t skip_seconds) { char ffmpeg_command[1024]; snprintf(ffmpeg_command, sizeof(ffmpeg_command), "ffmpeg -i \"%s\" -f s16le -acodec pcm_s16le -ar 48000 -ac 2 -ss %" PRIuSIZE " -", input_source, skip_seconds); FILE *ffmpeg_pipe = NULL; ffmpeg_pipe = osPopen(ffmpeg_command, "r"); if (ffmpeg_pipe == NULL) { return ffmpeg_pipe; } return ffmpeg_pipe; } error_t ffmpeg_stream(char *source, char *target_taf, size_t skip_seconds, bool_t *active) { FILE *ffmpeg_pipe = NULL; error_t error = NO_ERROR; ffmpeg_pipe = ffmpeg_decode_audio_start_skip(source, skip_seconds); if (ffmpeg_pipe == NULL) { return -1; } //toniefile_t *taf = [...] int16_t sample_buffer[2 * 4096]; size_t samples = sizeof(sample_buffer) / sizeof(uint16_t); size_t blocks_read = 0; *active = true; while (*active) { error = ffmpeg_decode_audio(ffmpeg_pipe, sample_buffer, samples, &blocks_read); if (error != NO_ERROR && error != ERROR_END_OF_STREAM) { break; } else if (error == ERROR_END_OF_STREAM) { error = NO_ERROR; break; } //toniefile_encode(taf, sample_buffer, blocks_read / 2); } ffmpeg_decode_audio_end(ffmpeg_pipe, error); //toniefile_close(taf); return error; } error_t ffmpeg_decode_audio(FILE *ffmpeg_pipe, int16_t *buffer, size_t size, size_t *blocks_read) { if (ffmpeg_pipe == NULL) return ERROR_ABORTED; *blocks_read = 0; size_t chunk_size = 4096; while (*blocks_read < size) { size_t remaining_samples = size - *blocks_read; size_t samples_to_read = (remaining_samples < chunk_size) ? remaining_samples : chunk_size; size_t read = fread(&buffer[*blocks_read], sizeof(int16_t), samples_to_read, ffmpeg_pipe); if (read == 0) { if (*blocks_read > 0) { return NO_ERROR; } else { return ERROR_END_OF_STREAM; // End of audio data } } *blocks_read += read; } return NO_ERROR; }
问题分析与解决方案
1. 核心问题根源
Broken pipe错误本质是FFmpeg进程向管道写入数据时,读取端(你的应用)已经提前关闭,或者读取速度跟不上写入速度,导致管道被系统中断。结合你的代码,主要有几个触发点:FFmpeg输出干扰、管道读取逻辑僵化、进程退出时资源清理不彻底、推流端异常未同步。
2. 针对性修复
(1)优化FFmpeg命令参数
给FFmpeg添加IO优化和输出控制参数,避免日志混入PCM数据,同时提升管道写入稳定性:
snprintf(ffmpeg_command, sizeof(ffmpeg_command), "ffmpeg -hide_banner -loglevel error -i \"%s\" -f s16le -acodec pcm_s16le -ar 48000 -ac 2 -ss %" PRIuSIZE " -avioflags direct -", input_source, skip_seconds);
-hide_banner -loglevel error:关闭冗余日志输出,防止非PCM数据混入管道,干扰读取逻辑-avioflags direct:优化IO路径,减少缓冲延迟,降低管道阻塞概率
(2)修改管道读取逻辑,避免强制读满
当前ffmpeg_decode_audio强制读满缓冲区才返回,若FFmpeg输出速度慢或推流端处理不及时,会导致FFmpeg写管道阻塞。改成允许读取部分数据就返回:
error_t ffmpeg_decode_audio(FILE *ffmpeg_pipe, int16_t *buffer, size_t size, size_t *blocks_read) { if (ffmpeg_pipe == NULL) return ERROR_ABORTED; *blocks_read = 0; size_t chunk_size = 4096; // 单次读取,不强制填满缓冲区 size_t read = fread(buffer, sizeof(int16_t), min(size, chunk_size), ffmpeg_pipe); if (read == 0) { if (feof(ffmpeg_pipe)) { return ERROR_END_OF_STREAM; } else { return ERROR_ABORTED; // 管道异常 } } *blocks_read = read; return NO_ERROR; }
(3)完善进程退出时的资源清理
用pclose替代fclose关闭管道,确保等待FFmpeg进程正常退出,避免僵尸进程,同时让FFmpeg能正确写入trailer:
void ffmpeg_decode_audio_end(FILE *ffmpeg_pipe, error_t error) { if (ffmpeg_pipe) { // pclose会等待子进程结束并回收资源 pclose(ffmpeg_pipe); } }
(4)同步推流端错误状态
如果推流到专用设备时出现连接失败或异常,toniefile_encode需要及时返回错误,你要在主循环中检测到错误后立即设置*active = false,让读取端停止读取,避免FFmpeg持续写无效管道:
// 在ffmpeg_stream的循环中补充: error = toniefile_encode(taf, sample_buffer, blocks_read / 2); if (error != NO_ERROR) { *active = false; break; }
(5)添加FFmpeg进程状态检查
在读取管道的循环中,定期检查FFmpeg子进程是否意外终止(比如用waitpid配合WNOHANG),如果进程已退出,直接终止读取流程:
// 示例:在ffmpeg_decode_audio中添加进程状态检查(需根据平台适配) #include <sys/wait.h> pid_t ffmpeg_pid = ...; // 保存osPopen返回的子进程ID int status; if (waitpid(ffmpeg_pid, &status, WNOHANG) != 0) { return ERROR_ABORTED; }
3. 调试建议
- 临时将FFmpeg日志级别改为
-loglevel debug并输出到文件,查看启动阶段的详细错误,排查输入源是否正常、参数是否合法 - 先用本地音频文件测试,排除流源不稳定的影响
内容的提问来源于stack exchange,提问作者Schr0nk

