NodeJS中用ReadStream/Buffer调用ffprobe时进程挂起问题求助
解决ffprobe进程挂起问题
问题根源
- 无效参数导致命令异常:
-ar是ffmpeg用于设置输出采样率的参数,ffprobe仅负责媒体探测,不支持该参数,会导致命令解析错误,进程无法正常执行。 - 未处理stderr缓冲区阻塞:子进程的stderr流如果不监听,当输出内容填满系统缓冲区时,进程会被阻塞挂起。
- ReadStream处理方式低效且易出问题:将ReadStream先转为Buffer再写入stdin,既浪费内存,又可能因手动调用
stdin.end()的时机不当导致ffprobe未完成读取。
修复方案
- 移除
-ar参数,ffprobe不需要设置输出采样率。 - 添加stderr流监听,避免缓冲区阻塞。
- 对于ReadStream,直接通过
pipe方式写入ffprobe的stdin,自动处理流的传输和结束事件,无需手动转Buffer和调用stdin.end()。 - 修正
close事件的参数,ffprobe的close事件回调参数为退出码和信号,而非输出内容。
修改后的probeStream代码
let probeStream = function (streamOrBuffer, params) { var self = this; return new Promise(async (resolve, reject) => { var loglevel = self.logger.isDebug() ? 'debug' : 'error'; var args = []; args = args.concat([ '-v', 'quiet', '-loglevel', loglevel, '-print_format', 'json', '-show_chapters', '-show_format', '-show_streams', '-i', 'pipe:' ]); // 移除无效的-ar参数 const opts = self._options.child_process; self.logger.debug("probeStream probe options: %s", args.join(" ")); var result = ''; const ffprobe = cp.spawn('ffprobe', args, opts) .on('message', msg => self.logger.info(msg)) .on('error', reject) .on('exit', (code, signal) => { self.logger.debug("probeStream [exit] code:%s signal:%s", code, signal); // 非0退出码直接抛出错误 if (code !== 0) { reject(new Error(`ffprobe exited with code ${code}`)); } }) .on('close', (code, signal) => { self.logger.debug("probeStream [close] code:%s signal:%s", code, signal); if (code === 0) { try { // 解析JSON格式的探测结果 resolve(JSON.parse(result)); } catch (err) { reject(new Error(`Failed to parse ffprobe output: ${err.message}`)); } } else { reject(new Error(`ffprobe closed with code ${code}`)); } }); // 收集stdout输出的探测结果 ffprobe.stdout.on('data', function (data) { result += data.toString(); }); // 处理stderr,避免缓冲区阻塞导致进程挂起 ffprobe.stderr.on('data', function (data) { self.logger.debug("ffprobe stderr: %s", data.toString()); }); ffprobe.stdin.on("error", (err) => { if (err.code !== "EPIPE") { self.logger.warn("probeStream ffprobe stdin error", err); reject(err); } }); if (streamOrBuffer instanceof Buffer) { // 处理Buffer输入 ffprobe.stdin.write(streamOrBuffer, (err) => { if (err) reject(err); ffprobe.stdin.end(); }); } else { // 处理ReadStream输入 // 直接pipe,自动处理流的传输和结束事件 streamOrBuffer.pipe(ffprobe.stdin); streamOrBuffer.on('error', reject); } }); }//probeStream
额外优化
- 移除了无用的
sample_rate参数,因为ffprobe不需要该参数。 - 添加了JSON解析的错误处理,避免无效输出导致程序崩溃。
- 对非0退出码进行了明确的错误处理,便于排查问题。
内容的提问来源于stack exchange,提问作者loretoparisi
相关产品推荐
相关产品推荐

