123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- <template>
- <view>
- <u-toast ref="uToast" />
- </view>
- </template>
- <script>
- import { videoStart,videoControl } from "@/api/core/sendOrder";
- //录音对象
- 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]);
- },
- };
- 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));
- };
- };
- export default {
- data() {
- return {
- ws: null,
- deviceId:'',
- wsUrl: "wss://80.5000v.com/websocket", // websocket地址
- record: null, //多媒体对象,用来处理音频
- sendingTimes: 0, // 发送设备号的次数
- timeInte: null, // 定时任务 定时发送
- audo: new Audio(),
- openIntercom:false
- };
- },
- created() {
-
- },
- methods: {
- init () {
- navigator.mediaDevices
- .getUserMedia({ audio: true})
- .then( (mediaStream)=> {
- /* 使用这个 stream stream */
- console.log('使用这个',mediaStream)
- this.record = new Recorder(mediaStream);
- })
- .catch(function (err) {
- /* 处理 error */
- console.log('处理 error',err)
- uni.showToast({
- title: '浏览器不支持音频输入'+err,
- duration: 2000,
- icon: 'none'
- });
- });
- // navigator.getUserMedia(
- // {
- // audio: true,
- // },
- // (mediaStream) => {
- // console.log('2使用这个',mediaStream)
- // this.record = new Recorder(mediaStream);
-
- // },
- // (error) => {
- // });
- // console.log('方法',getUserMedia)
- // let getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
- // navigator.getUserMedia = getUserMedia
- // if (!navigator.getUserMedia) {
- // uni.showToast({
- // title: '浏览器不支持音频输入'+getUserMedia,
- // duration: 2000,
- // icon: 'none'
- // });
- // console.log('浏览器不支持音频输入')
- // } else {
- // navigator.getUserMedia(
- // {
- // audio: true,
- // },
- // (mediaStream) => {
- // this.record = new Recorder(mediaStream);
- // console.log('2121',this.record)
- // },
- // (error) => {
- // }
- // );
- // }
- },
- async toIntercom(deviceId) {
- console.log(deviceId,'itemdeviceId')
- if(this.isNull(deviceId)) {
- uni.showToast({
- title: 'deviceId为空',
- duration: 2000,
- icon: 'none'
- });
- return false
- }
- this.init()
- this.deviceId = deviceId;
- // #ifdef H5
- if(this.openIntercom){
- //打开中 执行关闭
- let obj = {
- audioVideoType: 0,
- channel: 1,
- controll: 4,
- dataRate: 0,
- deviceId: deviceId, //设备号
- };
- let res = await videoControl(obj);
- if(res.code == '0'){
- uni.closeSocket();
-
- this.openIntercom = false;
- this.ws.close();
- this.record.stop();
- clearInterval(this.timeInte);
- this.ws = null;
- this.timeInte = null;
-
- }else{
- uni.showToast({
- title:res.desc,
- duration: 2000,
- icon: 'none'
- });
- }
- }else {
- // 关闭中 执行打开
- let obj = {
- channel: 1,
- dataRate: 1,
- dataType: 2, // 对讲
- deviceId: deviceId, // 设备号
- };
- let res = await videoStart(obj);
- if(res.code == '0'){
- // 成功以后连接socket
- // 命令下发成功
- let wsUrl;
- if (window.location.hostname != "localhost") {
- wsUrl = "wss://" + window.location.host + "/websocket";
- } else {
- wsUrl = "wss://tq.5000v.cn:443/websocket"
- }
- this.ws = new WebSocket(wsUrl); // 连接websocket
- this.ws.binaryType = "arraybuffer"; //传输的是 ArrayBuffer 类型的数据
- this.ws.onopen = (event) => {
- // 连接成功后
- console.log("websocket连接成功");
- // 连接成功后发送设备号
- this.websocketSend();
- };
-
- this.ws.onclose = (event) => {
- // 监听websocket断开
- console.log("websocket连接关闭");
- this.$emit('oNstate',false)//回传状态
- };
-
- this.ws.onmessage = (evt) => {
- // 收到websocket传来的信息
- console.log('websocket传来的信息',evt.data)
- this.$emit('oNstate',true)//回传状态
- if(typeof evt.data == 'string'){
- if(evt.data.indexOf(':1') > -1){
- // :1 代表接通 :0代表未接通
- this.record.start(); //开始录音
- this.receive();
- }else {
- if(this.sendingTimes < 3) {
- // 重发次数小于3 继续发
- // 间隔一秒发
- setTimeout(()=>{
- this.websocketSend();
- this.sendingTimes++
- }, 1000);
- }else{
- // 重发3次 提升无法连接 并且下发关闭指令 关闭连接
- // this.$message.error('设备无法链接');
- // this.$refs.uToast.show({
- // title: '设备无法链接',
- // type: "error",
- // });
- uni.showToast({
- title:'设备无法链接',
- duration: 2000,
- icon: 'none'
- });
- // this.closeIntercom();
- this.toIntercom(this.deviceId);
- this.sendingTimes = 0;
- }
- }
- } else {
- this.playAudio(evt.data);
- }
-
- };
-
- this.openIntercom = true;
- }else{
- // this.$refs.uToast.show({
- // title: res.desc,
- // type: "error",
- // });
- uni.showToast({
- title:res.desc,
- duration: 2000,
- icon: 'none'
- });
- }
-
- }
- this.$emit('oNstate',this.openIntercom)
- // #endif
- },
- websocketSend() {
- this.ws.send(this.deviceId);
- },
- receive() {
- // 对讲
- this.timeInte = setInterval(()=>{
- if(this.ws.readyState==1){ // ws进入连接状态,则每隔500毫秒发送一包数据
- console.log(this.record.getBlob());
- this.ws.send(this.record.getBlob()); //发送音频数据
- this.record.clear();//每次发送完成则清理掉旧数据
- }
- },200); //每隔200ms发送一次,定时器
- },
-
-
-
- playAudio(data) {
- // 收听
- let _this = this;
- var buffer = (new Response(data)).arrayBuffer();
- buffer.then(function(buf){
- var audioContext = new ( window.AudioContext || window.webkitAudioContext )();
- var fileResult = _this.addWavHeader(buf, '8000', '16', '1'); // 解析数据转码wav
- audioContext.decodeAudioData(fileResult, function(buffer) {
- _this.visualize(audioContext, buffer); // 播放
- });
- });
- },
-
- visualize(audioContext, buffer) {
- var audioBufferSouceNode = audioContext.createBufferSource();
- var analyser = audioContext.createAnalyser();
- //将信号源连接到分析仪
- audioBufferSouceNode.connect(analyser);
- //将分析仪连接到目的地(扬声器),否则我们将听不到声音
- analyser.connect(audioContext.destination);
- //然后将缓冲区分配给缓冲区源节点
- audioBufferSouceNode.buffer = buffer;
- //发挥作用
- if (!audioBufferSouceNode.start) {
- audioBufferSouceNode.start = audioBufferSouceNode.noteOn //在旧浏览器中使用noteOn方法
- audioBufferSouceNode.stop = audioBufferSouceNode.noteOff //在旧浏览器中使用noteOff方法
- };
- //如果有的话,停止前一个声音
- if (this.animationId !== null) {
- cancelAnimationFrame(this.animationId);
- }
- audioBufferSouceNode.start(0);
- this.audo.source = audioBufferSouceNode;
- this.audo.audioContext = audioContext;
- },
-
-
- //处理音频流,转码wav
- addWavHeader(samples, sampleRateTmp, sampleBits, channelCount) {
- var dataLength = samples.byteLength;
- var buffer = new ArrayBuffer(44 + dataLength);
- var view = new DataView(buffer);
- function writeString(view, offset, string){
- for (var i = 0; i < string.length; i++){
- view.setUint8(offset + i, string.charCodeAt(i));
- }
- }
- var offset = 0;
- /* 资源交换文件标识符 */
- writeString(view, offset, 'RIFF'); offset += 4;
- /* 下个地址开始到文件尾总字节数,即文件大小-8 */
- view.setUint32(offset, /*32*/ 36 + dataLength, true); offset += 4;
- /* WAV文件标志 */
- writeString(view, offset, 'WAVE'); offset += 4;
- /* 波形格式标志 */
- writeString(view, offset, 'fmt '); offset += 4;
- /* 过滤字节,一般为 0x10 = 16 */
- view.setUint32(offset, 16, true); offset += 4;
- /* 格式类别 (PCM形式采样数据) */
- view.setUint16(offset, 1, true); offset += 2;
- /* 通道数 */
- view.setUint16(offset, channelCount, true); offset += 2;
- /* 采样率,每秒样本数,表示每个通道的播放速度 */
- view.setUint32(offset, sampleRateTmp, true); offset += 4;
- /* 波形数据传输率 (每秒平均字节数) 通道数×每秒数据位数×每样本数据位/8 */
- view.setUint32(offset, sampleRateTmp * channelCount * (sampleBits / 8), true); offset +=4;
- /* 快数据调整数 采样一次占用字节数 通道数×每样本的数据位数/8 */
- view.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
- /* 每样本数据位数 */
- view.setUint16(offset, sampleBits, true); offset += 2;
- /* 数据标识符 */
- writeString(view, offset, 'data'); offset += 4;
- /* 采样数据总数,即数据总大小-44 */
- view.setUint32(offset, dataLength, true); offset += 4;
- function floatTo32BitPCM(output, offset, input){
- input = new Int32Array(input);
- for (var i = 0; i < input.length; i++, offset+=4){
- output.setInt32(offset,input[i],true);
- }
- }
- function floatTo16BitPCM(output, offset, input){
- input = new Int16Array(input);
- for (var i = 0; i < input.length; i++, offset+=2){
- output.setInt16(offset,input[i],true);
- }
- }
- function floatTo8BitPCM(output, offset, input){
- input = new Int8Array(input);
- for (var i = 0; i < input.length; i++, offset++){
- output.setInt8(offset,input[i],true);
- }
- }
- if(sampleBits == 16){
- floatTo16BitPCM(view, 44, samples);
- }else if(sampleBits == 8){
- floatTo8BitPCM(view, 44, samples);
- }else{
- floatTo32BitPCM(view, 44, samples);
- }
- return view.buffer;
- }
- }
- }
- </script>
|