问题
I'm struggling to find a solution for streaming synthesized audio from a Python server. The synthesized audio is incrementally generated and returned as a np.float32
NumPy array. It then needs to be transformed from a NumPy array into an MP3 chunk. Finally, the MP3 chunk is served via flask
.
Here is some pseudo-code:
import numpy
from flask import Flask
from flask import Response
app = Flask(__name__)
sample_rate = 24000
def pcm_to_mp3():
raise NotImplementedError()
def get_synthetic_audio():
""" Mock function for synthetic audio. """
while True:
yield numpy.random.rand(1024) * 2 - 1 # Return: 32-bit Floating Point PCM
@app.route('/stream', methods=['GET'])
def get_stream():
""" Stream synthetic audio. """
def response():
for numpy_array in get_synthetic_audio():
# NOTE: The raw audio needs additional metadata to be playable like sample rate.
yield pcm_to_mp3(numpy_array, sample_rate=sample_rate)
return Response(
response(),
headers={
# NOTE: Ensure stream is not cached.
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
# NOTE: Enable streaming.
'Transfer-Encoding': 'chunked'
},
mimetype='audio/mpeg')
if __name__ == "__main__":
app.run()
While a similar setup works for WAV files, I am unable to figure out how to do something similar for MP3 files.
Thank you!
Sources
- NumPy Array to MP3 File via PyDub: How to read a MP3 audio file into a numpy array / save a numpy array to MP3?
- Stream Audio WAV File with Flask: HTTP realtime audio streaming server
- Stream Audio WAV File with PyAudio: Python: realtime audio streaming with PyAudio (or something else)?
- Stream with FFMpeg and Flask: https://gist.github.com/anthonyeden/f3b3bdf6f62badd8f87bb574283f488a
回答1:
I was able to figure out a working approach:
import select
import subprocess
import numpy
from flask import Flask
from flask import Response
app = Flask(__name__)
def get_synthetic_audio(num_samples):
audio = numpy.random.rand(num_samples).astype(numpy.float32) * 2 - 1
assert audio.max() <= 1.0
assert audio.min() >= -1.0
assert audio.dtype == numpy.float32
return audio
def response():
pipe = subprocess.Popen(
'ffmpeg -f f32le -acodec pcm_f32le -ar 24000 -ac 1 -i pipe: -f mp3 pipe:'
.split(),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
poll = select.poll()
poll.register(pipe.stdout, select.POLLIN)
while True:
pipe.stdin.write(get_synthetic_audio(24000).tobytes())
while poll.poll(0):
yield pipe.stdout.readline()
@app.route('/stream.mp3', methods=['GET'])
def stream():
return Response(
response(),
headers={
# NOTE: Ensure stream is not cached.
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
},
mimetype='audio/mpeg')
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8000, debug=True)
During my exploration, I learned that flask
does not support chunked transfer encoding. This was surprising because chunked transfer encoding was introduced in 1997 as part of HTTP 1.1.
Regardless, I was surprised to learn that ffmpeg
's stream is compatible with flask
, and that it was supported on Safari, Firefox, and Chrome.
来源:https://stackoverflow.com/questions/61351764/how-to-stream-mp3-chunks-given-a-numpy-array-in-python