Haikang SDK / protocolo Ehome / protocolo RTSP / GB28181 seguridad video servicio en la nube EasyCVR introducción al proceso de recopilación de audio front-end

Haikang SDK / Protocolo Ehome / Protocolo RTSP / Servicio de nube de video de seguridad GB28181 EasyCVR se puede conectar en cascada a través del protocolo GB28181. Si la cámara o el dispositivo admite audio, EasyCVR también puede realizar captura de audio.

EasyCVR.png

El front-end js de la plataforma de video EasyCVR utiliza webapi para recopilar el audio del dispositivo. Preste especial atención a getUserMedia en el caso de no localhost y 127, y https debe estar activado.

Pasos básicos

1. Utilice el método getUserMedia de webrtc para obtener la entrada de audio del dispositivo y utilice el proceso de audio para obtener el flujo de audio (flujo pcm, rango -1 a 1).

2. Remuestreo, la frecuencia de muestreo del front-end es 48000, la frecuencia de muestreo de las necesidades del back-end es 8000, todos deben combinarse y comprimirse

3. Conversión de valor Los datos de entrada obtenidos en cada fragmento son una matriz fija Float32Array con una longitud de 4096, lo que significa que la información de cada punto de muestreo se almacena en coma flotante de 32 bits.

El valor del marco de muestreo almacenado de 32 bits usa -1 a 1 para mapear el rango de almacenamiento de 16 bits -32768 ~ 32767.

El siguiente es el código de conversión:

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);
    }
}

Descripción:

Si s> 0 en realidad asigna 01 a 032767, y el primer bit de signo de un número positivo es 0, entonces 32767 corresponde a 0111 1111 1111 1111, que es 0x7FFF, simplemente multiplique s como un coeficiente; cuando s es Para números negativos, debe mapear 0-1 a 0-32768, por lo que el valor de s también se puede usar directamente como factor de escala para los cálculos de conversión. Cuando se almacenan números negativos en la memoria, debe usar el código de complemento. El código de complemento es el código original además del bit de signo. El bit se invierte y luego se obtiene +1, por lo que el código original de -32768 es 1000 0000 0000 0000 (el bit desbordado se descarta directamente), excepto por el bit de signo, el bit se invierte para obtener 1111 1111 1111 1111, y finalmente se calcula +1 para obtener 1000 0000 0000 0000 (los bits desbordados también se descartan directamente), expresado en hexadecimal es 0x8000. Por cierto, el complemento existe para hacer que la suma de valores positivos y negativos sea igual a 0 en forma binaria.

4. Websocket establece un enlace de cliente para enviar datos y observa que la devolución de llamada de recopilación se activa una vez aproximadamente 80 ms, y los datos se envían en la función de devolución de llamada activada.

<!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>

Más sobre la plataforma de video EasyCVR

La función principal del servicio en la nube de video de seguridad EasyCVR es impulsar fuentes de video RTSP conectadas en la LAN local, incluidas, entre otras, cámaras de red digitales, DVR, NVR, codificadores y otros flujos de video de dispositivos, y enviarlos a Ali, Tencent y otros proveedores de nube pública a través del protocolo RTMP. Entre los servicios de video, tiene excelentes capacidades de transcodificación, reproducción y cascada de video. Al mismo tiempo, el nuevo sistema también es compatible con Haikang SDK, el acuerdo Ehome, el acuerdo estándar nacional GB28181, es una verdadera plataforma de integración de video.

EasyCVR gestión en segundo plano acceso a video video square V1.1.png

EasyCVR ya es compatible con el protocolo integrado Haikang EHome. Los usuarios interesados ​​pueden leer " EasyCVR integrado Haikang EHome protocolo serie-configuración e introducción del protocolo ", " EasyCVR integrado Haikang EHome protocolo serie-protocolo Ehome proceso de llamada de introducción " y otros artículos .

Supongo que te gusta

Origin blog.csdn.net/EasyNTS/article/details/108705234
Recomendado
Clasificación