【问题标题】:HTTP realtime audio streaming serverHTTP 实时音频流服务器
【发布时间】:2020-03-22 17:39:44
【问题描述】:

作为概念验证,我需要创建一个 HTTP 服务器,它在 GET 请求时应该启动非编码/非压缩音频数据的连续流 - WAV、PCM16。假设音频数据是 4096 个随机生成的单声道音频样本,采样率为 44.1kHz。

为了让另一端的浏览器在其 UI 中启动播放器让用户实时收听,我应该在 HTTP 响应标头中添加什么?

我正在阅读有关“Transfer-Encoding: chunked”、“multipart”、mimetype="audio/xwav" 的信息,但仍然不知道使用什么以及如何使用...

如果有人能给我一个关于 Python/Flask 的确切示例,那就太好了,因为我对 Web 开发不是很有信心。

PS1:将 HTTP 服务器替换为硬件功率有限的嵌入式设备将是 PoC 之后的下一个阶段。

PS2:这是实际工作并将 WAV 块作为单个 HTTP 响应发送的代码:

from flask import Flask, Response,render_template
import pyaudio
import audio_processing as audioRec

app = Flask(__name__)

def genHeader(sampleRate, bitsPerSample, channels, samples):
    datasize = samples * channels * bitsPerSample // 8
    o = bytes("RIFF",'ascii')                                               # (4byte) Marks file as RIFF
    o += (datasize + 36).to_bytes(4,'little')                               # (4byte) File size in bytes excluding this and RIFF marker
    o += bytes("WAVE",'ascii')                                              # (4byte) File type
    o += bytes("fmt ",'ascii')                                              # (4byte) Format Chunk Marker
    o += (16).to_bytes(4,'little')                                          # (4byte) Length of above format data
    o += (1).to_bytes(2,'little')                                           # (2byte) Format type (1 - PCM)
    o += (channels).to_bytes(2,'little')                                    # (2byte)
    o += (sampleRate).to_bytes(4,'little')                                  # (4byte)
    o += (sampleRate * channels * bitsPerSample // 8).to_bytes(4,'little')  # (4byte)
    o += (channels * bitsPerSample // 8).to_bytes(2,'little')               # (2byte)
    o += (bitsPerSample).to_bytes(2,'little')                               # (2byte)
    o += bytes("data",'ascii')                                              # (4byte) Data Chunk Marker
    o += (datasize).to_bytes(4,'little')                                    # (4byte) Data size in bytes
    return o

FORMAT = pyaudio.paInt16
CHUNK = 102400 #1024
RATE = 44100
bitsPerSample = 16 #16
CHANNELS = 1
wav_header = genHeader(RATE, bitsPerSample, CHANNELS, CHUNK)

audio = pyaudio.PyAudio()

# start Recording
stream = audio.open(format=FORMAT, channels=CHANNELS,
    rate=RATE, input=True, input_device_index=10,
    frames_per_buffer=CHUNK)
# print "recording..."

@app.route('/')
def index():
    """Video streaming home page."""
    return render_template('index2.html')

@app.route('/audio_unlim')
def audio_unlim():
    # start Recording
    def sound():

        #while True:
        #    data = wav_header + stream.read(CHUNK)
        #    yield(data)
        data = wav_header + stream.read(CHUNK)
        yield(data)

    return Response(sound(),
                    mimetype="audio/x-wav")


if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=True, threaded=True,port=5000)

和 index2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <audio controls>
        <source src="{{ url_for('audio_unlim') }}" type="audio/x-wav;codec=pcm">
        Your browser does not support the audio element.
    </audio
</body>
</html>

为了实现块的连续流需要改变什么?

【问题讨论】:

  • 这能回答你的问题吗? Audio Livestreaming with Python & Flask
  • @noslenkwah,不,这不能解决我的问题。您所指的链接是关于如何使用单个 HTTP 响应一次性发送一大块数据。我想通过使用“Transfer-Encoding:chunked”或“Content-Transfer-Encoding:multipart”来获得连续的音频数据流。仍然不确定 HTTP 序列图如何执行此操作以及在 HTTP 标头和 MIME 中键入什么以通知浏览器启动相应的播放器以正确解释 WAV 标头中的 RIFF 数据...

标签: python http flask audio-streaming http-live-streaming


【解决方案1】:

服务器端流技术

要流式传输实时音频,您需要在您的设备上运行特定的流式传输软件 服务器。



  • 冰播

    Icecast 服务器是一种用于流式传输的开源技术 媒体。由 Xiph.org 基金会维护,它流式传输 Ogg 通过 SHOUTcast 播放 Vorbis/Theora 以及 MP3 和 AAC 格式 协议。

    注意:SHOUTcast 和 Icecast 是最成熟的和 流行的技术,但还有更多的流媒体系统 可用。


编辑

我是一个 Django 人,我一直在测试一些东西,而且,它似乎工作正常,只需要一些适当的文件管理和东西。我一直在使用 mp3,但你可以使用任何浏览器支持的东西。

from django.http import StreamingHttpResponse

def stream(request):
    return StreamingHttpResponse(streamer(200000) ,content_type='audio/mp3')

def streamer(pointer):
    with open('media/Indila - Parle A Ta Tete.mp3', 'rb') as file:
        file.seek(pointer)
        for chunk in iter(lambda: file.read(4096), b''):
            yield chunk
#the connection is open until this iterator hasn't finished

【讨论】:

  • 以后在嵌入式设备上运行像 SHOUTcast 和 Icecast 这样的复杂技术是很困难的,甚至是不可能的。我希望看到带有确切 HTTP 响应标头的简约示例以及通知 Chrome 浏览器启动其 WAV PCM16 音频播放器并连续播放所需的任何元数据。
  • 那么,你将不得不进入低级浏览器的Web Audio API,或者使用一个非常著名的JS库,比如howler.js
  • @ArthurGrigoryan 这个答案根本不正确。 valioiv 正在寻找的是可行的,实际上与 SHOUTcast/Icecast 的工作方式非常相似。
  • 是的,对不起。我认为在流式传输之前需要对音频进行分段,就像视频一样。
【解决方案2】:

建议使用分块传输编码,因为资源的长度不定。如果没有它,您将需要指定 Content-Length 标头。旧客户端过去无法很好地处理分块传输编码,因此旧的 hack 要么完全省略 Content-Length 标头(HTTP/1.0 行为),要么指定一个非常大(实际上是无限)的长度。

至于Content-Type,您可以使用audio/vnd.wav;codec=1 进行常规PCM。

请务必在您的&lt;audio&gt; 元素上设置preload="none",这样浏览器就不会尝试提前缓冲内容。

【讨论】:

  • 你能告诉我在上面的代码中需要改变什么吗?我已经用我正在使用的确切代码更新了我的问题...
  • @valioiv 不,抱歉,我不是 Python 人。请按照我的建议设置正确的Content-Type。 (现在,你有audio/x-wav,它可能有用,但谁知道呢。)我想 Flask 会为你处理分块传输编码?
  • 我假设,Flask 通过将迭代器传递给 Response 来处理分块传输
【解决方案3】:

实际上,我已经使用以下代码(没有任何 index.html)做了一种解决方法,并且它可以正常工作而没有任何中断:

from flask import Flask, Response,render_template
import pyaudio
import audio_processing as audioRec

app = Flask(__name__)

def genHeader(sampleRate, bitsPerSample, channels, samples):
    datasize = 10240000 # Some veeery big number here instead of: #samples * channels * bitsPerSample // 8
    o = bytes("RIFF",'ascii')                                               # (4byte) Marks file as RIFF
    o += (datasize + 36).to_bytes(4,'little')                               # (4byte) File size in bytes excluding this and RIFF marker
    o += bytes("WAVE",'ascii')                                              # (4byte) File type
    o += bytes("fmt ",'ascii')                                              # (4byte) Format Chunk Marker
    o += (16).to_bytes(4,'little')                                          # (4byte) Length of above format data
    o += (1).to_bytes(2,'little')                                           # (2byte) Format type (1 - PCM)
    o += (channels).to_bytes(2,'little')                                    # (2byte)
    o += (sampleRate).to_bytes(4,'little')                                  # (4byte)
    o += (sampleRate * channels * bitsPerSample // 8).to_bytes(4,'little')  # (4byte)
    o += (channels * bitsPerSample // 8).to_bytes(2,'little')               # (2byte)
    o += (bitsPerSample).to_bytes(2,'little')                               # (2byte)
    o += bytes("data",'ascii')                                              # (4byte) Data Chunk Marker
    o += (datasize).to_bytes(4,'little')                                    # (4byte) Data size in bytes
    return o

FORMAT = pyaudio.paInt16
CHUNK = 1024 #1024
RATE = 44100
bitsPerSample = 16 #16
CHANNELS = 1
wav_header = genHeader(RATE, bitsPerSample, CHANNELS, CHUNK)

audio = pyaudio.PyAudio()

# start Recording
stream = audio.open(format=FORMAT, channels=CHANNELS,
    rate=RATE, input=True, input_device_index=10,
    frames_per_buffer=CHUNK)
# print "recording..."

@app.route('/audio_unlim')
def audio_unlim():
    # start Recording
    def sound():
        data = wav_header
        data += stream.read(CHUNK)
        yield(data)
        while True:
            data = stream.read(CHUNK)
            yield(data)

    return Response(sound(), mimetype="audio/x-wav")


if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=True, threaded=True,port=5000)

我刚刚开始发送 WAV 标头,但写入的大小是一个非常大的数字,告诉玩家等待非常大的数据缓冲区。直到“结束”播放器毫无问题地播放即将到来的数据块(不再有 WAV 标头,只是音频数据块!)。这没有任何“传输编码:分块”或其他任何东西!只需将 mimetype 设置为“audio/x-wav”。而且HTTP响应非常简单,如下:

【讨论】:

  • import audio_processing as audioRec ModuleNotFoundError: No module named 'audio_processing'
猜你喜欢
  • 2015-08-10
  • 2013-01-08
  • 2017-10-30
  • 2013-03-05
  • 2013-02-27
  • 2018-06-27
  • 1970-01-01
  • 1970-01-01
  • 2011-12-19
相关资源
最近更新 更多