Как использовать ffmpeg на сервере flask для преобразования аудиоформатов без записи файлов на диск? - PullRequest
1 голос
/ 06 января 2020

Мне успешно удалось использовать ffmpeg в python для преобразования формата некоторых аудиофайлов, например, так:

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

Однако я хочу сделать это в памяти и избежать сохранения входных и выходных файлов. на диск.

Я нашел следующий код, чтобы сделать такую ​​вещь ( Передача файла, подобного Python, как объект в ffmpeg через подпроцесс ):

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))
    # check file format
    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()  # create file-object
memfile.write(file.read())  # write in file-object
memfile.seek(0)  # move to beginning so it will read from beginning

И затем повторяю попытку:

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)

1 Ответ

1 голос
/ 07 января 2020

Согласно вашему комментарию, это, кажется, было исправлено:

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

Я не уверен на 100%, почему передача 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'>

Так что, похоже, f и memfile.read() в вашем случае относятся к одному типу.

На самом деле я не уверен, почему первый даст, AssertionError: Given audio file must be a filename string or a file-like object.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...