You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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):
    HxD Python程序十六进制输出
  • Rust程序输出(HxD):
    HxD Rust程序十六进制输出

内容的提问来源于stack exchange,提问作者rust_convert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:00:51