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

时间:2018-02-27 16:05:30

标签: python ffmpeg scipy subprocess wav

搜索了很长时间后,我仍然找不到使用scipy.io.wavfile.read()FFmpeg 3.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.

有没有办法解决这个问题?

1 个答案:

答案 0 :(得分:0)

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

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

代码的解决方法是在数据通过wavfile.read()对象传递到io.BytesIO()之前手动修补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())