跳到主要内容Web 对讲网关:基于 Netty 的 GB28181 音频推流实现 | 极客日志Java大前端java算法
Web 对讲网关:基于 Netty 的 GB28181 音频推流实现
一个轻量级 Web 对讲/广播网关系统,利用 Netty 实现从 Web 前端采集 PCM 音频,经服务端转码为 G.711A 并封装为 RTP 协议,最终通过 TCP 推送到 GB28181 国标设备。系统包含 Web 接入层、音频编解码层、媒体传输层及缓冲机制,支持高并发连接与标准安防协议对接。前端采用 JavaScript 实现二进制流传输以节省带宽,后端 Java 处理零拷贝与协议适配。
灰度发布15 浏览 Web 对讲网关:基于 Netty 的 GB28181 音频推流实现
介绍
轻量级的 Web 对讲/广播网关系统。利用 Netty 的高性能网络处理能力,实现了从 Web 前端采集音频,经过服务端转码与协议封装,最终以标准 GB28181/RTP 协议推送到前端设备(如摄像机、NVR 或国标平台)的核心功能。
核心功能模块
1.1. Web 接入与信令层 (Web Access Layer)
- 核心类:NettyServer.java, WebSocketHandler.java
- 功能:
- WebSocket 服务:监听 WebSocket 连接(路径 /ws_pcm),维持与 Web 前端的长连接。
- 信令交互:解析连接 URL 中的 deviceId,并在建立连接时调用外部接口(wvp-gb28181)通知设备准备接收语音流。
- 音频接收:接收前端发送的 Base64 编码的 PCM 音频数据。
1.2. 音频编解码层 (Audio Codec Layer)
- 核心类:G711Codec.java, AudioCodec.java
- 功能:
- 格式转换:实现了 PCM (16-bit 8000Hz) 与 G.711 A-law (8-bit 8000Hz) 之间的双向转换算法。这是安防领域最通用的音频编码标准。
- 海思头处理:具备识别和剔除海思私有音频头(Hisilicon Header)的能力,增强了对特定硬件的兼容性。
1.3. 媒体流传输层 (Media Transport Layer)
- 核心类:BroadcastServer.java, RtpPack.java
- 功能:
- RTP 封装:将 G.711A 音频数据封装为 RTP (Real-time Transport Protocol) 包。优化后的逻辑现在能正确处理 RTP 头、序列号(Sequence Number)和时间戳(Timestamp),符合标准 RFC 3550。
- TCP 流式发送:支持 RTP over TCP 模式(RFC 4571),每个广播会话启动一个独立的 TCP Server 进行推流。
- 动态端口:为每个会话动态分配监听端口,支持多路并发广播。
1.4. 数据缓冲机制 (Buffering Strategy)
- 实现方式:文件系统缓冲
- 流程:WebSocketHandler 将转码后的音频写入磁盘文件 -> BroadcastServer 轮询读取文件发送。
- 特点:利用磁盘文件作为简易的'消息队列',解耦了 Web 接收线程和 TCP 发送线程。
能力与数据流向
2.1. 核心能力
- Web 实时对讲:用户无需安装插件,通过浏览器即可向监控设备喊话。
- 标准协议对接:输出标准的 RTP 流,可对接海康、大华等主流安防设备及 GB28181 国标平台。
- 高并发基础:基于 Netty NIO 框架,具备处理高并发连接的潜力(注:目前的磁盘 IO 缓冲方式可能是瓶颈,建议未来优化为内存队列)
2.2. 数据处理全流程
采集 PCM (Blob) -> BinaryWebSocketFrame -> 转码 (PCM -> G.711A) -> 轮询读取 -> RTP 封装 (RtpPack) -> Web 用户 -> Netty Server -> WebSocketHandler -> 磁盘文件缓冲 -> BroadcastServer -> TCP Stream -> 摄像机/国标平台
前端实现 (web/):采集与二进制发送
前端核心逻辑位于 teach.realtime.encode_transfer_frame_pcm.js 中,主要负责音频的采集、切片和直接二进制传输。
- 音频采集与切片:
- 使用 Recorder.js 采集 8000Hz 16bit PCM 音频。
- RealTimeSendTry 函数将音频流切分为固定大小的帧(SendFrameSize,例如 3200 字节),确保发送频率稳定(约 100ms/帧)。
- 二进制发送 (优化点):
- 传输层:TransferUpload 函数中移除了 FileReader 转 Base64 的逻辑。
- WebSocket:直接调用 ws.send(blob) 发送 Blob 对象。浏览器底层会自动将其作为二进制帧(Binary Frame)处理,带宽节省约 33%。
- 配置:显式设置 ws.binaryType = 'arraybuffer' 以便正确接收服务端回显(如有)。
以下是前端核心实现(音频采集、切片与二进制发送)的关键代码片段,位于 teach.realtime.encode_transfer_frame_pcm.js 中:
音频参数配置与切片
这段代码定义了采样率(与后端对齐为 8000Hz)、位深(16bit)和发送帧大小(3200 字节,约 200ms),是保证音频流畅播放的基础。
var testSampleRate = 8000;
var testBitRate = 16;
var SendFrameSize = 3200;
实时切片与转码 (RealTimeSendTry)
RealTimeSendTry 函数负责将采集到的 PCM 数据流缓存起来,凑够一帧(SendFrameSize)后才进行处理。虽然源数据本身就是 PCM,但为了统一流程,这里使用了 Recorder.mock 将数据封装为 Blob 对象。
if(pcmLen == chunkSize){ pcmOK=true; }
new Blob([pcm.buffer], {type:"audio/pcm"})
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);
});
二进制发送逻辑 (TransferUpload)
var ws;
var TransferUpload=function(number, blobOrNull, duration, blobRec, isClose){
if(blobOrNull){
var blob = blobOrNull;
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);
}
}
};
后端接入 (src/):二进制帧处理与转码
后端核心逻辑位于 WebSocketHandler.java,负责接收二进制流并进行转码存储。
- Netty 处理器升级:
- 类定义改为 SimpleChannelInboundHandler,同时支持 FullHttpRequest(握手)和 WebSocketFrame(数据)。
- handleWebSocketFrame 方法增加了对 BinaryWebSocketFrame 的支持。
- 零拷贝读取:直接从 Netty 的 ByteBuf 中读取 PCM 字节流,避免了 Base64 解码的 CPU 消耗和内存分配。
- 音频转码:
- 调用优化后的 G711Codec.java 的 encodeToG711A 方法。
- 将 16bit PCM (128kbps) 压缩为 8bit G.711A (64kbps),符合安防标准。
- 文件缓冲:
- 将转码后的 G.711 数据写入本地磁盘({项目根目录}/recorders/{日期}/{端口}/),作为发送缓冲。
二进制帧处理 (Netty Inbound)
兼容 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);
}
}
}
音频转码与缓冲 (Transcode & Buffer)
收到 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);
}
}
连接握手与信令触发 (Handshake & Signaling)
在建立 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());
}
}
}
推流服务 (src/):RTP 封装与 TCP 推送
这部分由 BroadcastServer.java 和 RtpPack.java 负责,确保输出符合 GB28181 标准的流。
- RTP 封装 (已修复):
- RtpPack 类负责将 G.711 音频流封装为 RTP 包。
- 头部修正:现在能正确生成 12 字节 RTP 头 + 2 字节 TCP 长度头。
- 时间戳同步:根据数据长度动态计算 RTP 时间戳,确保播放连续、不卡顿、不变调。
- TCP 推流:
- BroadcastServer 启动一个独立的 TCP Server(动态端口)。
- 智能轮询 (优化点):循环读取缓冲目录下的新文件。如果暂无新文件,线程会短暂休眠(Thread.sleep),防止 CPU 空转。
- 读取到的数据经过 RTP 封装后,直接通过 Socket 发送给连接的摄像机或平台。
总结
该工程是一个轻量级的 Web 对讲/广播网关系统。它利用 Netty 的高性能网络处理能力,实现了从 Web 前端采集音频,经过服务端转码与协议封装,最终以标准 GB28181/RTP 协议推送到前端设备(如摄像机、NVR 或国标平台)的核心功能。
- 前端:PCM -> Blob -> WebSocket (Binary)
- 后端:BinaryFrame -> PCM -> G.711A -> File -> RTP -> TCP
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online