华丽丶转身 發表於 2020-2-16 15:05:00

Html5 播放实时音频流

<p>&nbsp; &nbsp;&nbsp; 项目需求 Web端播放实时音频流,折腾了两天后问题得以解决。记录下开发调试过程,方便后来者。</p>
<p>首次想到是利用Audio标签,Audio标签可以直接播放MP3格式,服务端将实时音频流编码成MP3格式</p>
<p>通过Http方式传给Web端即可,前端代码如下所示:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Title&lt;/title&gt;
    &lt;script&gt;
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
        &lt;audio controls="controls" autoplay="autoplay"&gt;
        &lt;source src="http://127.0.0.1:12345/cgmedia/28181/getaudio?id=34020000001310000001@192.168.1.108:5060&amp;format=mp3&amp;transporttype=udp&amp;transportport=22000" type="audio/mpeg"&gt;
        &lt;/audio&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<p>  通过Audio标签 实现音频流播放 代码比较简单,但有缓冲过大问题,粗略测试了下延时 20-30s左右,这显然</p>
<p>不满足实时播放实时播放需求。开始调试时怀疑是后台服务端传输过来的流有问题,于是将流保存成MP3文件进行</p>
<p>测试 ,结果正常未出现缓冲一段时间后开始播放。分析Audio标签发出的Http报文,发现Http请求Head中有Range字段,</p>
<p>尝试做了相应Response,结果未发生变化。猜想Audio标签可能只适合于MP3文件(一次性将Audio数据加载完成再处</p>
<p>理)。如这个猜测不对,欢迎指正(本人主要从事后台媒体服务开发,前端经验很少)。</p>
<p>&nbsp; &nbsp; &nbsp; Audio标签的方式不行,想到利用Web Audio API是实现,基本的思路是:通过WebSocket 接收服务端推送过来的音</p>
<p>频流(MP3格式)调用decodeAudioData进行解码,最后将解码数据推送到AudioContext最后一个Node,代码如下:</p>
<div class="cnblogs_Highlighter">&nbsp;
<div>&lt;!DOCTYPE html&gt;<br>&lt;html lang="en"&gt;<br>&lt;head&gt;<br>&nbsp;&nbsp;&nbsp; &lt;meta charset="UTF-8"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;title&gt;Title&lt;/title&gt;<br>&nbsp;&nbsp;&nbsp; &lt;script&gt;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function WebSocketTest()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; var wsUrl = "ws://127.0.0.1:12345/cgmedia/28181/getaudio?id=34020000001310000001@192.168.1.108:5060&amp;format=mp3&amp;transporttype=udp&amp;transportport=22000";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws = new WebSocket(wsUrl);<br>&nbsp;&nbsp;&nbsp;&nbsp; ws.binaryType = 'arraybuffer'; //arraybuffer<br>&nbsp;&nbsp;&nbsp;&nbsp; ws.onmessage = function(msg) {<br>&nbsp;&nbsp;&nbsp;&nbsp; var data =&nbsp; msg.data;<br>&nbsp;&nbsp;&nbsp;&nbsp;var datalen = msg.data.size;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;var reader = new FileReader();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var audioContext = new AudioContext({<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sampleRate:8000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reader.onload = function(evt)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(evt.target.readyState == FileReader.DONE)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; audioContext.decodeAudioData(data, function(buffer) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log("decode success");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var bufferSource = audioContext.createBufferSource();<br>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; bufferSource.connect(audioContext.destination);<br>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; bufferSource.buffer = buffer;<br>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; bufferSource.start(0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}, function(e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log("decode failed" + e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;reader.readAsArrayBuffer(new Blob());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;ws.onopen = function(evt) {<br>&nbsp;&nbsp;&nbsp;&nbsp;if(self.verbose) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log("Connection open......");<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;ws.onclose = function(evt) {<br>&nbsp;&nbsp;&nbsp;&nbsp;if(self.verbose) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log("Connection closed......");<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</div>
<div>&nbsp;&nbsp;&nbsp; &lt;/script&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>&nbsp;&lt;button onclick="WebSocketTest()"&gt;发送请求&lt;/button&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</div>






</div>
<p> &nbsp; 采用Audio Web API方式播放实时流会出现卡顿现象,以上方法一次性解码的数据可以连续播放,每次解码后要重新</p>
<p>创建BufferSource,显而易见这种播放模式播放实时流效率很低,查阅了Audio Web API 文档 播放网络流似乎要利用</p>
<p>&nbsp;基于<span class="token class-name">AudioWorkletProcessor的自定义节点,文档也给了一个简单的例子,那个例子不符合我们的使用场景。没有</span></p>
<p><span class="token class-name">更多的时间研究Audio Web API,这种方案只好作罢,单看Audio Web API接口有些无语, 这有可能跟Web端处理能力有关。</span></p>
<p><span class="token class-name">&nbsp;<strong>&nbsp;<span style="font-size: 16px"> 可行的方法</span></strong></span></p>
<p>&nbsp; &nbsp;&nbsp; 条条大路通罗马,毕竟项目不是科研,可以实现需求就行。一种可行的方法是服务端输出Rtmp音频流 通过video.js播放</p>
<p>实际应该是用了flash。项目开始已经想到了这个方案可行只是觉得有些绕(需要将音频流封装后推送到rtmp server)。</p>
<p>另外一个可行的方案就是利用H5 MSE实现,这个方案也有开源的库可以用,例如flv.js 这需要 服务端将音频打包成flv格式</p>
<p>推送给Web前端,Web端接收到音频数据后调用flv.js进行播放。video.js 及flv.js github都有下载 代码就不贴出来了。</p>
<p>如需交流可加QQ群766718184,1038388075 或者QQ3501870</p>
<p>视频下载地址:http://www.chungen90.com/?list_53</p>
<p>&nbsp;Demo下载地址:&nbsp;http://www.chungen90.com/?list_52</p><br><br>
来源:https://www.cnblogs.com/wanggang123/p/12316895.html
頁: [1]
查看完整版本: Html5 播放实时音频流