[英]How do terminal pipes in Python differ from those in Rust?
为了学习 Rust(在 Tauri 项目中),我正在转换一个 Python 2 程序,该程序使用 ffmpeg 从 GUI 创建自定义视频格式。 视频部分转换成功,但我无法让音频正常工作。 通过我过去几天所做的调试,看起来我无法从终端 pipe 正确读取 Rust 中的音频数据 - 正在读取视频数据的内容不适用于音频。 我尝试将音频数据作为字符串读取,然后将其转换为字节,但随后字节数组显示为空。 我一直在研究 rust 文档和 python 文档中的数据“管道”,我不确定 Rust pipe 如果它对视频有效,它是如何为空或不正确的。
从这篇python 文章和这篇 rust 堆栈溢出交换,看起来 python stdout pipe 等同于 rust stdin pipe?
python 视频和音频转换的代码片段:
output=open(self.outputFile, 'wb')
devnull = open(os.devnull, 'wb')
vidcommand = [ FFMPEG_BIN,
'-i', self.inputFile,
'-f', 'image2pipe',
'-r', '%d' % (self.outputFrameRate),
'-vf', scaleCommand,
'-vcodec', 'rawvideo',
'-pix_fmt', 'bgr565be',
'-f', 'rawvideo', '-']
vidPipe = '';
if os.name=='nt' :
startupinfo = sp.STARTUPINFO()
startupinfo.dwFlags |= sp.STARTF_USESHOWWINDOW
vidPipe=sp.Popen(vidcommand, stdin = sp.PIPE, stdout = sp.PIPE, stderr = devnull, bufsize=self.inputVidFrameBytes*10, startupinfo=startupinfo)
else:
vidPipe=sp.Popen(vidcommand, stdin = sp.PIPE, stdout = sp.PIPE, stderr = devnull, bufsize=self.inputVidFrameBytes*10)
vidFrame = vidPipe.stdout.read(self.inputVidFrameBytes)
audioCommand = [ FFMPEG_BIN,
'-i', self.inputFile,
'-f', 's16le',
'-acodec', 'pcm_s16le',
'-ar', '%d' % (self.outputAudioSampleRate),
'-ac', '1',
'-']
audioPipe=''
if (self.audioEnable.get() == 1):
if os.name=='nt' :
startupinfo = sp.STARTUPINFO()
startupinfo.dwFlags |= sp.STARTF_USESHOWWINDOW
audioPipe = sp.Popen(audioCommand, stdin = sp.PIPE, stdout=sp.PIPE, stderr = devnull, bufsize=self.audioFrameBytes*10, startupinfo=startupinfo)
else:
audioPipe = sp.Popen(audioCommand, stdin = sp.PIPE, stdout=sp.PIPE, stderr = devnull, bufsize=self.audioFrameBytes*10)
audioFrame = audioPipe.stdout.read(self.audioFrameBytes)
currentFrame=0;
while len(vidFrame)==self.inputVidFrameBytes:
currentFrame+=1
if(currentFrame%30==0):
self.progressBarVar.set(100.0*(currentFrame*1.0)/self.totalFrames)
if (self.videoBitDepth.get() == 16):
output.write(vidFrame)
else:
b16VidFrame=bytearray(vidFrame)
b8VidFrame=[]
for p in range(self.outputVidFrameBytes):
b8VidFrame.append(((b16VidFrame[(p*2)+0]>>0)&0xE0)|((b16VidFrame[(p*2)+0]<<2)&0x1C)|((b16VidFrame[(p*2)+1]>>3)&0x03))
output.write(bytearray(b8VidFrame))
vidFrame = vidPipe.stdout.read(self.inputVidFrameBytes) # Read where vidframe is to match up with audio frame and output?
if (self.audioEnable.get() == 1):
if len(audioFrame)==self.audioFrameBytes:
audioData=bytearray(audioFrame)
for j in range(int(round(self.audioFrameBytes/2))):
sample = ((audioData[(j*2)+1]<<8) | audioData[j*2]) + 0x8000
sample = (sample>>(16-self.outputAudioSampleBitDepth)) & (0x0000FFFF>>(16-self.outputAudioSampleBitDepth))
audioData[j*2] = sample & 0xFF
audioData[(j*2)+1] = sample>>8
output.write(audioData)
audioFrame = audioPipe.stdout.read(self.audioFrameBytes)
else:
emptySamples=[]
for samples in range(int(round(self.audioFrameBytes/2))):
emptySamples.append(0x00)
emptySamples.append(0x00)
output.write(bytearray(emptySamples))
self.progressBarVar.set(100.0)
vidPipe.terminate()
vidPipe.stdout.close()
vidPipe.wait()
if (self.audioEnable.get() == 1):
audioPipe.terminate()
audioPipe.stdout.close()
audioPipe.wait()
output.close()
应该实现相同目标的 Rust 片段:
let output_file = OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(&output_path)
.unwrap();
let mut writer = BufWriter::with_capacity(
options.video_frame_bytes.max(options.audio_frame_bytes),
output_file,
);
let ffmpeg_path = sidecar_path("ffmpeg");
#[cfg(debug_assertions)]
let timer = Instant::now();
let mut video_cmd = Command::new(&ffmpeg_path);
#[rustfmt::skip]
video_cmd.args([
"-i", options.path,
"-f", "image2pipe",
"-r", options.frame_rate,
"-vf", options.scale,
"-vcodec", "rawvideo",
"-pix_fmt", "bgr565be",
"-f", "rawvideo",
"-",
])
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null());
// windows creation flag CREATE_NO_WINDOW: stops the process from creating a CMD window
// https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
#[cfg(windows)]
video_cmd.creation_flags(0x08000000);
let mut video_child = video_cmd.spawn().unwrap();
let mut video_stdout = video_child.stdout.take().unwrap();
let mut video_frame = vec![0; options.video_frame_bytes];
let mut audio_cmd = Command::new(&ffmpeg_path);
#[rustfmt::skip]
audio_cmd.args([
"-i", options.path,
"-f", "s16le",
"-acodec", "pcm_s16le",
"-ar", options.sample_rate,
"-ac", "1",
"-",
])
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null());
#[cfg(windows)]
audio_cmd.creation_flags(0x08000000);
let mut audio_child = audio_cmd.spawn().unwrap();
let mut audio_stdout = audio_child.stdout.take().unwrap();
let mut audio_frame = vec![0; options.audio_frame_bytes];
while video_stdout.read_exact(&mut video_frame).is_ok() {
writer.write_all(&video_frame).unwrap();
if audio_stdout.read_to_end(&mut audio_frame).is_ok() {
if audio_frame.len() == options.audio_frame_bytes {
for i in 0..options.audio_frame_bytes / 2 {
let temp_sample = ((u32::from(audio_frame[(i * 2) + 1]) << 8)
| u32::from(audio_frame[i * 2]))
+ 0x8000;
let sample = (temp_sample >> (16 - 10)) & (0x0000FFFF >> (16 - 10));
audio_frame[i * 2] = (sample & 0xFF) as u8;
audio_frame[(i * 2) + 1] = (sample >> 8) as u8;
}
} else {
audio_frame.fill(0x00);
}
}
writer.write_all(&audio_frame).unwrap();
}
video_child.wait().unwrap();
audio_child.wait().unwrap();
#[cfg(debug_assertions)]
{
let elapsed = timer.elapsed();
dbg!(elapsed);
}
writer.flush().unwrap();
我已经使用 HxD 查看了文件的十六进制数据 - 无论我如何更改 Rust 程序,我都无法获得与所附图像中预览的数据不同的数据 - 因此音频 pipe 的接口不正确。 我附上了一张十六进制数据的屏幕截图,该屏幕截图来自可正确转换视频和音频的工作 python 程序。
HxD Python 程序十六进制 output:
HxD Rust 程序十六进制 output:
vec;[0. options.audio_frame_bytes]
vec;[0. options.audio_frame_bytes]
不会分配容量为options.audio_frame_bytes
的空缓冲区,它会创建一个充满该大小的零的缓冲区。 要创建一个没有数据但有大量底层分配的 vec,请使用Vec::with_capacity
。
此外, read_to_end
不会覆盖传递给它的缓冲区,它会添加到末尾。
为了一个简单的例子,我们假设音频帧大小为 3。 这就是您的代码的功能:
buffer = vec![0; 3];
read_to_end(&mut buffer);
println!("{buffer:?}");
// Prints out [0, 0, 0, 90, 98, 10]
if buffer.len() == 3 {
// Does not enter, buffer.len is 2 * options.audio_frame_bytes
} else {
buffer.fill(0);
println!("{buffer:?}");
// prints out [0, 0, 0, 0, 0, 0]
}
如果你用Vec::with_capacity
替换你的 vec 声明它应该工作
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.