【问题标题】:How to stream MP3 chunks given a NumPy array in Python?如何在 Python 中给定 NumPy 数组流式传输 MP3 块?
【发布时间】:2020-08-04 16:26:44
【问题描述】:

我正在努力寻找从 Python 服务器流式传输合成音频的解决方案。合成的音频是增量生成的,并以np.float32 NumPy 数组的形式返回。然后需要将其从 NumPy 数组转换为 MP3 块。最后,通过flask 提供 MP3 块。

这是一些伪代码:

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()

虽然类似的设置适用于 WAV 文件,但我无法弄清楚如何对 MP3 文件执行类似的设置。

谢谢!


来源

【问题讨论】:

    标签: python numpy flask audio mp3


    【解决方案1】:

    我想出了一个可行的方法:

    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)
    

    在探索过程中,我了解到flask 不支持分块传输编码。这是令人惊讶的,因为分块传输编码是在 1997 年作为 HTTP 1.1 的一部分引入的。

    不管怎样,我很惊讶ffmpeg 的流与flask 兼容,并且它在 Safari、Firefox 和 Chrome 上得到支持。

    【讨论】:

      猜你喜欢
      • 2013-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-14
      相关资源
      最近更新 更多