HTTP 实时音频流服务器

问题描述 投票:0回答:4

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

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

我正在阅读有关“传输编码:分块”、“多部分”、mimetype =“audio/xwav”的内容,但仍然不知道使用什么以及如何使用...

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

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>

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

python http flask audio-streaming http-live-streaming
4个回答
3
投票

建议使用分块传输编码,因为资源的长度不定。如果没有它,您将需要指定一个

Content-Length
标头。较旧的客户端过去无法很好地处理分块传输编码,因此旧的黑客要么完全省略
Content-Length
标头(HTTP/1.0 行为),要么指定非常大(实际上无限)的长度。

至于

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

请务必在

preload="none"
元素上设置
<audio>
,以便浏览器不会尝试提前缓冲内容。


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 响应非常简单,如下所示:


0
投票

ffmpeg-python
的帮助下找到了另一个更简单且适合我的解决方案。

from flask import Flask, Response
import ffmpeg

app = Flask(__name__)

RATE = 44100

@app.route('/')
def audio_unlim():
    """Audio streaming generator"""
    print ("Running proc!")
    proc = (ffmpeg
            # Use alsa device 1
            .input("hw:1", format="alsa", ar=RATE)
            # Set output format to wav
            .output("pipe:", format="wav")
            # Configure ffmpeg for steaming
            .global_args("-re")
            # Get subprocess to comunicate with its stdout
            .run_async(pipe_stdout=True, quiet=True, overwrite_output=True)
    )
    def sound():
        # Get 44 bytes of header first
        yield proc.stdout.read(44)
        try:
            while True:
                yield proc.stdout.read(16)
        finally:
            proc.kill()
            proc.wait()

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

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

您不需要在这里关心标头格式 - ffmpeg 将为您完成所有工作。此外,您可能想使用其他格式。我没能做到,但我相信,我们可以。


-1
投票

服务器端流媒体技术

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


  • SHOUTcast

    SHOUTcast是一种跨平台的流媒体专有技术 媒体。由 Nullsoft 开发,它允许 MP3 中的数字音频内容 或 AAC 格式进行广播。对于网络使用,SHOUTcast 流是 通过 HTTP 传输。

    注意SHOUTcast URL 可能需要附加分号 他们


  • 冰铸

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

    注意: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
© www.soinside.com 2019 - 2024. All rights reserved.