海康SDK/Ehome协议/RTSP协议/GB28181安防视频云服务EasyCVR前端音频采集流程介绍
海康SDK/Ehome協(xié)議/RTSP協(xié)議/GB28181安防視頻云服務(wù)EasyCVR能夠通過GB28181協(xié)議進(jìn)行級(jí)聯(lián),假如攝像頭或設(shè)備支持音頻的話,EasyCVR同樣也能夠進(jìn)行音頻采集。
EasyCVR視頻平臺(tái)前端js 使用webapi采集設(shè)備音頻,需特別注意getUserMedia在非localhost和127的情況下,需要開啟https。
前端基本步驟
1、利用webrtc的getUserMedia方法獲取設(shè)備音頻輸入,使用audioprocess得到音頻流(pcm流,范圍-1到1)。
2、重采樣,前端采樣率為48000,后端需要的采樣率為8000 ,所有需要合并壓縮
3、值轉(zhuǎn)換,每個(gè)chunk中獲取到的輸入數(shù)據(jù)是一個(gè)長(zhǎng)度為4096的Float32Array定型數(shù)組,也就是說每個(gè)采樣點(diǎn)信息是用32位浮點(diǎn)來存儲(chǔ)的。
32位存儲(chǔ)的采樣幀數(shù)值,是用-1到1來映射16bit存儲(chǔ)范圍-32768~32767的。
如下為轉(zhuǎn)換代碼:
function floatTo16BitPCM(output, offset, input) { for (let i = 0; i < input.length; i++, offset += 2) { //下面一行代碼保證了采樣幀的值在-1到1之間,因?yàn)橛锌赡茉诙嗦暤篮喜⒒蚱渌麪顩r下超出范圍 let s = Math.max(-1, Math.min(1, input[i])); //將32位浮點(diǎn)映射為16位整形表示的值output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);} }說明:
如果s>0其實(shí)就是將01映射到到032767,正數(shù)第一位符號(hào)位為0,所以32767對(duì)應(yīng)的就是0111 1111 1111 1111也就是0x7FFF,直接把s當(dāng)系數(shù)相乘就可以了;當(dāng)s為負(fù)數(shù)時(shí),需要將0-1映射到0-32768,所以s的值也可以直接當(dāng)做比例系數(shù)來進(jìn)行轉(zhuǎn)換計(jì)算,負(fù)數(shù)在內(nèi)存中存儲(chǔ)時(shí)需要使用補(bǔ)碼,補(bǔ)碼是原碼除符號(hào)位以外按位取反再+1得到的,所以-32768原碼是1000 0000 0000 0000(溢出的位直接丟棄),除符號(hào)位外按位取反得到1111 1111 1111 1111,最后再+1運(yùn)算得到1000 0000 0000 0000(溢出的位也直接丟棄),用16進(jìn)制表示就是0x8000。順便多說一句,補(bǔ)碼的存在是為了讓正值和負(fù)值在二進(jìn)制形態(tài)上相加等于0。
4、websocket 建立客戶端鏈接發(fā)送數(shù)據(jù),觀察發(fā)現(xiàn)采集回調(diào)回80ms左右觸發(fā)一次,在觸發(fā)的回調(diào)函數(shù)中發(fā)送數(shù)據(jù)
<!DOCTYPE html> <html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta name="apple-mobile-web-capable" content="yes"><title>錄音并傳遞給后臺(tái)</title></head><body><button id="intercomBegin">開始對(duì)講</button><button id="intercomEnd">關(guān)閉對(duì)講</button></body><script type="text/javascript">var begin = document.getElementById('intercomBegin');var end = document.getElementById('intercomEnd');var ws = null; //實(shí)現(xiàn)WebSocket var record = null; //多媒體對(duì)象,用來處理音頻function init(rec) {record = rec;}//錄音對(duì)象var Recorder = function(stream) {var sampleBits = 16; //輸出采樣數(shù)位 8, 16var sampleRate = 8000; //輸出采樣率var context = new AudioContext();var audioInput = context.createMediaStreamSource(stream);var recorder = context.createScriptProcessor(4096, 1, 1);var audioData = {size: 0, //錄音文件長(zhǎng)度buffer: [], //錄音緩存inputSampleRate: 48000, //輸入采樣率inputSampleBits: 16, //輸入采樣數(shù)位 8, 16outputSampleRate: sampleRate, //輸出采樣數(shù)位oututSampleBits: sampleBits, //輸出采樣率clear: function() {this.buffer = [];this.size = 0;},input: function(data) {this.buffer.push(new Float32Array(data));this.size += data.length; },compress: function() { //合并壓縮//合并var data = new Float32Array(this.size);var offset = 0;for (var i = 0; i < this.buffer.length; i++) {data.set(this.buffer[i], offset);offset += this.buffer[i].length;}//壓縮var compression = parseInt(this.inputSampleRate / this.outputSampleRate);var length = data.length / compression;var result = new Float32Array(length);var index = 0,j = 0;while (index < length) {result[index] = data[j];j += compression;index++;}return result;},encodePCM: function() { //這里不對(duì)采集到的數(shù)據(jù)進(jìn)行其他格式處理,如有需要均交給服務(wù)器端處理。var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);var bytes = this.compress();var dataLength = bytes.length * (sampleBits / 8);var buffer = new ArrayBuffer(dataLength);var data = new DataView(buffer);var offset = 0;for (var i = 0; i < bytes.length; i++, offset += 2) {var s = Math.max(-1, Math.min(1, bytes[i]));data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);}return new Blob([data]);}};var sendData = function() { //對(duì)以獲取的數(shù)據(jù)進(jìn)行處理(分包)var reader = new FileReader();reader.onload = e => {var outbuffer = e.target.result;var arr = new Int8Array(outbuffer);if (arr.length > 0) {var tmparr = new Int8Array(1024);var j = 0;for (var i = 0; i < arr.byteLength; i++) {tmparr[j++] = arr[i];if (((i + 1) % 1024) == 0) {ws.send(tmparr);if (arr.byteLength - i - 1 >= 1024) {tmparr = new Int8Array(1024);} else {tmparr = new Int8Array(arr.byteLength - i - 1);}j = 0;}if ((i + 1 == arr.byteLength) && ((i + 1) % 1024) != 0) {ws.send(tmparr);}}}};reader.readAsArrayBuffer(audioData.encodePCM());audioData.clear();//每次發(fā)送完成則清理掉舊數(shù)據(jù)};this.start = function() {audioInput.connect(recorder);recorder.connect(context.destination);}this.stop = function() {recorder.disconnect();}this.getBlob = function() {return audioData.encodePCM();}this.clear = function() {audioData.clear();}recorder.onaudioprocess = function(e) {var inputBuffer = e.inputBuffer.getChannelData(0);audioData.input(inputBuffer);sendData();}}/** WebSocket*/function useWebSocket() {ws = new WebSocket("ws://192.168.2.9:8080/websocket");ws.binaryType = 'arraybuffer'; //傳輸?shù)氖?ArrayBuffer 類型的數(shù)據(jù)ws.onopen = function() {console.log('握手成功');if (ws.readyState == 1) { //ws進(jìn)入連接狀態(tài),則每隔500毫秒發(fā)送一包數(shù)據(jù)record.start();}};ws.onmessage = function(msg) {console.info(msg)}ws.onerror = function(err) {console.info(err)}}/** 開始對(duì)講*/begin.onclick = function() {navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;if (!navigator.getUserMedia) {alert('瀏覽器不支持音頻輸入');} else {navigator.getUserMedia({audio: true},function(mediaStream) {init(new Recorder(mediaStream));console.log('開始對(duì)講');useWebSocket();},function(error) {console.log(error);switch (error.message || error.name) {case 'PERMISSION_DENIED': case 'PermissionDeniedError': console.info('用戶拒絕提供信息。'); break; case 'NOT_SUPPORTED_ERROR': case 'NotSupportedError': console.info('瀏覽器不支持硬件設(shè)備。'); break; case 'MANDATORY_UNSATISFIED_ERROR': case 'MandatoryUnsatisfiedError': console.info('無法發(fā)現(xiàn)指定的硬件設(shè)備。'); break; default: console.info('無法打開麥克風(fēng)。異常信息:' + (error.code || error.name)); break; } })}}/** 關(guān)閉對(duì)講*/end.onclick = function() {if (ws) {ws.close();record.stop();console.log('關(guān)閉對(duì)講以及WebSocket');}}</script> </html>更多關(guān)于EasyCVR視頻平臺(tái)
EasyCVR安防視頻云服務(wù)的主要功能是將本地局域網(wǎng)內(nèi)連通的RTSP視頻源,包括但不限于數(shù)字網(wǎng)絡(luò)攝像機(jī)、DVR、NVR、編碼器等設(shè)備視頻流,通過RTMP協(xié)議推送到阿里、騰訊等公有云廠商的視頻服務(wù)中,具備優(yōu)秀的視頻轉(zhuǎn)碼、播放、級(jí)聯(lián)能力。同時(shí)該新系統(tǒng)也支持海康SDK、Ehome協(xié)議,GB28181國(guó)標(biāo)協(xié)議,是一套真正的視頻融合平臺(tái)。
EasyCVR已經(jīng)支持集成海康EHome協(xié)議,感興趣的用戶可以閱讀一下《EasyCVR集成海康EHome協(xié)議系列——配置及協(xié)議介紹》、《EasyCVR集成海康EHome協(xié)議系列——Ehome協(xié)議調(diào)用流程介紹》等文。
總結(jié)
以上是生活随笔為你收集整理的海康SDK/Ehome协议/RTSP协议/GB28181安防视频云服务EasyCVR前端音频采集流程介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决锚点在IE8中失效
- 下一篇: 人人开源(后台代码、前端项目、代码自动生