从Rust中的子进程读取部分行(数据块)



当我尝试生成一个子ffmpeg进程时,我使用附加标志-progress,接下来我使用管道将此进度输出传递到stderr。所以整个命令看起来像:

ffmpeg -i ... -progress pipe:2 ...

在没有-progress标志的情况下,ffmepg在stderr中输出以下行,可能每秒一次:

frame=46 46 fps=0.0 q=0.0 size=       0kB time=00:00:01.72 bitrate=   0.2kbits/s speed=2.69x

在stderr中使用-progress标志ffmepg输出(多行(,可能每秒一次:

frame=1   1 fps=0.0 q=0.0 size=       0kB time=00:00:00.19 bitrate=   2.0kbits/s speed=2.94x    
fps=0.00
stream_0_0_q=0.0
bitrate=   2.0kbits/s
total_size=48
out_time_us=192000
out_time_ms=192000
out_time=00:00:00.192000
dup_frames=0
drop_frames=0
speed=2.94x
progress=continue

使用-progress标志的主要用途是通过解析out_time_ms行并与整个持续时间进行比较来计算完成百分比。

在NodeJS中读取这个区块(部分行(非常简单:

const { spawn } = require('child_process');
const child = spawn('ffmpeg', [..., '-progress', 'pipe:2', ...]);
child.stderr.on('data', (data) => {
// data will contain multiple lines, exactly one chunk
});

在Deno中读取这个区块(部分行(也很简单:

const child = Deno.spawnChild("ffmpeg", {
args: [..., '-progress', 'pipe:2', ...],
});
const stderrReader = child.stderr.getReader();
let readResult = await stderrReader.read();
while (readResult.done !== true) {
readResult = await stderrReader.read();
// readResult will contain multiple lines, exactly one chunk
}

我在铁锈上无法做到这一点:

let mut command = Command::new("ffmpeg");
command.args(["...", "-progress", "pipe:2", "..."]);
let mut child = command
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let child_stderr = child.stderr.as_mut().expect("Unable to pipe stderr");
let mut reader = BufReader::new(child_stderr);
let mut buff = String::new();
while reader.read_line(&mut buff).expect("Unable to read chunk") > 0 {
// buff will contain only on line
buff.clear();
}

我是Rust的新手。我无法检测出哪个字符表示块结束。

  • Runnigread_line()-将只读取一行
  • Runnigread_to_end()-将读取整个输出,直到进程结束(EOF(

如何在Rust中读取ffmpeg每秒可能输出一次的行的部分?Node/Deno如何检测到这一"问题";块的末尾";?铁锈也有这样的事件/信号吗?

您可以使用ChildStderr::read来实现与其他语言相同的行为。

警告
任何语言中,这种行为都是不可靠的。

Stderr是数据流。它不是基于数据包的。它意味着被一个字符一个字符地消耗。

您之所以得到块是出于性能原因。操作系统将多个字符组合在一起进行单个读/写操作。请注意,对哪些字符进行分组的决定是由操作系统任意选择的。

在你的情况下,看起来这些状态信息总是作为一个数据包来的,因为它产生得太快了,操作系统将其组合在一起是明智的。但是,请注意,不能保证总是这样,依赖它是危险的。

BufReader将所有这些抽象掉,并公开了常见的功能,如阅读一行文本。

建议
使用BufReader逐行处理状态信息。没有可靠的方法来检测状态更新的结束,所以将每一行处理为一条自包含的消息。(除非ffmpeg打印某种消息结束标记,如双换行符(

最新更新