LM-based (GB28181/EHOME) device voice command (intercom, broadcast)

Introduction

LM video intermediate audio intercom and broadcast support functions such as one-to-one, one-to-many, many-to-many, controlling the front-end to play custom wav voice files and creating voice chat rooms. It can realize real-time multi-party voice calls and voice calls based on basic security equipment. Voice interaction in scenarios such as shouting and alarm-linked voice playback.

The devices supported by LM video middleware are as follows:

  1. Full range of Hikvision equipment
  2. Full range of Dahua equipment
  3. GB28181 device (internal network UDP/TCP, external network TCP mode)
  4. EHOME protocol equipment, including ISUP
  5. LM’s main open interfaces include turning on intercom (broadcast), turning off intercom (broadcast), sending voice and sending wav file playback, etc. For detailed API description, please see the API documentation.

Tips

Requires a microphone and speaker installed on the device

API interface description

Enable intercom (broadcast)
This interface is to start or join the intercom (group), and the request parameters are

  • When deviceId can be empty, initiate a new (join) intercom group for the user. This request does not add new devices to the intercom group.
  • When the ssid is 0, it means creating a new intercom (broadcast) group. When the ssid is not 0, it means joining an existing intercom group. The ssid intercom group must exist, otherwise an error that the intercom group does not exist will be returned.

Note:

  • After the user creates an intercom group, if the interface for sending voice or playing wav files is not called within a period of time (15 seconds), LM will automatically release the current intercom group and close the intercom session with the front-end device.
  • If the intercom device is occupied by an intercom group, LM will return a User Already Opening error in the interface. The caller can obtain the intercom group SSID of the occupied device in the array details returned by the interface, and then initiate a request to join the intercom group. Make voice communications.

Request

POST /api/v1/talk/start?token=f384932b-92b9-4947-a567-10c33a82b44f HTTP/1.1
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 63
    
{
"ssid":0,
    "deviceId":[
"A20221214135512",
"A123445"
]
}

Answer
The returned information needs to be obtained from the detail array to obtain the activation result of each device

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 177
Connection: keep-alive
Access-Control-Allow-Origin: *
    
{
"result": 200,
"message": "OK",
"ssid": 1,
"detail": [
{
"deviceId": "A20221214135512",
"result": "OK"
},
{
"deviceId": "A123445",
"result": "Invalid Device ID"
}
]
}
Close intercom (broadcast)

This interface is to close the intercom group interface. You do not need to call this interface when using it. LM will also automatically detect whether there are still users in the intercom group. The interface parameters

  • When deviceId is empty, close the intercom group specified by parameter ssid
  • ssId is non-0 and this intercom group must exist in LM

