wvp-GB28181-pro 国标语音对讲(Broadcast/Talk)原理与实战:从 ffmpeg 推流测试到网页发起
2026/9/16 14:58:41 网站建设 项目流程

wvp-GB28181-pro 国标语音对讲(Broadcast/Talk)原理与实战:从 ffmpeg 推流测试到网页发起

【免费下载链接】wvp-GB28181-pro基于GB28181-2016、部标808、部标1078标准实现的开箱即用的网络视频平台。自带管理页面,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联,支持将普通摄像机/直播流/直播推流转国标共享到国标平台。项目地址: https://gitcode.com/GitHub_Trending/wv/wvp-GB28181-pro

本文围绕 wvp-GB28181-pro 的语音对讲(Audio Broadcast / Talk)能力展开:先讲清国标 28181 中 broadcast 与 talk 两种模式的差异,再用两张原理图(ffmpeg 测试、网页测试)说明整条链路中 FFMPEG / 前端页面 / WVP / ZLMediaKit / 设备五者的协作关系,并深入仓库源码,逐段印证"推流 → 事件触发 → SIP 命令 → 反向推流"的实际调用链,最后给出 ffmpeg 快速测试命令与公网/局域网网页对讲的落地要点。读完本文,你可以独立完成:① 用一条 ffmpeg 命令把音频文件喊话到国标摄像头;② 理解 WVP 如何监听 ZLM 流事件并自动发起语音对讲;③ 判断某台设备能否公网对讲(收流方式决定的硬性限制)。

1. 背景:国标 28181 中 broadcast 与 talk 两种语音模式

国标 28181-2016 中,语音能力分为两种模式:

  • broadcast(广播)模式:服务端把音频单向上行传送到设备端播放。它本身是单向的,如需"双向对讲"(设备侧拾音回传)必须再叠加一路视频/音频点播来取回设备侧的声音;
  • talk(对讲)模式:协议上支持双向,但 wvp 的实际处理逻辑只实现了与 broadcast 相同的"把音频传递给设备"这一半,这样两种模式可以复用同一套处理逻辑。

不同厂商(海康、大华、宇视、各执法记录仪厂商)对两种模式的支持程度差异很大,语音对讲是所有 GB28181 功能中兼容性适配问题最多的一项。由于 talk 模式在 GB28181-2022 中已被移除,工程实践中以 broadcast 为准。

1.1 broadcast 模式的 SIP 时序

与点播流程最关键的区别在于:这里的 INVITE 消息是由设备发给 WVP 的,而不是 WVP 发给设备。完整时序如下:

@startuml "WVP-PRO" -> "设备": 语音广播通知(Notify/Broadcast) "WVP-PRO" <-- "设备": 200OK "WVP-PRO" <- "设备": 语音广播应答(响应携带SDP) "WVP-PRO" --> "设备": 200OK "WVP-PRO" <- "设备": Invite "WVP-PRO" --> "设备": 200OK(携带SDP消息体) "WVP-PRO" <-- "设备": ACK "ZLMediaKit" -> "设备": 向设备发送语音流 @enduml

流程要点:

  1. WVP 先向设备下发Broadcast类型的 Notify 消息,声明"我要对你发起语音广播";
  2. 设备回 200 OK 确认,随后主动发回广播应答;
  3. 设备反向 INVITE 平台,其 SDP 中携带设备侧的收流 IP/端口与传输方式(UDP / TCP 被动 / TCP 主动);
  4. WVP 按 INVITE 协商惯例回复 200 OK(SDP 应答)+ ACK 完成会话建立;
  5. 此后由 ZLMediaKit 把收到的语音流按协商结果推送到设备收流端口。

因为收流方式(UDP/TCP 被动/TCP 主动)由设备在 SDP 中单方面决定,这直接引出了第 4 节的公网对讲限制。

2. 语音对讲的两种触发链路:ffmpeg 测试 与 网页测试

WVP 的语音对讲设计为"推流即触发":客户端不需要调用任何对讲专用接口,只要把符合命名规则的语音流推到 ZLMediaKit,WVP 就会监听到流事件,自动向设备发起整条 SIP 对讲流程。触发入口有两种:

2.1 使用 ffmpeg 测试语音对讲(原理图)

@startuml "FFMPEG" -> "ZLMediaKit": 推流到zlm "WVP-PRO" <- "ZLMediaKit": 通知收到语音对讲推流,携带设备和通道信息 "WVP-PRO" -> "设备": 开始语音对讲 "WVP-PRO" <-- "设备": 语音对讲建立成功,携带收流端口 "WVP-PRO" -> "ZLMediaKit": 通知zlm将流推送到设备收流端口 "ZLMediaKit" -> "设备": 向设备推流 @enduml

