Python:mp3通过ffmpeg管道和wave.open(f,';r';)到alsaaudio

Python:mp3通过ffmpeg管道和wave.open(f,';r';)到alsaaudio,python,audio,ffmpeg,Python,Audio,Ffmpeg,我正在尝试使用ffmpeg将mp3解码为wav: import alsaaudio import wave from subprocess import Popen, PIPE with open('filename.mp3', 'rb') as infile: p=Popen(['ffmpeg', '-i', '-', '-f', 'wav', '-'], stdin=infile, stdout=PIPE) ... 接下来,我希望将数据从p.stdout.read()重定

我正在尝试使用ffmpeg将mp3解码为wav:

import alsaaudio
import wave
from subprocess import Popen, PIPE

with open('filename.mp3', 'rb') as infile:
    p=Popen(['ffmpeg', '-i', '-', '-f', 'wav', '-'], stdin=infile, stdout=PIPE)
    ...
接下来,我希望将数据从p.stdout.read()重定向到wave.open(文件,r),以使用readframes(n)和其他方法。但我不能,因为wave.open(file,'r')中的“file”只能是文件名或打开的文件指针

    ...
    file = wave.open(p.stdout.read(),'r')
    card='default'
    device=alsaaudio.PCM(card=card)
    device.setchannels(file.getnchannels())
    device.setrate(file.getframerate())
    device.setformat(alsaaudio.PCM_FORMAT_S16_LE)
    device.setsetperiodsize(320)
    data = file.readframes(320)
    while data:
        device.write(data)
        data = file.readframes(320)
我得到:

TypeError: file() argument 1 must be encoded string without NULL bytes, not str
那么,是否可以通过wave.open()处理p.stdout.read()中的数据呢? 制作临时的.wav文件不是解决方案

对不起我的英语。 谢谢

更新

多亏了pm2ring对io.BytesIO的热播

但是,生成的代码不起作用

import alsaaudio
import wave
from subprocess import Popen, PIPE

with open('sometrack.mp3', 'rb') as infile:
        p=Popen(['ffmpeg', '-i', '-', '-f','wav', '-'], stdin=infile , stdout=PIPE , stderr=PIPE)
        fobj = io.BytesIO(p.stdout.read())
fwave = wave.open(fobj, 'rb')
跟踪:

File "./script.py", line x, in <module>
  fwave = wave.open(fobj, 'rb')
File "/usr/lib/python2.7/wave.py", line x, in open
  return Wave_read(f)
File "/usr/lib/python2.7/wave.py", line x, in __init__
  self.initfp(f)
File "/usr/lib/python2.7/wave.py", line x, in initfp
  raise Error, 'not a WAVE file'
wave.Error: not a WAVE file
由于“错误”的self.\u文件对象,检查失败

在/usr/lib/python2.7/chunk.py中,我发现了问题的根源:

...
try:
    self.chunksize = struct.unpack(strflag+'L', file.read(4))[0]
except struct.error:
    raise EOFError
...
因为struct.unpack(strflag+'L',file.read(4))[0]返回0。 但是这个函数是正确的

按照规定:

“5-8字节-文件大小(整数) 整个文件的大小—8个字节,以字节(32位整数)为单位。通常,您会在创建后填写此文件。“ 这就是为什么我的脚本不起作用。wave.open和其他函数无法处理我的文件对象,因为self.chunksize=0。看起来ffmpeg在使用管道时无法插入文件大小

解决方案

很简单。 我更改了Chunk类的init函数:

之后:

...
try:
    self.chunksize = struct.unpack(strflag+'L', file.read(4))[0]
except struct.error:
    raise EOFError
...
之前:

...
try:
    self.chunksize = struct.unpack(strflag+'L', file.read(4))[0]
    currtell = file.tell()
    if self.chunksize == 0:
        file.seek(0)
        file.read(currtell)
        self.chunksize = len(file.read())-4
        file.seek(0)
        file.read(currtell)
except struct.error:
    raise EOFError
...
当然,编辑原始模块是不好的idia。因此,我为Chunk和Wave_read两个类创建了自定义分叉

可以找到工作但不稳定的完整代码

对不起,我的英语糟透了


谢谢。

正如您所提到的,wave.open(file,'r')的
文件参数只能是文件名或打开的文件指针。但是
p.stdout.read()
是作为字符串的文件
p.stdout
的内容。但是,您不能只将
p.stdout
文件传递给
wave.open()
,因为
wave.open()
需要一个可查找的类似文件的对象,并且通常不能在管道上
seek()

但您可以做的是将从
p.stdout.read()
读取的字节包装到对象中。结果对象的行为将类似于文件,并且它是可查找的

FWIW,使用
file
作为变量名不是一个好主意,因为它是Python 2中内置类型的名称;我想使用Python 3是可以的,但我还是会避免使用它

警告:未经测试的Python 2.6+代码

fbytes = io.BytesIO(p.stdout.read())
fwave = wave.open(fbytes, 'r')

如果使用
-f s16le
而不是
-f wav
将音频文件转换为原始音频而不是wav,则可能不需要
wave。完全打开
,就可以直接流式传输到ALSA。我只在PyAudio上试用过,效果很好。

很有趣!但是你真的应该把你找到的解决方案作为一个答案,而不是对你的问题的修改。
fbytes = io.BytesIO(p.stdout.read())
fwave = wave.open(fbytes, 'r')