繁体   English   中英

Python和Rust终端管有什么区别?

[英]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 Python程序十六进制输出

HxD Rust 程序十六进制 output:

HxD Rust 程序十六进制输出

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM