HTTP实时音频流服务器

时间:2019-11-27 07:58:21

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

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

我应该在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>

为了实现连续的块流而需要进行哪些更改?

3 个答案:

答案 0 :(得分:1)

建议使用块传输编码,因为资源的长度不确定。没有它,您将需要指定一个Content-Length标头。较旧的客户端过去无法很好地处理分块的传输编码,因此较旧的破解方法是完全放弃Content-Length标头(HTTP / 1.0行为),或者指定一个很大的长度(实际上是无限长)。

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

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

答案 1 :(得分:0)

实际上,我对以下代码(没有任何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标头,但其中写的大小是veerery大号,告诉播放器等待veeery大数据缓冲区。直到“结束”播放器毫无问题地播放即将到来的数据块为止(不再有WAV标头,而只是音频数据块!)。这没有任何“传输编码:分块”或其他任何东西!只需将mimetype设置为“ audio / x-wav”。 HTTP响应非常简单,如下所示:

enter image description here

答案 2 :(得分:-1)

  

服务器端流技术

     

为了流媒体直播音频,您将需要在自己的计算机上运行特定的流媒体软件   服务器。

     
     
      
  • SHOUTcast

         

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

         

    注意SHOUTcast URLs may require a semi-colon to be appended to them

  •   
     
     
      
  • Icecast

         

    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