Python与Rust终端管道差异及音频管道读取异常排查
Rust读取FFmpeg音频管道数据异常问题
我正在Tauri项目中学习Rust,将一个基于FFmpeg的Python 2 GUI程序转为Rust版本。视频转换功能正常,但音频部分无法工作。经多日调试发现,Rust中无法从终端管道正确读取音频数据——适用于视频的读取逻辑对音频无效。我尝试将音频数据读为字符串再转为字节,但字节数组为空。查阅Rust与Python的管道文档后,仍不解为何视频管道正常而音频管道数据为空或异常,且疑惑Python的stdout管道是否等价于Rust的stdin管道。
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();
十六进制输出对比
- Python程序输出(HxD):

- Rust程序输出(HxD):

内容的提问来源于stack exchange,提问作者rust_convert
相关产品推荐
相关产品推荐

