scipy.io.wavfile.read() 来自 FFmpeg 的标准输出

scipy.io.wavfile.read() the stdout from FFmpeg

找了半天,还是没找到用scipy.io.wavfile.read()读取FFmpeg3.3.6的stdout字节的解决方法。

这是完美运行的示例代码。但是,它需要将转换后的文件保存到磁盘。

import subprocess
import scipy.io.wavfile as wavfile

command = 'ffmpeg -i in.mp3 out.wav'
subprocess.run(command)

with open('out.wav', 'rb') as wf:
    rate, signal = wavfile.read(wf)

print(rate, signal)

这是我尝试从 stdout 获取 FFmpeg 输出并将其加载到 scipy wavfile 中的代码。

import io
import subprocess
import scipy.io.wavfile as wavfile

command = 'ffmpeg -i in.mp3 -f wav -'
proc = subprocess.run(command, stdout=subprocess.PIPE)

rate, signal = wavfile.read(io.BytesIO(proc.stdout))

print(rate, signal)

可悲的是,它引发了ValueError

Traceback (most recent call last):
  File ".\err.py", line 8, in <module>
    rate, signal = wavfile.read(io.BytesIO(proc.stdout))
  File "C:\Users\Sean Wu\AppData\Local\Programs\Python\Python36\lib\site-
packages\scipy\io\wavfile.py", line 246, in read
    raise ValueError("Unexpected end of file.")
ValueError: Unexpected end of file.

有什么方法可以解决这个问题吗?

显然当ffmpeg的输出发送到stdout时,程序没有填写文件头的RIFF块大小。相反,块大小应该在的四个字节都是 0xFF。 scipy.io.wavfile.read() 期望该值是正确的,因此它认为块的长度是 0xFFFFFFFF 字节。

当您给 ffmpeg 一个要写入的输出文件时,它会正确填充 RIFF 块大小,因此 wavfile.read() 能够在这种情况下读取文件。

您的代码的 work-around 是在数据通过 io.BytesIO() 对象传递到 wavfile.read() 之前手动修补 RIFF 块大小。这是对您的脚本的修改。注意:我必须使用 command.split() 作为 subprocess.run() 的第一个参数。我在 Mac OS X 上使用 Python 3.5.2。此外,我的测试文件名为 "mpthreetest.mp3".

import io
import subprocess
import scipy.io.wavfile as wavfile

command = 'ffmpeg -i mpthreetest.mp3 -f wav -'
proc = subprocess.run(command.split(), stdout=subprocess.PIPE)

riff_chunk_size = len(proc.stdout) - 8
# Break up the chunk size into four bytes, held in b.
q = riff_chunk_size
b = []
for i in range(4):
    q, r = divmod(q, 256)
    b.append(r)

# Replace bytes 4:8 in proc.stdout with the actual size of the RIFF chunk.
riff = proc.stdout[:4] + bytes(b) + proc.stdout[8:]

rate, signal = wavfile.read(io.BytesIO(riff))

print("rate:", rate)
print("len(signal):", len(signal))
print("signal min and max:", signal.min(), signal.max())