Request

 POST /api/v1/talk/stop?token=8305918c-69e8-4280-ae22-85aa5d696e0e HTTP/1.1
    Content-Type: application/json
    Accept: */*
    Host: 192.168.3.23:9030
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive
    Content-Length: 20
    
    {
        "ssid":636
    }

!!! sample “response”

 HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: 42
    Connection: keep-alive
    Access-Control-Allow-Origin: *
    
    {
        "result": 200,
        "message": "OK"
    }
Send voice

This interface is returned after a successful call to open the intercom interface. The client uses websocket to send local and collected PCM voice data. The voice sampling rate is: 44000, the sampling bit is 16, single channel.

Play wav files

This interface only supports PCM voice files in WAV format

Web page H5 audio collection

Web page audio uses the getUserMedia API of HTML5. The getUserMedia API provides users with an interface to access hardware device media (camera, video, audio, geographical location, etc.). Based on this interface, developers can do this without relying on any browser plug-ins. to access hardware media devices. The browser compatibility line for the getUserMedia API is as shown below:

Note
If the web page H5 needs to support audio collection, there are two ways (take the Chrome browser as an example):

  • Enable https on the server side
  • Browse local settings exception, enter browser local settings
  1. Enter chrome://flags/ in the address bar
  2. Find the option Insecure origins treated as secure on the settings page, add the exception server address, and restart the browser to collect audio in the development environment.
Collect audio
 initTalk(){//Initialize multimedia objects
      if (!navigator.mediaDevices.getUserMedia) {
        alert('The browser does not support audio input');
      } else if(this.record == null){
        navigator.mediaDevices.getUserMedia = navigator.mediaDevices.getUserMedia || navigator.mediaDevices.webkitGetUserMedia;
        navigator.mediaDevices.getUserMedia({audio: true})
        .then((mediaStream)=> {
          this.record = new this.getRecorder(mediaStream)
        })
      }
    }
getRecorder(stream){
      let sampleBits = 16;//Output sample bits 8, 16
      let sampleRate = 44100;//Output sampling rate
      let bufSize = 8192;
      let context = new AudioContext();
      let audioInput = context.createMediaStreamSource(stream);
      let recorder = context.createScriptProcessor(0, 1, 1);
      let audio_resample = new Resampler(context.sampleRate, sampleRate, 1, bufSize);
      console.log(audio_resample);
      let audioData = {
        size: 0, //recording file length
        buffer: [], //recording cache
        inputSampleRate: sampleRate, //Input sampling rate
        inputSampleBits: 16, //Input sample bits 8, 16
        outputSampleRate: sampleRate,
        outputSampleBits: sampleBits,
        clear: function () {
          audioData.buffer = [];
          audioData.size = 0;
        },
        input: function (data) {
          audioData.buffer.push(new Float32Array(data));
          audioData.size + = data.length;
        },
        compress: function () { //Combined compression
          //merge
          let data = new Float32Array(this.size);
          let offset = 0;
          for (let i = 0; i <this.buffer.length; i + + ) {
              data.set(this.buffer[i], offset);
              offset + = this.buffer[i].length;
          }
          //compression
          let compression = parseInt(this.inputSampleRate / this.outputSampleRate);
          let length = data.length / compression;
          let result = new Float32Array(length);
          let index = 0, j = 0;
          while (index < length) {
              result[index] = data[j];
              j + = compression;
              index + + ;
          }
          return result;
        },
        encodePCM: function () {//The collected data will not be processed in other formats here, and will be handed over to the server for processing if necessary.
          let sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
          let sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
          let bytes = this.compress();
          let dataLength = bytes.length * (sampleBits / 8);
          let buffer = new ArrayBuffer(dataLength);
          let data = new DataView(buffer);
          let offset = 0;
          for (let i = 0; i < bytes.length; i + + , offset + = 2) {
              let s = Math.max(-1, Math.min(1, bytes[i]));
              data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
          }
          return new Blob([data]);
        }
      };
      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) {
        //audioData.input(e.inputBuffer.getChannelData(0));
        audioData.input(audio_resample.resample(e.inputBuffer.getChannelData(0)));
      }
    }
 getTalkStart(){
this.initTalk()
      if(this.talkOpen){ // this.talkOpen is true to enable intercom
        let data = {
          deviceId: [this.orgId],
          ssid: 0
        };
        talkStartServe(data,this.getToken()).then(res => {
          if(res.result==200 & amp; & amp; res.ssid != 0) {
            this.ssid = res.ssid
            this.imgSrc = require('@/assets/img/yj-open.png')
            this.$message.success('Device intercom is on')
            this.talkOpen = false
            this.sendTalk()
          }else {
            if(res.detail[0] != undefined){
                if(res.detail[0].result == "User Opened Talking"){
                    this.ssid = res.detail[0].ssid
                    this.imgSrc = require('@/assets/img/yj-open.png')
                    this.$message.success('Device intercom is on')
                    this.talkOpen = false
                    this.sendTalk()
                }
                else{
                   this.$message.error(res.detail[0].result)
                   this.imgSrc = require('@/assets/img/yunjing.png')
                   this.talkOpen = true
                }
            }
            else{
              this.$message.error("Intercom opening failed");
              this.imgSrc = require('@/assets/img/yunjing.png')
              this.talkOpen = true
            }
            
          }
        })
      }else { // Close intercom
        if(this.record != null){
            this.record.stop();
        }
        
        if(this.socket != null){
            this.socket.close();
        }

        if(this.pcmPlayer != null){
           this.pcmPlayer.destroy();
           this.pcmPlayer = null;
        }

        //No need to close, the background will automatically close the intercom group based on the connection
        this.$message.success('Device intercom has been closed')
        this.imgSrc = require('@/assets/img/yunjing.png')
        this.talkOpen = true
        
        /*talkStopServe({ssid: this.ssid},this.getToken()).then(res => {
           if(res.result==200) {
            this.$message.success('Device intercom has been closed')
            this.imgSrc = require('@/assets/img/yunjing.png')
            this.talkOpen = true
          }else {
            this.$message.error('Operation failed')
            this.imgSrc = require('@/assets/img/yj-open.png')
            this.talkOpen = false
          }
        })*/
      }
    }

Send audio

 sendTalk() {// WebSocket sends audio
    let that = this // there will be problems with this pointing to that instead
    let locUrl = window.location.host
    let stemp = '';
    if(window.location.protocol === 'https:')
{
stemp ='s';
}
      const socketUrl = 'ws' + stemp + '://' + locUrl + '/ws_talk/' + this.ssid + '?token=' + this.getToken();// + ' &sampleRate=' + this.context.sampleRate;
      that.socket = new WebSocket(socketUrl)
      that.socket.binaryType = 'arraybuffer'
      that.socket.onopen = function () {
        console.log('Browser WebSocket is opened');
        if(that.record != null){
              that.record.start();
        }
        else
        {
          console.log('Sound collection failed to open');
        }
        window.timeInte = setInterval(() => {
          if(that.socket != null & amp; & amp; that.socket.readyState == 1) {
            if (that.record != null & amp; & amp; that.record.getBlob().size != 0) {
              that.socket.send(that.record.getBlob()); //Send audio data
              that.record.clear();
            }
            
          }
        },20)
      };
      if(that.pcmPlayer == null)
      {
          that.pcmPlayer = new PCMPlayer({
                                            inputCodec: 'Int16',
                                            channels: 1,
                                            sampleRate: 16000,
                                            flushTime: 200
                                            })
      }
      that.socket.onmessage = function(msg) {//Receive WebSocket messages
         that.pcmPlayer.feed(msg.data)
      }
    }

Common error codes

Communication contact:

Hangzhou Houhang Technology Co., Ltd. http://houhangkeji.com/

QQ technical exchange group: 698793654