基于WebSocket实现的前端实时声⾳告警提醒功能
原理介绍:
项⽬需求 Web端播放实时⾳频流,折腾了两天后问题得以解决。记录下开发调试过程,⽅便后来者。⾸次想到是利⽤Audio标签,Audio标签可以直接播放MP3格式,服务端将实时⾳频流编码成WAV格式通过Http⽅式传给Web端即可。采⽤Audio Web API⽅式播放实时流会出现卡顿现象,以上⽅法⼀次性解码的数据可以连续播放,每次解码后要重新创建BufferSource,显⽽易见这种播放模式播放实时流效率很低,查阅了Audio Web API ⽂档 播放⽹络流似乎要利⽤,基于AudioWorkletProcessor的⾃定义节点,⽂档也给了⼀个简单的例⼦。另外⼀种可⾏的⽅法是服务端输出Rtmp⾳频流 通过video.js播放实际应该是⽤了flash。项⽬开始已经想到了这个⽅案可⾏只是觉得有些绕(需要将⾳频流封装后推送到rtmp server)。另外⼀个可⾏的⽅案就是利⽤H5 MSE实现,这个⽅案也有开源的库可以⽤,例如video.js 或flv.js这需要服务端将⾳频打包成flv格式,推送给Web前端,Web端接收到⾳频数据后调⽤flv.js进⾏播放。
前端代码如下所⽰:
<!DOCTYPE html>
<html xmlns:th="">
<head>
<meta charset="utf-8">
<title>Web Audio API 测试</title>
<script src="../static/js/reconnectingwebsocket.js"
th:href=@{/js/reconnectingwebsocket.js}></script>
</head>
<body>
<table>
<tr>
<td>参数:</td>
<td><input type="text" name="type"
id="type"></td>
<td>
<button onclick="onSendMessage()">发送消息</button>
</td>
<td>
<button onclick="onCloseMessage()">断开连接</button>
</td>
</tr>
</table>
</body>
<script>
/
/Web Audio API
var nextStartTime = 0;
var context = null;
try {
context = new (window.AudioContext || window.webkitAudioContext)();
} catch (e) {
alert('您当前的浏览器不⽀持Web Audio API ');
}
var objSocket = null;
var wsUrl = 'ws://localhost:8080/Audiowebsocket';
if ('WebSocket' in window) {
/
/objSocket = new ReconnectingWebSocket(wsUrl);
objSocket = new WebSocket(wsUrl);
} else {
alert('Not support websocket')
}
/**
WebSocket服务连接
*/
onOpen(evt)
};
};
onClose(evt)
};
onMessage(evt)
};
onError(evt)
};
function onOpen(evt) {
console.log("Connected to WebSocket server.");
}
function onClose(evt) {
console.log("Disconnected");
}
function onError(evt) {
console.log('Error occured: ' + evt.data);
}
function onMessage(evt) { //websocket返回数据信息处理
//console.log('Retrieved data from server: ' + evt.data);
var reader = new FileReader(); //⽂件阅读器
//解码
context.sult, function(buffer) {
console.log("decode success");
playSound(buffer);
}, function(e) {
console.log("decode failed");
"Error with decoding audio data" + e.err
});
}
//--------onMessage-----end-----------
}
//播放声⾳
function playSound(buffer) {
var source = ateBufferSource();
source.buffer = buffer;
if (nextStartTime == 0) {
nextStartTime = context.currentTime + buffer.duration / 2;  }
console.log("nextStartTime=" + nextStartTime);
source.start(nextStartTime);
//source.start(0);
nextStartTime += buffer.duration;
if (nextStartTime > 11) {
nextStartTime = 0;
}
}
//发送消息
function onSendMessage() {
var mess = ElementById("type").value;
objSocket.send(mess);
/* if (firm("发送消息:" + mess)) {
//alert("确定");
objSocket.send(mess);
return true;
} else {
//alert("取消");
//alert("取消");
return false;
} */
}
//关闭连接
function onCloseMessage() {
objSocket.close()
}
</script>
</html>
后端核⼼代码:
@Controller
@RequestMapping("/audio")
public class AudioController {
@RequestMapping("/a1")
public String websocketAduo() {
return "audio/WebAudio";
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import urrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.springframework.stereotype.Component;
slf4j.Slf4j;
@Slf4j
@ServerEndpoint(value = "/Audiowebsocket")
@Component
public class AudioWavWebsocket {
private static int onlineCount = 0;
private static CopyOnWriteArraySet<AudioWavWebsocket> webSocketSet = new CopyOnWriteArraySet<AudioWavWebsocket>();
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建⽴成功调⽤的⽅法
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this);
addOnlineCount();
File file = new File("./src/main/resources/mp3/05.wav");
File file = new File("./src/main/resources/mp3/05.wav");
try {
FileInputStream fis = new FileInputStream(file);
int readLength = 1024;
byte[] b = new byte[readLength];
int n;
int index = 0;
byte[] header = writeWavFileHeader(file);
int headLen = header.length;
ByteBuffer buffer = ByteBuffer.allocate(readLength + headLen);  while ((n = ad(b)) != -1) {
index++;
buffer.flip();
buffer.clear();
byte[] subHeader=ByteUtils.subArray(b, 0, 4);
if (!isWAVFileHeader(subHeader)) {
log.info("the WAV ");
buffer.put(header, 0, headLen);
buffer.put(b, 0, n);
} else {
log.info("the WAV ");
buffer.put(b, 0, n);
}
buffer.flip();
log.info("Send消息:{}-{}", index, n);
sendMessage(buffer);
}
fis.close();
} catch (IOException e) {
<("IO异常");
}
}
/**
* ⾳频流判断
* @author: tompai
* @createTime: 2020年12⽉26⽇下午11:17:21
* @history:
* @param header
* @return boolean
*/
private boolean isWAVFileHeader(byte[] header) {
// WAV⽂件开始标志//
byte[] head = {'R','I','F','F'}; // RIFF
return ByteUtils.equals(header, head);
}
/**
* 连接关闭调⽤的⽅法
*/
@OnClose
public void onClose() {
subOnlineCount(); // 在线数减1
}
/**
* 收到客户端消息后调⽤的⽅法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
public void onMessage(String message, Session session) {
log.info("来⾃客户端的消息:" + message);
// 发消息
for (AudioWavWebsocket item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发⽣错误时调⽤
*/
@OnError
public void onError(Session session, Throwable error) {
<("发⽣错误");
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
log.info("来⾃后端的消息:{}", message);
BasicRemote().sendText(message);
}
public void sendMessage(ByteBuffer buffer) throws IOException {
// log.info("来⾃后端的消息:{}", buffer.);
BasicRemote().sendBinary(buffer);
}
/**
* 发⾃定义消息
*/
public static void sendInfo(String message) throws IOException {
for (AudioWavWebsocket item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
<("发⽣错误");
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {websocket和socket
}
public static synchronized void subOnlineCount() {
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public static CopyOnWriteArraySet<AudioWavWebsocket> getWebSocketSet() {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。