Web 对讲网关:基于 Netty 的 GB28181 音频推流实现
介绍
轻量级的 Web 对讲/广播网关系统。利用 Netty 的高性能网络处理能力,实现了从 Web 前端采集音频,经过服务端转码与协议封装,最终以标准 GB28181/RTP 协议推送到前端设备(如摄像机、NVR 或国标平台)的核心功能。
一个轻量级 Web 对讲/广播网关系统,利用 Netty 实现从 Web 前端采集 PCM 音频,经服务端转码为 G.711A 并封装为 RTP 协议,最终通过 TCP 推送到 GB28181 国标设备。系统包含 Web 接入层、音频编解码层、媒体传输层及缓冲机制,支持高并发连接与标准安防协议对接。前端采用 JavaScript 实现二进制流传输以节省带宽,后端 Java 处理零拷贝与协议适配。
轻量级的 Web 对讲/广播网关系统。利用 Netty 的高性能网络处理能力,实现了从 Web 前端采集音频,经过服务端转码与协议封装,最终以标准 GB28181/RTP 协议推送到前端设备(如摄像机、NVR 或国标平台)的核心功能。
采集 PCM (Blob) -> BinaryWebSocketFrame -> 转码 (PCM -> G.711A) -> 轮询读取 -> RTP 封装 (RtpPack) -> Web 用户 -> Netty Server -> WebSocketHandler -> 磁盘文件缓冲 -> BroadcastServer -> TCP Stream -> 摄像机/国标平台
前端核心逻辑位于 teach.realtime.encode_transfer_frame_pcm.js 中,主要负责音频的采集、切片和直接二进制传输。
以下是前端核心实现(音频采集、切片与二进制发送)的关键代码片段,位于 teach.realtime.encode_transfer_frame_pcm.js 中:
这段代码定义了采样率(与后端对齐为 8000Hz)、位深(16bit)和发送帧大小(3200 字节,约 200ms),是保证音频流畅播放的基础。
var testSampleRate = 8000; // 采样率必须与后端 G711Codec 一致
var testBitRate = 16; // 位深 16 位
// 每次发送指定二进制数据长度的数据帧,单位字节。
// 16 位 8000hz 的 pcm 1 秒有:8000hz * 16 位 / 8 比特 = 16000 字节的数据
// 配置 3200 字节则每秒发送大约 5 次 (200ms/帧)
var SendFrameSize = 3200;
RealTimeSendTry 函数负责将采集到的 PCM 数据流缓存起来,凑够一帧(SendFrameSize)后才进行处理。虽然源数据本身就是 PCM,但为了统一流程,这里使用了 Recorder.mock 将数据封装为 Blob 对象。
// ... 从缓冲中切出一帧数据 ...
// 满一帧了,清除已消费掉的缓冲
if(pcmLen == chunkSize){ pcmOK=true; }
// ...
// 16 位 pcm 格式可以不经过 mock 转码,直接发送
new Blob([pcm.buffer], {type:"audio/pcm"}) // 但为了通用性,这里使用 Recorder mock 接口统一封装为 Blob
var recMock=Recorder({type:"pcm",sampleRate:testSampleRate, bitRate:testBitRate});
recMock.mock(pcm,pcmSampleRate);
recMock.stop(function(blob,duration){
TransferUpload(number,blob,duration,recMock,false);
RealTimeSendTry([],0, isClose);
});
var ws;
var TransferUpload=function(number, blobOrNull, duration, blobRec, isClose){
if(blobOrNull){
var blob = blobOrNull;
//*********发送方式二:Blob 二进制发送 (推荐) ***************
if(!ws){
ws = new WebSocket('ws://127.0.0.1:7211/ws_pcm?deviceId=填入设备 id');
ws.binaryType = 'arraybuffer';
ws.onopen = evt=>{ console.log("ws talk open (Binary Mode)"); }
// ... 错误处理与关闭逻辑 ...
}
if(ws && ws.readyState === WebSocket.OPEN){
ws.send(blob);
console.log("Sent binary blob, size: "+ blob.size);
}
}
};
后端核心逻辑位于 WebSocketHandler.java,负责接收二进制流并进行转码存储。
兼容 Text 帧(Base64)和 Binary 帧(Raw PCM),并实现零拷贝读取二进制数据。
public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws IOException {
byte[] pcmData = null;
if(frame instanceof TextWebSocketFrame){
String text = ((TextWebSocketFrame) frame).text();
pcmData = Base64Decoder.decode(text);
} else if(frame instanceof BinaryWebSocketFrame){
// 二进制模式 (性能更优)
ByteBuf content = frame.content();
pcmData = new byte[content.readableBytes()];
content.readBytes(pcmData);
}
if(pcmData != null && pcmData.length > 0){
processPcmData(ctx, pcmData);
}
}
}
收到 PCM 数据后,立即进行 G.711A 转码,并将结果写入磁盘文件作为缓冲。这是连接 Web 前端与 RTP 发送端的关键桥梁。
private void processPcmData(ChannelHandlerContext ctx, byte[] pcmData) throws IOException {
byte[] g711a = G711Codec.encodeToG711A(pcmData);
String dirStr = getRecorderDir(server.getPort());
FileUtil.mkdir(dirStr);
String fileName = System.currentTimeMillis() + ".pcm";
try(FileOutputStream out = new FileOutputStream(dirStr + fileName, false)){
out.write(g711a);
}
}
在建立 WebSocket 连接时,解析 URL 参数并触发外部 SIP 信令(通知摄像机/平台准备接收)。
private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request){
server = new BroadcastServer();
server.start();
Map<String,String> paramMap = getUrlParams(request.uri());
String deviceId = paramMap.get("deviceId");
if(deviceId != null){
online(deviceId, ctx.channel());
try{
HttpRequest.get("https://127.0.0.1:8843/api/play/broadcast/"+ deviceId).execute(true);
} catch(Exception e){
System.err.println("调用 WVP 接口失败:"+ e.getMessage());
}
}
}
这部分由 BroadcastServer.java 和 RtpPack.java 负责,确保输出符合 GB28181 标准的流。
该工程是一个轻量级的 Web 对讲/广播网关系统。它利用 Netty 的高性能网络处理能力,实现了从 Web 前端采集音频,经过服务端转码与协议封装,最终以标准 GB28181/RTP 协议推送到前端设备(如摄像机、NVR 或国标平台)的核心功能。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online