WebSocket发送音频流

  • 原始每包数据过于大,后台不能接收,需要分包处理,每包最大1024
  • 原始采样率为48000;通过合并压缩为自己所需采样率,本例中最终采样率为8000
  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.         <meta charset=“UTF-8”>
  5.         <meta name=“viewport” content=“width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0”>
  6.         <meta name=“apple-mobile-web-capable” content=“yes”>
  7.         <title>WebSocket发送音频流</title>
  8.     </head>
  9.     <body>
  10.         <button id=“intercomBegin”>开始对讲</button>
  11.         <button id=“intercomEnd”>关闭对讲</button>
  12.     </body>
  13.     <script type=“text/javascript”>
  14.         var begin = document.getElementById(‘intercomBegin’);
  15.         var end = document.getElementById(‘intercomEnd’);
  16.         var ws = null; //实现WebSocket
  17.         var record = null; //多媒体对象,用来处理音频
  18.         function init(rec) {
  19.             recrecord = rec;
  20.         }
  21.         //录音对象
  22.         var Recorder = function(stream) {
  23.             var sampleBits = 16; //输出采样数位 8, 16
  24.             var sampleRate = 8000; //输出采样率
  25.             var context = new AudioContext();
  26.             var audioInput = context.createMediaStreamSource(stream);
  27.             var recorder = context.createScriptProcessor(4096, 1, 1);
  28.             var audioData = {
  29.                 size: 0, //录音文件长度
  30.                 buffer: [], //录音缓存
  31.                 inputSampleRate: 48000, //输入采样率
  32.                 inputSampleBits: 16, //输入采样数位 8, 16
  33.                 outputSampleRate: sampleRate, //输出采样数位
  34.                 oututSampleBits: sampleBits, //输出采样率
  35.                 clear: function() {
  36.                     this.buffer = [];
  37.                     this.size = 0;
  38.                 },
  39.                 input: function(data) {
  40.                     this.buffer.push(new Float32Array(data));
  41.                     this.size += data.length;
  42.                 },
  43.                 compress: function() { //合并压缩
  44.                     //合并
  45.                     var data = new Float32Array(this.size);
  46.                     var offset = 0;
  47.                     for (var i = 0; i < this.buffer.length; i++) {
  48.                         data.set(this.buffer[i], offset);
  49.                         offset += this.buffer[i].length;
  50.                     }
  51.                     //压缩
  52.                     var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
  53.                     var length = data.length / compression;
  54.                     var result = new Float32Array(length);
  55.                     var index = 0,
  56.                     j = 0;
  57.                     while (index < length) {
  58.                         result[index] = data[j];
  59.                         j += compression;
  60.                         index++;
  61.                     }
  62.                     return result;
  63.                 },
  64.                 encodePCM: function() { //这里不对采集到的数据进行其他格式处理,如有需要均交给服务器端处理。
  65.                     var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
  66.                     var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
  67.                     var bytes = this.compress();
  68.                     var dataLength = bytes.length * (sampleBits / 8);
  69.                     var buffer = new ArrayBuffer(dataLength);
  70.                     var data = new DataView(buffer);
  71.                     var offset = 0;
  72.                     for (var i = 0; i < bytes.length; i++, offset += 2) {
  73.                     var s = Math.max(-1, Math.min(1, bytes[i]));
  74.                         data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
  75.                     }
  76.                     return new Blob([data]);
  77.                 }
  78.             };
  79.             var sendData = function() { //对以获取的数据进行处理(分包)
  80.                 var reader = new FileReader();
  81.                 reader.onload = e => {
  82.                     var outbuffer = e.target.result;
  83.                     var arr = new Int8Array(outbuffer);
  84.                     if (arr.length > 0) {
  85.                         var tmparr = new Int8Array(1024);
  86.                         var j = 0;
  87.                         for (var i = 0; i < arr.byteLength; i++) {
  88.                             tmparr[j++] = arr[i];
  89.                             if (((i + 1) % 1024) == 0) {
  90.                                 ws.send(tmparr);
  91.                                 if (arr.byteLength – i – 1 >= 1024) {
  92.                                     tmparr = new Int8Array(1024);
  93.                                 } else {
  94.                                     tmparr = new Int8Array(arr.byteLength – i – 1);
  95.                                 }
  96.                                 j = 0;
  97.                             }
  98.                             if ((i + 1 == arr.byteLength) && ((i + 1) % 1024) != 0) {
  99.                                 ws.send(tmparr);
  100.                             }
  101.                         }
  102.                     }
  103.                 };
  104.                 reader.readAsArrayBuffer(audioData.encodePCM());
  105.                 audioData.clear();//每次发送完成则清理掉旧数据
  106.             };
  107.             this.start = function() {
  108.                 audioInput.connect(recorder);
  109.                 recorder.connect(context.destination);
  110.             }
  111.             this.stop = function() {
  112.                 recorder.disconnect();
  113.             }
  114.             this.getBlob = function() {
  115.                 return audioData.encodePCM();
  116.             }
  117.             this.clear = function() {
  118.                 audioData.clear();
  119.             }
  120.             recorder.onaudioprocess = function(e) {
  121.                 var inputBuffer = e.inputBuffer.getChannelData(0);
  122.                 audioData.input(inputBuffer);
  123.                 sendData();
  124.             }
  125.         }
  126.         /*
  127.         * WebSocket
  128.         */
  129.         function useWebSocket() {
  130.             ws = new WebSocket(“ws://172.18.5.61/ws”);
  131.             ws.binaryType = ‘arraybuffer’; //传输的是 ArrayBuffer 类型的数据
  132.             ws.onopen = function() {
  133.                 console.log(‘握手成功’);
  134.                 if (ws.readyState == 1) { //ws进入连接状态,则每隔500毫秒发送一包数据
  135.                     record.start();
  136.                 }
  137.             };
  138.             ws.onmessage = function(msg) {
  139.                 console.info(msg)
  140.             }
  141.             ws.onerror = function(err) {
  142.                 console.info(err)
  143.             }
  144.         }
  145.         /*
  146.         * 开始对讲
  147.         */
  148.         begin.onclick = function() {
  149.             navigatornavigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
  150.             if (!navigator.getUserMedia) {
  151.                 alert(‘浏览器不支持音频输入’);
  152.             } else {
  153.                 navigator.getUserMedia({
  154.                 audio: true
  155.             },
  156.             function(mediaStream) {
  157.                 init(new Recorder(mediaStream));
  158.                 console.log(‘开始对讲’);
  159.                 useWebSocket();
  160.             },
  161.             function(error) {
  162.                 console.log(error);
  163.                 switch (error.message || error.name) {
  164.                     case ‘PERMISSION_DENIED’:
  165.                     case ‘PermissionDeniedError’:
  166.                         console.info(‘用户拒绝提供信息。’);
  167.                         break;
  168.                     case ‘NOT_SUPPORTED_ERROR’:
  169.                     case ‘NotSupportedError’:
  170.                         console.info(‘浏览器不支持硬件设备。’);
  171.                         break;
  172.                     case ‘MANDATORY_UNSATISFIED_ERROR’:
  173.                     case ‘MandatoryUnsatisfiedError’:
  174.                         console.info(‘无法发现指定的硬件设备。’);
  175.                         break;
  176.                         default:
  177.                         console.info(‘无法打开麦克风。异常信息:’ + (error.code || error.name));
  178.                         break;
  179.                         }
  180.                     }
  181.                 )
  182.             }
  183.         }
  184.         /*
  185.         * 关闭对讲
  186.         */
  187.         end.onclick = function() {
  188.             if (ws) {
  189.                 ws.close();
  190.                 record.stop();
  191.                 console.log(‘关闭对讲以及WebSocket’);
  192.             }
  193.         }
  194.     </script>
  195. </html>

本文链接地址: WebSocket发送音频流

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注