写在前面
迩来在写一个web项目,必要实现web客户端之间的语音通话,期望能够借助webSocket全双工通讯的方式来实现,但是网上没有发现可以正确利用的代码。网上能找到的一个代码利用之后只能听到“嘀嘀嘀”的杂音
办理方案:利用Json来通报数据代替原有的二进制输入输出流
技能栈:VUE3、SpingBoot、WebSocket
Java后端代码
pom.xml
配置Maven所需的jar包
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-websocket</artifactId>
- </dependency>
复制代码 WebSocketConfig.java
webSocket配置类
- package com.shu.config;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.socket.server.standard.ServerEndpointExporter;
- @Configuration
- public class WebSocketConfig {
- /**
- * 注入ServerEndpointExporter,
- * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
- */
- @Bean
- public ServerEndpointExporter serverEndpointExporter() {
- return new ServerEndpointExporter();
- }
-
- }
复制代码 WebSocketAudioServer.java
webSocket实现类,其中roomId是语音谈天室的id,userId是发送语音的用户id
以是前端哀求加入webSocket时间的哀求样例应该是:ws://localhost:8080/audio/1/123这个哀求中1是roomId,123是userId,这里建议利用ws,一般来说ws对于http,wss对应https
VUE前端代码
audioChat.vue
这段代码是博主从自己的vue代码中截取出来的(原本的代码太多了),可能有些部分代码有函数没写上(如果有错的话贫苦大家在评论区指出,博主会及时修改)
注意事项
之前有博客利用二进制数据输入输出流来向后端传输数据,但是功能无法实现,厥后发现那位博主的数据并没有发成功,我直接在Java中利用Json来传输float数组数据,实现了语音通话功能。
- <template>
- <div class="play-audio">
- <button @click="startCall" ref="start">开始对讲</el-button>
- <button @click="stopCall" ref="stop">结束对讲</el-button>
- </div>
- </template>
- <script setup>
- // 语音聊天的变量
- const audioSocket = ref(null);
- let mediaStack;
- let audioCtx;
- let scriptNode;
- let source;
- let play;
- // 语音socket
- const connectAudioWebSocket = () => {
- let url = "ws://localhost:8080/audio/1/123"; //roomId:1 ,userId123
- audioSocket.value = new WebSocket(url); // 替换为实际的 WebSocket 地址
- audioSocket.value.onopen = () => {
- console.log("audioSocket connected");
- };
- audioSocket.value.onmessage = (event) => {
- // 将接收的数据转换成与传输过来的数据相同的Float32Array
- const jsonAudio = JSON.parse(event.data);
- // let buffer = new Float32Array(event.data);
- let buffer = new Float32Array(4096);
- for (let i = 0; i < 4096; i++) {
- // buffer.push(parseFloat(jsonAudio[i]));
- buffer[i] = parseFloat(jsonAudio[i]);
- }
- // 创建一个空白的AudioBuffer对象,这里的4096跟发送方保持一致,48000是采样率
- const myArrayBuffer = audioCtx.createBuffer(1, 4096, 16000);
- // 也是由于只创建了一个音轨,可以直接取到0
- const nowBuffering = myArrayBuffer.getChannelData(0);
- // 通过循环,将接收过来的数据赋值给简单音频对象
- for (let i = 0; i < 4096; i++) {
- nowBuffering[i] = buffer[i];
- }
- // 使用AudioBufferSourceNode播放音频
- const source = audioCtx.createBufferSource();
- source.buffer = myArrayBuffer;
- const gainNode = audioCtx.createGain();
- source.connect(gainNode);
- gainNode.connect(audioCtx.destination);
- var muteValue = 1;
- if (!play) {
- // 是否静音
- muteValue = 0;
- }
- gainNode.gain.setValueAtTime(muteValue, audioCtx.currentTime);
- source.start();
- };
- audioSocket.value.onclose = () => {
- console.log("audioSocket closed");
- };
- audioSocket.value.onerror = (error) => {
- console.error("audioSocket error:", error);
- };
- };
- // 开始对讲
- function startCall() {
- isInChannel.value = true;
- play = true;
- audioCtx = new AudioContext();
- connectAudioWebSocket();
- // 该变量存储当前MediaStreamAudioSourceNode的引用
- // 可以通过它关闭麦克风停止音频传输
- // 创建一个ScriptProcessorNode 用于接收当前麦克风的音频
- scriptNode = audioCtx.createScriptProcessor(4096, 1, 1);
- navigator.mediaDevices
- .getUserMedia({ audio: true, video: false })
- .then((stream) => {
- mediaStack = stream;
- source = audioCtx.createMediaStreamSource(stream);
- source.connect(scriptNode);
- scriptNode.connect(audioCtx.destination);
- })
- .catch(function (err) {
- /* 处理error */
- isInChannel.value = false;
- console.log("err", err);
- });
- // 当麦克风有声音输入时,会调用此事件
- // 实际上麦克风始终处于打开状态时,即使不说话,此事件也在一直调用
- scriptNode.onaudioprocess = (audioProcessingEvent) => {
- const inputBuffer = audioProcessingEvent.inputBuffer;
- // console.log("inputBuffer",inputBuffer);
- // 由于只创建了一个音轨,这里只取第一个频道的数据
- const inputData = inputBuffer.getChannelData(0);
- // 通过socket传输数据,实际上传输的是Float32Array
- if (audioSocket.value.readyState === 1) {
- // console.log("发送的数据",inputData);
- // audioSocket.value.send(inputData);
- let jsonData = JSON.stringify(inputData);
- audioSocket.value.send(jsonData);
- // stopCall();
- }
- };
- }
- // 关闭麦克风
- function stopCall() {
- isInChannel.value = false;
- play = false;
- mediaStack.getTracks()[0].stop();
- scriptNode.disconnect();
- if (audioSocket.value) {
- audioSocket.value.close();
- audioSocket.value = null;
- }
- }
- </script>
复制代码 关于Chrome或Edge浏览器报错
关于谷歌浏览器提示TypeError: Cannot read property ‘getUserMedia’ of undefined
办理方案:
1.网页利用https访问,服务端升级为https访问,配置ssl证书
2.利用localhost或127.0.0.1 进行访问
3.修改浏览器安全配置
在chrome浏览器中输入如下指令
- chrome://flags/#unsafely-treat-insecure-origin-as-secure
复制代码 开启 Insecure origins treated as secure
在下方输入栏内输入你访问的地点url,然后将右侧Disabled 改成 Enabled即可
浏览器会提示重启, 点击Relaunch即可
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |