使用ffmpeg在Flask服务器中转换音频格式,而无需将文件写入磁盘。

huangapple go评论147阅读模式
英文:

How to use ffmpeg in flask server to convert between audio formats without writing files to disk?

问题

我已成功使用Python中的ffmpeg来转换一些音频文件的格式,如下所示:

command = "ffmpeg -i audio.wav -vn -acodec pcm_s16le output.wav"
subprocess.call(command, shell=True)

但是我希望在内存中执行此操作,避免将输入和输出文件保存到磁盘上。

我已找到以下代码来执行此操作(https://stackoverflow.com/questions/11301863/passing-pythons-file-like-object-to-ffmpeg-via-subprocess):

command = ['ffmpeg', '-y', '-i', '-', '-f', 'wav', '-']
process = subprocess.Popen(command, stdin=subprocess.PIPE)
wav, errordata = process.communicate(file)

但我在我的情况下遇到了困难。

我在服务器上作为multipart/form-data请求的一部分接收文件。

@server.route("/api/getText", methods=["POST"])
def api():
    if "multipart/form-data" not in request.content_type:
        return Response("invalid content type: {}".format(request.content_type))
    # 检查文件格式
    file = request.files['file']
    if file:
        print('**found file', file.filename)

现在我有一个FileStorage对象(https://tedboy.github.io/flask/generated/generated/werkzeug.FileStorage.html)。这个对象有一个流,可以使用read方法访问。因此,我认为我可以将其用作ffmpeg的输入,如下所示:

f = file.read()
command = ['ffmpeg', '-y', '-i', '-', '-f', 'wav', '-']
process = subprocess.Popen(command, stdin=subprocess.PIPE)
wav, errordata = process.communicate(f)

然而,这会产生以下错误:

AssertionError: Given audio file must be a filename string or a file-like object

我还尝试了另一种方法,我在网上找到的,使用io.BytesIO,但我无法再找到来源:

memfile = io.BytesIO()  # 创建文件对象
memfile.write(file.read())  # 在文件对象中写入数据
memfile.seek(0)  # 移动到开头以便从开头读取

然后再次尝试:

command = ['ffmpeg', '-y', '-i', '-', '-f', 'wav', '-']
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
wav, errordata = process.communicate(memfile)

这给我带来了以下错误:

TypeError: a bytes-like object is required, not '_io.BytesIO'

有人有想法如何做到这一点吗?

更新

第一个错误消息实际上不是由ffmpeg抛出的错误消息。正如v25在他的答案中正确指出的那样,第一个方法也返回一个字节对象,也是一个有效的解决方案。

错误消息是由一个库(speech_recognition)在尝试处理修改后的文件时抛出的。如果有人在同样的问题上遇到了这个问题,这里是解决方法:

ffmpeg返回的字节对象(变量wav)必须转换为类似文件的对象,正如错误消息所暗示的那样。可以像这样轻松地完成:

memfileOutput = io.BytesIO(wav)
英文:

I have successfully managed to use ffmpeg in python to convert the format of some audio files like this:

command = "ffmpeg -i audio.wav -vn -acodec pcm_s16le output.wav"
subprocess.call(command, shell=True)

However I want to do this in memory and avoid saving the input and output files to disk.

I have found the follwing code to do such a thing (https://stackoverflow.com/questions/11301863/passing-pythons-file-like-object-to-ffmpeg-via-subprocess):

command = ['ffmpeg', '-y', '-i', '-', '-f', 'wav', '-']
process = subprocess.Popen(command, stdin=subprocess.PIPE)
wav, errordata = process.communicate(file)

But I am struggeling to use this in my context.

I am receiving the file on a server as part of a multipart/form-data request.

@server.route("/api/getText", methods=["POST"])
def api():
    if "multipart/form-data" not in request.content_type:
        return Response("invalid content type: {}".format(request.content_type))
    # check file format
    file = request.files['file']
    if file:
        print('**found file', file.filename)

Now I have the file as a FileStorage Object (https://tedboy.github.io/flask/generated/generated/werkzeug.FileStorage.html). This object has a stream, which can be accessed by using the read method. So I thought I might be able to use this as input for ffmpeg like so:

f = file.read()
command = ['ffmpeg', '-y', '-i', '-', '-f', 'wav', '-']
process = subprocess.Popen(command, stdin=subprocess.PIPE)
wav, errordata = process.communicate(f)

However this yields the the following error:

AssertionError: Given audio file must be a filename string or a file-like object

I have also tried another approach which I found online, using io.BytesIO, to which I can't find the source anymore:

memfile = io.BytesIO()  # create file-object
memfile.write(file.read())  # write in file-object
memfile.seek(0)  # move to beginning so it will read from beginning

And then trying this again:

command = ['ffmpeg', '-y', '-i', '-', '-f', 'wav', '-']
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
wav, errordata = process.communicate(memfile)

This gets me the following error:

TypeError: a bytes-like object is required, not '_io.BytesIO'

Does anyone have an idea of how to do this?

Update

The first error message is actually not a error message thrown by ffmpeg. As v25 pointed out correctly in his answer the first approach also returns a bytes object and is also a valid solution.

The error message got thrown by a library (speech_recognition) when trying to work with the modified file. In the unlikely case of someone coming across the same problem here the solution:

The bytes objected returned by ffmpeg (variable wav) has to be turned into a file-like object as the error message implies. This can easily be done like this:

memfileOutput = io.BytesIO(wav)

答案1

得分: 2

根据您的评论,这似乎已经得到修复:

wav, errordata = process.communicate(memfile.read())

我不完全确定为什么在这里传递 f 不起作用,因为:

import io

print('file.read()', type(file.read()))

memfile = io.BytesIO()
memfile.write(file.read())
memfile.seek(0)

print('memfile', type(memfile))

print('memfile.read()', type(memfile.read()))

输出如下:

file.read() <class 'bytes'> # (f)
memfile <class '_io.BytesIO'>
memfile.read() <class 'bytes'>

所以在您的情况下,似乎 fmemfile.read() 的类型是相同的。

我实际上不确定为什么前者会导致 AssertionError: 给定的音频文件必须是文件名字符串或类似文件的对象

英文:

As per your comment, this appears to have been fixed by:

wav, errordata = process.communicate(memfile.read())

I'm not 100% sure on why passing f here wouldn't work, because:

import io

print (&#39;file.read()&#39;, type(file.read()))

memfile = io.BytesIO() 
memfile.write(file.read())  
memfile.seek(0) 

print (&#39;memfile&#39;, type(memfile))

print (&#39;memfile.read()&#39;, type(memfile.read()))

gives...

file.read() &lt;class &#39;bytes&#39;&gt; # (f)
memfile &lt;class &#39;_io.BytesIO&#39;&gt;
memfile.read() &lt;class &#39;bytes&#39;&gt;

So it appears f and memfile.read() in your case are of the same type.

I'm not actually sure why the former would yield, AssertionError: Given audio file must be a filename string or a file-like object.

huangapple
  • 本文由 发表于 2020年1月7日 02:11:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/59616923.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定