HTTP实时音频流服务器

HTTP realtime audio streaming server

作为 proof-of-concept 我需要创建一个 HTTP 服务器,它在 GET 请求时应该启动连续的 non-encoded/non-compressed 音频数据流 - WAV、PCM16。假设音频数据是 4096 个随机生成的单声道音频样本块 @44.1kHz 采样率。

我应该在 HTTP 响应 header 中输入什么,以便另一端的浏览器在其 UI 中启动播放器供用户实时收听?

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

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

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

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>

要改变什么才能实现块的连续流?

Server-side Streaming Technologies

In order to stream live audio, you will need to run specific streaming software on your server.



  • Icecast

    The Icecast server is an open source technology for streaming media. Maintained by the Xiph.org Foundation, it streams Ogg Vorbis/Theora as well as MP3 and AAC format via the SHOUTcast protocol.

    Note: SHOUTcast and Icecast are among the most established and popular technologies, but there are many more streaming media systems available.


编辑

我是 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

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

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

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

实际上,我已经使用以下代码(没有任何 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 header,但是那里写的大小非常大,告诉播放器等待非常大的数据缓冲区。直到 "end" 播放器毫无问题地播放即将到来的数据块(不再有 WAV headers 只是音频数据块!)。这没有任何 "Transfer-encoding: chunked" 或其他任何东西!只需将 mimetype 设置为 "audio/x-wav"。并且 HTTP 响应非常简单,如下所示: