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

时间:2015-04-07 13:49:05

标签: 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()重定向到wave.open(file,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文件不是解决方案。

抱歉我的英文。 感谢。

更新

感谢关于io.BytesIO的 PM 2Ring

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

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

来自/usr/lib/python2.7/wave.py:

...
self._file = Chunk(file, bigendian = 0)
if self._file.getname() != 'RIFF':
    raise Error, 'file does not start with RIFF id'
if self._file.read(4) != 'WAVE':
    raise Error, 'not a WAVE file'
...

由于'bad'self._file对象导致检查失败。

在/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。 但是这个功能正常。

按照指定here

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

这很简单。 我已经改变了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。所以我为2类Chunk和Wave_read创建了自定义分支。

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

对不起我糟糕的英语。

感谢。

2 个答案:

答案 0 :(得分:0)

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

可以做的是将您从p.stdout.read()读取的字节包装在io.BytesIO对象中。生成的对象将表现得像一个文件,它将是可搜索的。

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

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

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

答案 1 :(得分:0)

如果您使用-f s16le代替-f wav将音频文件转换为原始音频而非wav,则可能根本不需要wave.open,您可以直接转发到ALSA。我只用PyAudio尝试过它,但它确实有效。