zoukankan      html  css  js  c++  java
  • 海康SDK/Ehome协议/RTSP协议/GB28181安防视频云服务EasyCVR前端音频采集流程介绍

    海康SDK/Ehome协议/RTSP协议/GB28181安防视频云服务EasyCVR能够通过GB28181协议进行级联,假如摄像头或设备支持音频的话,EasyCVR同样也能够进行音频采集。

    EasyCVR视频平台前端js 使用webapi采集设备音频,需特别注意getUserMedia在非localhost和127的情况下,需要开启https。

    前端基本步骤

    1、利用webrtc的getUserMedia方法获取设备音频输入,使用audioprocess得到音频流(pcm流,范围-1到1)。

    2、重采样,前端采样率为48000,后端需要的采样率为8000 ,所有需要合并压缩

    3、值转换,每个chunk中获取到的输入数据是一个长度为4096的Float32Array定型数组,也就是说每个采样点信息是用32位浮点来存储的。

    32位存储的采样帧数值,是用-1到1来映射16bit存储范围-32768~32767的。

    如下为转换代码:

    function floatTo16BitPCM(output, offset, input) {
    for (let i = 0; i < input.length; i++, offset += 2) {
    //下面一行代码保证了采样帧的值在-1到1之间,因为有可能在多声道合并或其他状况下超出范围 
     
            let s = Math.max(-1, Math.min(1, input[i]));
    //将32位浮点映射为16位整形表示的值
            output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
        }
    }
    

      

    说明:

    如果s>0其实就是将01映射到到032767,正数第一位符号位为0,所以32767对应的就是0111 1111 1111 1111也就是0x7FFF,直接把s当系数相乘就可以了;当s为负数时,需要将0-1映射到0-32768,所以s的值也可以直接当做比例系数来进行转换计算,负数在内存中存储时需要使用补码,补码是原码除符号位以外按位取反再+1得到的,所以-32768原码是1000 0000 0000 0000(溢出的位直接丢弃),除符号位外按位取反得到1111 1111 1111 1111,最后再+1运算得到1000 0000 0000 0000(溢出的位也直接丢弃),用16进制表示就是0x8000。顺便多说一句,补码的存在是为了让正值和负值在二进制形态上相加等于0。

    4、websocket 建立客户端链接发送数据,观察发现采集回调回80ms左右触发一次,在触发的回调函数中发送数据

    <!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>录音并传递给后台</title>
        </head>
        <body>
            <button id="intercomBegin">开始对讲</button>
            <button id="intercomEnd">关闭对讲</button>
        </body>
        <script type="text/javascript">
            var begin = document.getElementById('intercomBegin');
            var end = document.getElementById('intercomEnd');
    		
            var ws = null; //实现WebSocket 
            var record = null; //多媒体对象,用来处理音频
     
            function init(rec) {
                record = rec;
            }
    		
            //录音对象
            var Recorder = function(stream) {
                var sampleBits = 16; //输出采样数位 8, 16
                var sampleRate = 8000; //输出采样率
                var context = new AudioContext();
                var audioInput = context.createMediaStreamSource(stream);
                var recorder = context.createScriptProcessor(4096, 1, 1);
                var audioData = {
                    size: 0, //录音文件长度
                    buffer: [], //录音缓存
                    inputSampleRate: 48000, //输入采样率
                    inputSampleBits: 16, //输入采样数位 8, 16
                    outputSampleRate: sampleRate, //输出采样数位
                    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() { //这里不对采集到的数据进行其他格式处理,如有需要均交给服务器端处理。
                        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() { //对以获取的数据进行处理(分包)
                    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();//每次发送完成则清理掉旧数据
                };
    			
                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'; //传输的是 ArrayBuffer 类型的数据
                ws.onopen = function() {
                    console.log('握手成功');
                    if (ws.readyState == 1) { //ws进入连接状态,则每隔500毫秒发送一包数据
                        record.start();
                    }
                };
    			
                ws.onmessage = function(msg) {
                    console.info(msg)
                }
    			
                ws.onerror = function(err) {
                    console.info(err)
                }
            }
    		
            /*
            * 开始对讲
            */
            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('开始对讲');
                    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('浏览器不支持硬件设备。');  
                            break;  
                        case 'MANDATORY_UNSATISFIED_ERROR':  
                        case 'MandatoryUnsatisfiedError':  
                            console.info('无法发现指定的硬件设备。');  
                            break;  
                            default:  
                            console.info('无法打开麦克风。异常信息:' + (error.code || error.name));  
                            break;  
                            }  
                        }
                    )
                }
            }
     
            /*
            * 关闭对讲
            */
            end.onclick = function() {
                if (ws) {
                    ws.close();
                    record.stop();
                    console.log('关闭对讲以及WebSocket');
                }
            }
        </script>
    </html>
    

      

    更多关于EasyCVR视频平台

    EasyCVR安防视频云服务的主要功能是将本地局域网内连通的RTSP视频源,包括但不限于数字网络摄像机、DVR、NVR、编码器等设备视频流,通过RTMP协议推送到阿里、腾讯等公有云厂商的视频服务中,具备优秀的视频转码、播放、级联能力。同时该新系统也支持海康SDK、Ehome协议,GB28181国标协议,是一套真正的视频融合平台。

    EasyCVR已经支持集成海康EHome协议,感兴趣的用户可以阅读一下《EasyCVR集成海康EHome协议系列——配置及协议介绍》、《EasyCVR集成海康EHome协议系列——Ehome协议调用流程介绍》等文。

     
  • 相关阅读:
    JdbcTemplate查询数据 三种callback之间的区别
    velocity加减运算注意格式 ,加减号的左右都要有空格
    java怎样读取数据库表中字段的数据类型?
    一台电脑同时运行多个tomcat配置方法
    启动PL/SQL Developer 报字符编码不一致错误 Database character set (AL32UTF8) and Client character set (ZHS16GBK) are different. Character set conversion may cause unexpected results. Note: you can set the client
    PL/SQL database character set(AL32UTF8) and Client character set(ZHS16GBK) are different 2012-04-11 13:01
    sqlserver得到昨天的数据
    iOS消息机制
    Narrow Art Gallery
    Hadoop入门进阶步步高(一)-环境准备
  • 原文地址:https://www.cnblogs.com/EasyNVR/p/13704506.html
Copyright © 2011-2022 走看看