跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java算法

Web 对讲网关:基于 Netty 的 GB28181 音频推流实现

一个轻量级 Web 对讲/广播网关系统,利用 Netty 实现从 Web 前端采集 PCM 音频,经服务端转码为 G.711A 并封装为 RTP 协议,最终通过 TCP 推送到 GB28181 国标设备。系统包含 Web 接入层、音频编解码层、媒体传输层及缓冲机制,支持高并发连接与标准安防协议对接。前端采用 JavaScript 实现二进制流传输以节省带宽,后端 Java 处理零拷贝与协议适配。

灰度发布发布于 2026/3/21更新于 2026/8/1750 浏览

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. 核心能力
  1. Web 实时对讲:用户无需安装插件,通过浏览器即可向监控设备喊话。
  2. 标准协议对接:输出标准的 RTP 流,可对接海康、大华等主流安防设备及 GB28181 国标平台。
  3. 高并发基础:基于 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; // 采样率必须与后端 G711Codec 一致
var testBitRate = 16; // 位深 16 位
// 每次发送指定二进制数据长度的数据帧,单位字节。
// 16 位 8000hz 的 pcm 1 秒有:8000hz * 16 位 / 8 比特 = 16000 字节的数据
// 配置 3200 字节则每秒发送大约 5 次 (200ms/帧)
var SendFrameSize = 3200;
实时切片与转码 (RealTimeSendTry)

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);
});
二进制发送逻辑 (TransferUpload)
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);
        }
    }
};

后端接入 (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

参考代码仓库:https://github.com/clpz299/gb28181-intercom-example

目录

  1. Web 对讲网关:基于 Netty 的 GB28181 音频推流实现
  2. 介绍
  3. 核心功能模块
  4. 1.1. Web 接入与信令层 (Web Access Layer)
  5. 1.2. 音频编解码层 (Audio Codec Layer)
  6. 1.3. 媒体流传输层 (Media Transport Layer)
  7. 1.4. 数据缓冲机制 (Buffering Strategy)
  8. 能力与数据流向
  9. 2.1. 核心能力
  10. 2.2. 数据处理全流程
  11. 前端实现 (web/):采集与二进制发送
  12. 音频参数配置与切片
  13. 实时切片与转码 (RealTimeSendTry)
  14. 二进制发送逻辑 (TransferUpload)
  15. 后端接入 (src/):二进制帧处理与转码
  16. 二进制帧处理 (Netty Inbound)
  17. 音频转码与缓冲 (Transcode & Buffer)
  18. 连接握手与信令触发 (Handshake & Signaling)
  19. 推流服务 (src/):RTP 封装与 TCP 推送
  20. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • EasyProfiler C++ 性能分析库配置与使用指南
  • AI 大模型时代下易受影响的 20 种职业分析
  • 初识 AI 大语言模型:概念、原理与能力
  • RTX 4090 加速国产 AIGC 视频生成:腾讯混元与阿里通义万相
  • Python 爬虫入门:批量下载网页图片实战
  • Python 合并多个 PDF 文件:完整指南与实践
  • OpenClaw WebSocket Channel 开发实战:从零打造自定义 AI 通信通道
  • GitHub Copilot 代理功能与 VS Code 集成指南
  • LLM 申请 Hugging Face 访问令牌(以 Meta-Llama-3.1-8B-Instruct 为例)
  • Vite7+Vue3+DeepSeek-R1 流式 AI 聊天系统搭建
  • 10 款开源 AI 视频工具:免配置开箱即用
  • 解决 VsCode 远程 SSH 下 Copilot 使用 Claude 模型的代理配置问题
  • Ubuntu 下载、安装及软件管理详解
  • Python 与 PyCharm 环境搭建及安装指南
  • Flutter for OpenHarmony 底部导航与 TabBar 多页切换
  • 从敏捷到生成式:AIGC 重塑软件测试全流程
  • OmniSteward:基于 LLM Agent 的智能家居与电脑控制方案
  • Android Studio 安装及核心组件配置指南(SDK、JDK、Gradle)
  • Trae IDE 实战:从零开发 AI Chatbot 应用
  • Chatbot UI 实战指南:基于 Web Components 构建高可用对话界面

相关免费在线工具

  • 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