Я работаю над программным обеспечением для потоковой передачи звука из источника через HTTP с использованием веб-сервиса Flask. Я могу получить звуковые кадры через sounddevice
и направить их в браузер через маршрут Flask с yield
и правильным MIME-типом, но необработанный аудиоформат довольно громоздкий для удаленной потоковой передачи и не самый лучший, когда речь идет о совместимости с клиентом.
Я бы хотел использовать pydub
для преобразования необработанных аудиокадров в такой формат, как mp3 или ogg, но мне это непонятно ни из документацию или из исходный код как добиться преобразования формата "на лету" без сброса вывода в файл через .export()
.
Скелет моего кода пока выглядит примерно так:
### audio.py
import queue
import sounddevice as sd
from pydub.audio_segment import AudioSegment
def input_stream(device, sample_width=2, sample_rate=44100,
channels=1, latency=0, blocksize=2048, timeout=5.0):
audio_queue = queue.Queue()
def audio_callback(indata, frames, time_duration, status):
audio = AudioSegment(indata, sample_width=sample_width,
channels=channels, frame_rate=sample_rate)
# Some pydub magic should happen here to convert the raw frame to mp3/ogg
audio_queue.put(audio.raw_data)
with sd.InputStream(samplerate=sample_rate, device=device,
channels=channels, callback=audio_callback,
latency=latency, blocksize=blocksize):
while not recording_terminated():
yield audio_queue.get(block=True, timeout=timeout)
### web.py
from flask import route, request, Response
from audio import input_stream
@route('/sound/stream', methods=['GET'])
def get_sound_feed():
device = request.args.get('device')
return Response(input_stream(device), mimetype='audio/ogg')
Как преобразовать необработанный объект AudioSegment
в audio_callback
в сжатый mp3/ogg, подходящий для потоковой передачи в Интернете? Я знаю, что можно создать сегмент из mp3 через AudioSegment.from_file
или выгрузить его в mp3-файл через .export()
, но на самом деле это не вариант, поскольку такие операции ввода-вывода приведут к существенной задержке. Я думаю, что может быть теоретически возможно взломать .export()
, чтобы заставить его выгружаться в сокет или файловый дескриптор fifo, но мне это кажется немного хакерским обходным путем, плюс я не уверен, что это достаточно, чтобы файловый дескриптор предоставил метод .write()
, иначе он сломается из-за того, что требуются другие методы (например, seek
).