I'm working on a software to stream audio from a source over HTTP using a Flask webservice. I can get the sound frames through sounddevice
and route them to a browser through a Flask route with yield
and the right mimetype, but the raw audio format is quite cumbersome for remote streaming, and not really the best when it comes to client compatibility.
I'd love to use pydub
to convert the raw audio frames to a format like mp3 or ogg, but it's not clear to me neither from the documentation nor from the source code how to achieve an on-the-fly format conversion without dumping the output to a file through .export()
.
The skeleton of my code so far is something like:
### 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')
How would one convert the raw AudioSegment
object in audio_callback
into a compressed mp3/ogg suitable for web streaming? I know that it's possible to create a segment from an mp3 through AudioSegment.from_file
, or dump it to an mp3 file through .export()
, but that wouldn't really be an option as such I/O operations would introduce non-negligible latency. I think it might be theoretically possible to hack .export()
to get it to dump to a socket or fifo file descriptor, but that sounds a bit as a hacky workaround to me, plus I'm not sure whether it's sufficient for the file descriptor to provide the .write()
method or if it'd break because other methods (e.g. seek
) are required.