网页端推流

本文介绍网页端如何进行实时推流来支持实时记录场景下的音频识别流程。

前提条件

开始录音

  1. 开启录音功能

说明

以下示例是使用浏览器原生 navigator.getUserMedia方法,基础的实现验证听悟API服务可调通。您可以根据业务场景需要,选用合适的录音库或录音方法,实现更多设备兼容(如浏览器Web&H5、小程序、NativeApp等,不同场景的录音方法不同)

navigator.getUserMedia({
  audio: true
}, stream => {
  globalRecorder = new Recorder(stream);
  console.log('开始录音');
  connectWebSocket();
}, error => {
  console.log(error);
  // TODO 一些异常错误处理
})
  1. 音频数据格式处理

    录音开启成功后,对原始音频流进行数据合并压缩,并处理成听悟API支持的实时语音流格式

  • 音频压缩

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;
},
  • 转成听悟API支持的实时语音流格式。

说明

以下示例是将语音转换成16K、16bit、PCM的目标格式。(建议使用setIntervel, 100ms 发送一次)

如果业务场景需要转换成其他语音格式,以下encodePCM方法不可用,请注意更改成合适的转换方法。

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

音频数据传输

  1. 建立WSS链接

说明

WSS链接地址是创建实时记录并成功获得推流地址返回的MeetingJoinUrl。

function connectWebSocket() {
    ws = new WebSocket("请输入您创建实时会议时返回的MeetingJoinUrl"); 
    ws.binaryType = 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据
    ws.onopen = function () {
      if (ws.readyState == 1) {
        globalRecorder.start();
      }
      const obj = {
        header: {
          name: "StartTranscription", 
          namespace: "SpeechTranscriber", 
        },
        payload: {
          format: "pcm", // 示例为pcm格式,可按需选择格式。注意如果非pcm格式,则样例中encodePCM方法不能使用,需自行实现数据格式转换。
        }
        
      }
      ws.send(JSON.stringify(obj));
    };

    ws.onmessage = function (msg) {
      console.info(msg)
    }

    ws.onerror = function (err) {
      console.info(err)
    }
  }
  1. 音频数据传输

说明

建议对音频数据进行分包传输,以下示例为分包传输示例。

var sendAudioData = 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();
    };

回调处理

客户端循环发送语音数据后,根据如下返回事件持续接收识别和翻译结果

 ws.onmessage = function (msg) {
        console.info(msg);
        // msg.data 是接收到的数据(具体参考「实时推流返回事件」)
        // 根据业务处理数据
        if (typeof msg.data === "string") {
          const dataJson = JSON.parse(msg.data);
          switch (dataJson.header.name) {
            case "SentenceBegin": {
              // 句子开始事件
              console.log("句子", dataJson.payload.index, "开始");
              break;
            }
            case "TranscriptionResultChanged":
              // 句中识别结果变化事件
              console.log(
                "句子" + dataJson.payload.index + "中间结果:",
                dataJson.payload.result
              );
              break;

            case "SentenceEnd": {
              // 句子结束事件
              console.log(
                "句子" + dataJson.payload.index + "结束:",
                dataJson.payload.result + dataJson.payload.stash_result.text
              );
              break;
            }
            case "ResultTranslated": {
              // 识别结果翻译事件
              console.log(
                "句子翻译结果",
                JSON.stringify(dataJson.payload.translate_result)
              );
              break;
            }
            //... 
          }
        }
      };

关闭录音

    const params = {
      header: {
        name: 'StopTranscription', 
        namespace: 'SpeechTranscriber',
      },
      payload: {},
    };
    ws.send(JSON.stringify(params));
    setTimeout(() => {
      ws.close();
    }, 10000);

示例结果

以上代码示例输出结果可通过如下方式查看。

image