十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

wvp-GB28181-pro 国标点播全流程解析:从 SIP Invite 到 ZLMediaKit 收流的事件驱动机制

wvp-GB28181-pro 国标点播全流程解析:从 SIP Invite 到 ZLMediaKit 收流的事件驱动机制 wvp-GB28181-pro 国标点播全流程解析从 SIP Invite 到 ZLMediaKit 收流的事件驱动机制【免费下载链接】wvp-GB28181-pro基于GB28181-2016、部标808、部标1078标准实现的开箱即用的网络视频平台。自带管理页面支持NAT穿透支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联支持将普通摄像机/直播流/直播推流转国标共享到国标平台。项目地址: https://gitcode.com/GitHub_Trending/wv/wvp-GB28181-pro本文基于 wvp-GB28181-pro 的点播流程文档与仓库源码完整解析国标GB/T 28181实时点播的十步信令流程SIP Invite/200OK/Ack 会话建立、SDP 中sPlay/ySSRC字段的构造与校验、ZLMediaKit 收流 Hook 事件回调、流地址下发以及无人观看时的 Bye 会话释放。读完后你将能够对照 PlayController 与 PlayServiceImpl 逐环节定位点播超时问题并理解收流模式UDP/TCP 主动/TCP 被动、SSRC 修正等关键实现细节。一、点播流程总览十个环节的时序官方文档 doc/_content/theory/play.md 给出的点播时序如下原文档为 PlantUML 时序图WEB用户 - WVP-PRO : 1. 发起点播请求 WVP-PRO - 设备 : 2. Invite(携带SDP消息体) 设备 - WVP-PRO : 3. 200OK(携带SDP消息体) WVP-PRO - 设备 : 4. Ack 设备 - ZLMediaKit: 5. 发送实时流 ZLMediaKit- WVP-PRO : 6. 流改变事件 WVP-PRO - WEB用户 : 7. 回复流播放地址携带流地址 ZLMediaKit- WVP-PRO : 8. 无人观看事件 WVP-PRO - 设备 : 9. Bye消息 设备 - WVP-PRO : 10. 200OK文档同时给出了文字版流程描述这里完整保留并逐条对应到源码用户从网页或调用接口发起点播请求WVP-PRO 向摄像机发送 Invite 消息消息头域中携带 Subject 字段表明点播的视频源 ID、发送方媒体流序列号、ZLMediaKit 接收流使用的 IP、端口号、接收端媒体流序列号等参数SDP 消息体中s字段为Play代表实时点播y字段描述 SSRC 值f字段描述媒体参数摄像机向 WVP-PRO 回复 200OK消息体中描述了媒体流发送者发送媒体流的 IP、端口、媒体格式、SSRC 字段等内容WVP-PRO 向设备回复 Ack会话建立成功设备向 ZLMediaKit 发送实时流ZLMediaKit 向 WVP-PRO 发送流改变事件WVP-PRO 向 WEB 用户回复播放地址ZLMediaKit 向 WVP 发送流无人观看事件WVP-PRO 向设备回复 Bye结束会话设备回复 200OK会话结束成功。核心结论点播成功前的任何一个环节出现问题都可能导致点播超时上述十个环节即是排查点播超时的依据。下文按此顺序把每一步落到具体源码位置。二、第 1 步WEB 用户发起点播请求入口是 PlayController接口为GET /api/play/start/{deviceId}/{channelId}对应前端页面 web/src/api/play.js 中封装的playStart请求。该接口的实现要点异步等待模型方法返回DeferredResultWVPResultStreamContent超时时间取自userSetting.getPlayTimeout()即配置项play-timeout默认 10 秒。这与第 2 步 SIP Invite 的等待逻辑共用同一超时配置超时后的资源清理result.onTimeout回调中若最终未收到流会调用inviteStreamService.removeInviteInfoByDeviceAndChannel(...)清除 Redis 中的邀请缓存并调用deviceChannelService.stopPlay(channel.getId())将通道状态复位避免僵尸点播流地址改写当配置use-source-ip-as-stream-ip开启时回调中会深拷贝streamInfo并调用streamInfo.changeStreamIp(host)把播放地址中的流媒体 IP 替换为客户端请求来源 IP——这是外网穿透场景下让浏览器能直接访问流地址的关键转码流拼接若该 MediaServer 配置了transcodeSuffix转码流后缀返回的流 ID 会自动拼接后缀例如xxx变为xxx_h265。对应的停止接口为GET /api/play/stop/{deviceId}/{channelId}playStop内部调用playService.stop(InviteSessionType.PLAY, device, channel, streamId)发送 Bye。三、第 2 步构造并发送 SIP Invite含 SDP 消息体这是整个流程中最核心的一步。PlayServiceImpl.play 在真正发 Invite 之前做了四件准备工作3.1 复用已有会话幂等保护方法开头先查询 Redis 中的InviteInfo若已有同通道的点播请求正在等待结果streamInfo null则仅注册回调inviteStreamService.once(...)不重复发 Invite若已有点播已成功且 ZLM 查询isStreamReady返回 true则直接回调成功并复用已有流地址日志打印[点播已存在] 直接返回若缓存的状态异常则清除缓存后重新发起。这一机制解释了为什么快速重复点击播放按钮不会造成多路重复 Invite。3.2 分配收流端口与 SSRCSSRCInfo ssrcInfo receiveRtpServerService.openGbRTPServerForPlay(mediaServer, device, channel, ssrc, record ! null ? record : userSetting.getRecordSip(), (code, msg, result) - { ... });通过 ReceiveRtpServerServiceImpl 调用 ZLMediaKit 的openRtpServer接口分配收流端口并注册 SSRC 鉴权信息on_publishHook 中按 SSRC/stream 校验是否放行推流。SSRC 由 SSRCFactory 生成流 ID 的格式为设备国标编号_通道国标编号streamId String.format(%s_%s, ...)。若端口或 SSRC 分配失败会以ERROR_FOR_RESOURCE_EXHAUSTION获取端口或者ssrc失败错误回调并清理 SSRC 事务。3.3 写入 Redis 邀请状态InviteInfo inviteInfo InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServer.getId(), mediaServer.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, InviteSessionStatus.ready); inviteStreamService.updateInviteInfo(inviteInfo);InviteInfo以ready状态写入 Redis后续 200OK 处理中会更新为ok并填充StreamInfo失败则删除。它是跨请求如点播已存在判断、超时清理、Bye 反查设备的共享状态。3.4 SDP 消息体构造sPlay 与 y真正构造 SDP 并发送 Invite 的是 SIPCommander.playStreamCmd。SDP 构造逻辑与文档描述一一对应content.append(v0\r\n); content.append(o device.getDeviceId() 0 0 IN IP4 sdpIp \r\n); content.append(sPlay\r\n); // s字段Play 代表实时点播 content.append(cIN IP4 sdpIp \r\n); content.append(t0 0\r\n);其中sdpIp优先取设备级device.getSdpIp()为空则取 MediaServer 的sdpIp——这对应文档中ZLMediaKit 接收流使用的 IP。媒体描述部分按收流模式区分三种写法收流模式m 行协议附加属性UDPmvideo port RTP/AVP 96 126 125 99 34 98 97无 setupTCP-PASSIVEmvideo port TCP/RTP/AVP 96 126 125 99 34 98 97asetup:passiveaconnection:newTCP-ACTIVE同 TCP/RTP/AVPasetup:activeaconnection:new并始终携带arecvonly及 rtpmap 映射PS96、H264126/98、H26599、MPEG497 等当senior-sdp配置开启时会声明更完整的负载集。最后追加 SSRC 字段content.append(y ssrcInfo.getSsrc() \r\n); // y字段SSRC文档提到的f字段媒体参数如fv/2/6/25/1/4000a/6/8/1在源码中被注释掉注释说明未发现支持此特性的设备因此当前版本不发送f字段。Invite 请求经 SIPSender.transmitRequest 发出并挂接两个回调成功回调收到 200OK 后进入InviteOKHandler见第四节失败回调SIP 传输异常或非 2xx 响应时关闭 RTP 收流端口receiveRtpServerService.closeRTPServer、删除 SSRC 事务、回调错误码并清除 Redis 邀请状态。发送动作本身抛出SipException时则以ERROR_FOR_SIP_SENDING_FAILED处理。另外PlayServiceImpl.play 对外层还有一个前置分支若设备不属于当前节点device.getServerId() ! userSetting.getServerId()多节点部署场景会通过redisRpcPlayService.play(device.getServerId(), ...)转给真正持有该设备 SIP 会话的节点执行。四、第 3、4 步200OK 响应与 Ack 应答4.1 InviteResponseProcessor 回复 AckInviteResponseProcessor 订阅了 INVITE 的响应处理。收到200 OK时取出响应原始内容response.getRawContent()用SipUtils.parseSDP解析出 SDPGb28181Sdp同时解析了 GB28181 扩展的y行从 SDP 的o行取设备标识结合响应来源地址构造 Request-URI调用headerProvider.createAckRequest(...)构造 Ack 请求通过sipSender.transmitRequest发往设备日志打印[回复ack] 设备 - IP:Port。TRYING100响应则不处理。Ack 发出后 SIP 会话建立成功——设备收到 Ack 即视为可开始推流。4.2 InviteOKHandlerSSRC 一致性校验与 TCP 主动连接200OK 的另一半处理在 PlayServiceImpl.InviteOKHandler它把响应中y字段解析出的 SSRC 与本机请求的 SSRC 对比SSRC 一致若为TCP-ACTIVE且为多端口收流mediaServerItem.isRtpEnable()调用 tcpActiveHandler——解析 200OK 的 SDP找到 PS 负载payload 96对应的端口调用mediaServerService.connectRtpServer(...)由 ZLMediaKit主动发起 TCP 连接到设备。连接失败会以ERROR_FOR_TCP_ACTIVE_CONNECTION_REFUSED_ERROR结束流程并清理端口、SSRC 事务与 Redis 状态。单端口收流模式下则不支持 TCP 主动打印告警日志。SSRC 不一致下级设备自定义了 SSRC单端口收流由于流按端口绑定重注册 SSRC 鉴权即可receiveRtpServerService.refreshAuthenticateInfo(oldStreamId, newStreamId)并更新SsrcTransaction多端口收流若设备开启ssrcCheck调用mediaServerService.updateRtpServerSSRC修正 ZLM 收流端口的 SSRC修正失败则发 Bye 终止点播未开启校验则直接以设备回复的 SSRC 更新本地记录。这一步是点播超时高发点之一部分海康/大华设备的 200OK 回复不携带y字段源码对此做了兼容——ssrcInResponse null时直接采用请求侧 SSRC。五、第 5、6 步设备推流与 ZLMediaKit 流事件设备收到 Ack 后开始向sdpIp:port发送 RTP 流。收流端口在 3.2 节已预先打开设备推流到达后触发 ZLM 的 Hook。ZLMMediaServerStatusManager 在注册节点时向 ZLM 写入了四个关键 Hook 地址param.put(hook.on_publish, String.format(%s/on_publish, hookPrefix)); param.put(hook.on_stream_changed, String.format(%s/on_stream_changed, hookPrefix)); param.put(hook.on_stream_none_reader, String.format(%s/on_stream_none_reader, hookPrefix)); param.put(hook.on_rtp_server_timeout, String.format(%s/on_rtp_server_timeout, hookPrefix));这些 Hook 在 ZLMHttpHookListener 中实现/on_publish推流鉴权onPublish 调用mediaService.authenticatePublish按 SSRC/stream 校验是否为本次点播申请打开的收流端口是防止非法 RTP 流进入的关键闸门。鉴权通过即回调 3.2 节openGbRTPServerForPlay的 Hook 成功分支——onPublishHandlerForPlay组装StreamInfoapp、stream、协议地址、媒体服务器信息、把通道置为播放中deviceChannelService.startPlay将InviteInfo状态更新为ok并回填StreamInfo随后回调触发第 7 步的播放地址下发。/on_stream_changed流改变事件onStreamChanged 即文档时序图中第 6 步 流改变事件的落点。它区分 rtsp 流的注册/注销分别发布MediaArrivalEvent/MediaDepartureEvent应用事件。自动点播兜底PlayServiceImpl 还监听了MediaNotFoundEventon_stream_not_found转发而来当客户端直接请求播放一个尚未存在的 GB28181 流流名格式为设备ID_通道ID时WVP 会自动发起一次点播实现按需拉流。六、第 7 步回复播放地址openGbRTPServerForPlay的 Hook 成功回调与DeferredResult回调链路在第五节已闭环StreamInfo最终包装为StreamContent通过result.setResult(wvpResult)返回给 WEB 端内容包括wsWebRTC、rtsp、rtmp、url等协议播放地址见 StreamInfo。前端随后在 web/src/views/common/rtcPlayer.vue、jessibuca.vue、h265web.vue 等播放器组件中建立播放。至此文档时序图的成功路径步骤 1-7走完。DeferredResult未超时即视为点播成功日志可检索[点播成功] 设备编号: ...确认。七、第 8、9、10 步无人观看事件与 Bye 释放当所有观看端断开ZLM 检测到流无人观看回调/on_stream_none_reader。onStreamNoneReader 调用mediaService.closeStreamOnNoneReader(...)决定本次是否关闭流返回closetrue后 ZLM 销毁该流流销毁事件进一步触发PlayServiceImpl.onApplicationEvent(MediaDepartureEvent)对流名符合 GB28181 规则设备ID_通道ID的流查找InviteInfo若状态为ok则调用stop(inviteInfo)——内部执行cmder.streamByeCmd(...)向设备发送Bye 消息时序图第 9 步设备回复200OK第 10 步SIP 会话终结配套清理关闭 RTP 收流端口、释放 SSRC 事务、删除 Redis 邀请状态。除被动释放外还有两条主动释放路径点播超时playStreamCmd挂接的超时任务在playTimeout秒内未等到成功时同样发送 Bye 并清理资源见 PlayServiceImpl 失败分支 的cmder.streamByeCmd前端显式停止调用GET /api/play/stop/{deviceId}/{channelId}。八、点播超时排查清单结合上述实现点播超时按十个环节逐一定位的排查要点环节症状/日志特征排查方向1 发起请求[开始点播]日志缺失鉴权/接口调用本身失败2 Invite 发送[命令发送失败] 点播消息SIP 网络不通、设备地址错误ERROR_FOR_SIP_SENDING_FAILED端口/SSRC 分配获取端口或者ssrc失败该 ZLM 端口耗尽、SSRC 池用尽ERROR_FOR_RESOURCE_EXHAUSTION3 等待 200OK点播等待超时/errorEvent.statusCode非 200设备离线、防火墙拦截 SIP设备返回 403/486 等4 SSRC 校验[Invite 200OK] SSRC修正、单端口收流时不支持TCP主动方式收流收流模式与设备能力不匹配ssrcCheck行为5 设备推流收流端口长时间无数据on_rtp_server_timeoutNAT 穿越、SDP 中 IP 不可达检查设备级sdpIp配置、TCP 主动连接被拒6 流事件[ZLM HOOK]推流鉴权-拒绝SSRC 鉴权信息未注册或被设备改写7 下发地址超时回调点播超时前 6 步均未完成的综合表现/api/play/ssrc接口getSSRC可列出当前所有进行中的 SSRC 事务配合日志快速确认卡在哪一步。九、小结wvp-GB28181-pro 的点播实现是一个典型的信令面 媒体面分离架构SIP 层JAIN-SIP负责 Invite/200OK/Ack/Bye 的会话生命周期ZLMediaKit 层负责收流、鉴权与转封装两者通过预先打开收流端口 on_publish鉴权回调 流事件监听解耦串联。Redis 中的InviteInfo与内存中的SsrcTransaction是贯穿全流程的两份状态台账保证了幂等重入、超时清理与 Bye 反查的正确性。理解这套机制后无论是排查点播超时、定制收流模式还是扩展新的流事件处理都能沿着本文的十步时序精准定位到对应源码。【免费下载链接】wvp-GB28181-pro基于GB28181-2016、部标808、部标1078标准实现的开箱即用的网络视频平台。自带管理页面支持NAT穿透支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联支持将普通摄像机/直播流/直播推流转国标共享到国标平台。项目地址: https://gitcode.com/GitHub_Trending/wv/wvp-GB28181-pro创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表