六步链路:

  1. FFMPEG 把转码后的 G.711 音频流推到 ZLM;
  2. ZLM 通过 hook 通知 WVP"收到了一路 broadcast 推流",流名中自带设备与通道信息({设备国标编号}_{通道国标编号});
  3. WVP 向设备下发 Broadcast 通知、接收设备反向 INVITE 并协商完成;
  4. 协商成功后 WVP 拿到设备的收流端口;
  5. WVP 通知 ZLM 把这一路流转发(forward)到设备收流端口;
  6. ZLM 开始向设备推流,设备喇叭出声。

如果听到设备播放了你推送的音频,即意味着全链路打通。整个过程只需要推流,不需要调用任何对讲接口。

2.2 使用网页测试语音对讲(原理图)

@startuml "前端页面" -> "WVP-PRO": 请求推流地址 "前端页面" <-- "WVP-PRO": 返回推流地址 "前端页面" -> "ZLMediaKit": 使用webrtc推流到zlm,以下过程相同 "WVP-PRO" <- "ZLMediaKit": 通知收到语音对讲推流,携带设备和通道信息 "WVP-PRO" -> "设备": 开始语音对讲 "WVP-PRO" <-- "设备": 语音对讲建立成功,携带收流端口 "WVP-PRO" -> "ZLMediaKit": 通知zlm将流推送到设备收流端口 "ZLMediaKit" -> "设备": 向设备推流 @enduml

与 ffmpeg 链路的唯一差别是流的生产端换成了前端页面:前端先向 WVP 请求推流地址,然后使用 WebRTC 把麦克风采集的音频推到 ZLM(ZLM 侧同样得到 app 为broadcast、流名为{deviceId}_{channelId}的流),从 ZLM 通知 WVP 那一刻开始,后续过程与 ffmpeg 完全相同。也就是说WVP 侧的对讲处理逻辑与"谁来推流"无关,它只认流名里的设备和通道编号——这正是"推流即触发"设计的价值:任何能向 ZLM 推 G.711 音频流的客户端(自研 App、摄像头、网页 WebRTC)都天然支持。

注意:浏览器采集麦克风/音频要求页面必须是 https(或 localhost),因此网页方式发起语音对讲时,必须给 WVP 和 ZLM 配置证书启用 https,这一点详见第 5 节。

3. 源码印证:推流如何自动触发 SIP 对讲

以下所有代码路径均相对仓库根目录,可逐段对照第 2 节的原理图。

3.1 流名的约定:broadcast/{deviceId}_{channelId}

推流地址中的 app 名是触发开关。MediaStreamUtil.java 定义了三个保留 app 名与判定方法:

public final static String GB28181_TALK = "talk"; public final static String GB28181_BROADCAST = "broadcast"; public static boolean isBroadcast(String app, String streamId) { return GB28181_BROADCAST.equals(app); }

即:推到 ZLM 的地址形如.../broadcast/{设备国标编号}_{通道国标编号}时,WVP 会把它识别为语音广播流;流名必须以_恰好分成设备编号与通道编号两段,否则不触发对讲。

3.2 流到达事件:MediaArrivalEvent监听

ZLM 收到流后通过 hook 通知 WVP,最终在 PlayServiceImpl.java 的onApplicationEvent(MediaArrivalEvent)中被消费,这段代码就是原理图中第 2、3 步的实现:

@Async @EventListener public void onApplicationEvent(MediaArrivalEvent event) { if (MediaStreamUtil.GB28181_BROADCAST.equals(event.getApp()) || MediaStreamUtil.GB28181_TALK.equals(event.getApp())) { if (event.getStream().indexOf("_") > 0) { String[] streamArray = event.getStream().split("_"); if (streamArray.length == 2) { String deviceId = streamArray[0]; String channelId = streamArray[1]; Device device = deviceService.getDeviceByDeviceId(deviceId); DeviceChannel channel = deviceChannelService.getOneForSource(deviceId, channelId); ... if (MediaStreamUtil.GB28181_BROADCAST.equals(event.getApp())) { if (audioBroadcastManager.exit(channel.getId())) { stopAudioBroadcast(device, channel); // 同一通道重复推流:先停旧会话 } // 开启语音对讲通道 audioBroadcastCmd(device, channel, event.getMediaServer(), event.getApp(), event.getStream(), 60, false, ...); } } } } }

对应原理图可读出三个关键行为:

  • 设备/通道校验:从流名解析出deviceId/channelId后先查设备与通道记录,查不到只打日志不报错([语音对讲/喊话] 未找到设备/通道),所以推流失败排查第一步是确认流名中的国标编号与 WVP 中注册的完全一致;
  • 幂等与重入:同一通道已存在对讲会话时(audioBroadcastManager.exit(...))会先stopAudioBroadcast再重建,因此"连续推流"是安全的,不会叠加出多个 INVITE 会话;
  • 60 秒超时参数audioBroadcastCmd(..., 60, ...)中传入 60 秒,作为会话建立的超时兜底。

流离开时同样有对称处理:PlayServiceImpl.java 监听MediaDepartureEvent,app 为broadcast/talk时自动stopAudioBroadcast结束对讲——即ffmpeg 停止推流 = 自动结束对讲,无需手动停止。

3.3 Broadcast SIP 消息体的构造

第 1 节时序图中第一帧"语音广播通知"的实际报文在 SIPCommander.java:

public void audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { StringBuffer broadcastXml = new StringBuffer(200); String charset = device.getCharset(); broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); broadcastXml.append("<Notify>\r\n"); broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n"); broadcastXml.append("<TargetID>" + channelId + "</TargetID>\r\n"); broadcastXml.append("</Notify>\r\n"); Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport())); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); }

这是一条标准 MESSAGE 请求,正文为<Notify><CmdType>Broadcast</CmdType>...</Notify>的国标 XML;SourceID为平台国标编号(取自 SIP 配置),TargetID为通道国标编号。命令发送成功后,WVP 在 PlayServiceImpl.audioBroadcastCmd 中创建AudioBroadcastCatch(状态Ready)登记会话,记录设备、通道、所在流媒体节点、app/stream 名,作为后续设备反向 INVITE 到达时的匹配凭证——设备收到 Broadcast 通知后反向 INVITE 平台,平台按登记信息回复 200 OK(SDP)并拿到收流端口,随后指令 ZLM 转发,完成原理图最后两步。

3.4 HTTP API 入口与多节点路由

除"推流即触发"外,WVP 也提供了显式的广播 API,定义在 PlayController.java:

  • GET/POST /api/play/broadcast/{deviceId}/{channelId}:语音广播命令,可选broadcastMode参数决定使用broadcast还是talk作为 app 名(见 PlayServiceImpl.audioBroadcast,broadcastMode缺省为true),返回AudioBroadcastResult(含 app、stream、编码格式 G.711 等),客户端拿到后即可按返回的 app/stream 拼出推流地址去推流;
  • GET/POST /api/play/broadcast/stop/{deviceId}/{channelId}:显式停止广播(校验设备与通道存在后调用stopAudioBroadcast)。

在分布式部署(多节点)下,该 API 还会通过 Redis RPC 把命令转发到设备实际注册所在的节点执行(redisRpcPlayService.audioBroadcast(...)),保证命令与设备会话在同节点处理。

3.5 推流鉴权:sign = md5(pushKey)

原理图中"推流到 zlm"这一步并非匿名放行。ZLM 收到推流后会回调 WVP 做鉴权,逻辑在 MediaServiceImpl.java:

log.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)"); String sign = paramMap.get("sign"); ... boolean hasAuthority = userService.checkPushAuthority(callId, sign);

即推流 URL 必须携带?sign={md5(pushKey)},其中 pushKey 是 WVP 用户表中配置的推流密钥(管理界面"用户 API Key"处可查看/修改)。broadcast 流与普通推流共用这套鉴权,这也是第 4 节 ffmpeg 测试命令中sign参数的来源。

4. 使用条件与限制(决定能否公网对讲的关键)

第 1 节已指出:收流方式由设备在 SDP 中单方面指定。由此产生一个硬性限制——

  • 大部分海康设备只支持 UDP 收流:设备在 SDP 中填的是自己内网的 IP:Port。若 WVP 部署在公网而设备在 NAT 后的内网,WVP 无法向这个内网 IP 发包,发流必然失败,公网对讲做不起来(新版海康设备正在改善此问题);
  • 大华以及许多执法记录仪厂商支持 TCP 主动(由设备主动连平台)方式收流:连接方向反过来,内网设备可以主动打到公网平台,公网对讲即可实现。

因此选型或排障时,先抓一次设备反向 INVITE 的 SDP,确认其中的传输方式与 IP 归属,再判断该设备在你的网络拓扑下能否对讲。这一判断与推流端(ffmpeg / 网页 / App)无关,是设备侧能力问题。

5. 使用 ffmpeg 快速测试(可复制命令)

网页方式要求 https(浏览器采集音频的硬性规定),测试阶段最简单的路径是用 ffmpeg 模拟语音流:把任意音频文件转码为 G.711(A-law)、8kHz、单声道,按约定 app/流名推到 ZLM,即可把音频文件喊话到摄像头。

命令格式:

ffmpeg -re -i {音频文件} -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp 'rtsp://{zlm的IP}:{zlm的RTSP端口}/broadcast/{设备国标编号}_{通道国标编号}?sign={md5(pushKey)}'

示例:

ffmpeg -re -i test.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp 'rtsp://192.168.1.3:22554/broadcast/34020000001320000001_34020000001320000001?sign=41db35390ddad33f83944f44b8b75ded'

参数说明:

参数含义
-re按原始速率逐帧读取,模拟实时语音(不加会把整段音频瞬间推完)
-acodec pcm_alaw音频编码 A-law,即国标 G.711,与设备协商的编码一致
-ar 8000 -ac 18kHz 采样、单声道,国标语音要求
rtsp://{zlm的IP}:{RTSP端口}ZLM 的 RTSP 推流服务,端口见 ZLM 配置
/broadcast/{设备}_{通道}app 必须是broadcast(或talk),流名两段国标编号用_连接
?sign={md5(pushKey)}推流鉴权,pushKey 为 WVP 用户表中的推流密钥

执行后对照第 2.1 节原理图观察:ffmpeg 推流成功 → ZLM hook 通知 WVP → WVP 向设备下发 Broadcast → 设备反向 INVITE 协商 → ZLM 转发到设备端口。设备喇叭出声即成功;此过程推流即可,不需要调用任何接口。停止 ffmpeg 推流,WVP 侧MediaDepartureEvent会自动结束对讲会话。

6. 生产环境网页发起语音对讲

生产环境下网页发起对讲需要解决两件事:

  1. 很多设备不支持公网对讲(第 4 节的 UDP 收流限制)——先确认目标设备的收流方式;
  2. https 证书——浏览器采集音频要求页面为 https,而公网与局域网获取证书的方式不同。

6.1 公网使用

公网环境直接使用证书厂商或云服务器厂商提供的正规证书即可,将证书分别配置到 WVP(Web 服务)与 ZLM(RTSP/WebRTC 服务)后即可用网页发起。

6.2 局域网使用(mkcert 自签名证书)

局域网需要为 WVP 和 ZLM 生成自签名证书。Linux 下推荐用 mkcert(可生成本机受信任的自签名证书):

# 1. 安装证书生成工具(以 v1.4.4 linux-amd64 为例) ./mkcert-v1.4.4-linux-amd64 -install # 2. 为局域网内各主机 IP 生成 pem 证书 ./mkcert-v1.4.4-linux-amd64 局域网IP 局域网IP2 局域网IP3

执行后得到两个文件:*-key.pem(私钥)与*.pem(证书)。WVP 侧配置这两个文件即可加载证书。

ZLM 侧证书格式稍有不同,需要把证书与私钥拼成单文件:

cat *.pem *-key.pem > ./zlm.pem

ZLM 使用证书有两种方式:

  1. 替换 ZLM 安装目录下的default.pem:删除原文件,把zlm.pem重命名为default.pem
  2. 在启动 ZLM 时追加参数-s zlm.pem指定证书文件。

完成后,浏览器访问 https 页面采集麦克风,按第 2.2 节网页原理图流程(先向 WVP 请求推流地址,再用 WebRTC 推到 ZLM 的broadcast/{设备}_{通道}),即可在局域网内实现网页语音对讲。

7. 小结:一张表看懂全链路

环节责任方仓库中对应实现
生产 G.711 语音流ffmpeg / 前端 WebRTC推流地址/broadcast/{deviceId}_{channelId}?sign=md5(pushKey)
推流鉴权WVP 响应 ZLM hookMediaServiceImpl.java
流事件监听并解析设备/通道WVPPlayServiceImpl.java
下发 Broadcast 通知(MESSAGE + XML)WVP → 设备SIPCommander.java
接收设备反向 INVITE 协商收流端口WVP会话登记于AudioBroadcastManager,见audioBroadcastCmd(PlayServiceImpl.java)
把流转发到设备收流端口ZLM由 WVP 协商完成后指令执行
停止对讲推流断开自动触发 / HTTP APIMediaDepartureEvent监听 +/api/play/broadcast/stop/{deviceId}/{channelId}(PlayController.java)

核心结论:WVP 的语音对讲是"流驱动"架构——设备/通道身份编码在流名里,SIP 协商方向由设备主导,公网可用性取决于设备收流方式,客户端形态(ffmpeg、App、网页 WebRTC)则可自由替换。更多背景可参考仓库文档 语音对讲 与 级联语音喊话流程。

【免费下载链接】wvp-GB28181-pro基于GB28181-2016、部标808、部标1078标准实现的开箱即用的网络视频平台。自带管理页面,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联,支持将普通摄像机/直播流/直播推流转国标共享到国标平台。项目地址: https://gitcode.com/GitHub_Trending/wv/wvp-GB28181-pro

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询