📹 第七章 ONVIF协议原理解析
📄 一句话概括

receiver 这一组问的是「你能接别人的流吗」——它是「拉流端」的能力声明。 配套的四步里,最有用的一个是 GetReceiverState:它直接告诉你连上没有。

📝 本节和前面几节的关系

上一节讲「怎么显示」, 这一节讲「流从哪来」——一个上墙的解码器,既要有 display 也要有 receiver。 这一节和 07.02.10 正好是一对:那里是「给地址」,这里是「收地址」。

一、发流端和拉流端

角色 service 干什么
发流端 media 给地址、被拉
拉流端 receiver 收别人的地址、自己拉

一台 NVR 通常两者都有:它 media 那侧给客户端提供回放流,receiver 那侧去拉摄像机的流。 所以「NVR 的 ONVIF 地址」和「NVR 上某路摄像机的 RTSP 地址」是两个不同的东西, 很多人第一次配 NVR 时会混淆。 这个混淆的后果很具体: 你拿 NVR 的 ONVIF 地址当摄像机的流地址去播,当然播不出来—— 前者是「管理接口」,后者是「媒体地址」,中间差着一层转发。

二、GetReceivers:配了哪些接收任务

项 本例的值 说明
Token Receiver_1 接收任务的标识
模式 RTP-Unicast 单播,一台拉一路
媒体地址 rtsp://127.0.0.1:554/live 你填进去的源地址

第二行那个 RTP-Unicast 说明它是单播接收——一台拉一路,不和别人共享。 如果同一个源要给十台解码器看,用 RTP-Multicast 接收能省九份带宽—— 但前提是整条链路都通组播。 第三行是你填进去的源地址:它可以是 RTSP(rtsp://),也可以是裸 RTP(rtp://)。 后者没有握手,直接收包,延迟更低但更容易乱序。 所以「低延迟」和「稳」在这里也是二选一,和第一章讲的 UDP/TCP 取舍是一回事。

三、配置一个接收器的四步

步 操作 产出
1 GetReceiver 看默认配置
2 SetReceiver 填媒体地址和模式
3 CreateReceiver 新开一个接收任务
4 GetReceiverState 看它连上没有

第四行是最有用的一步:它返回 Connected/NotConnected 加一个 Error 字段。 「拉不到流」的时候,先看这个状态—— NotConnected 说明是源地址或网络问题,Connected 但没画面说明是解码问题。 这一个字段就能把排查范围砍一半。 这个「状态查询」的设计值得学:很多协议只告诉你「配置成功」,不告诉你「实际连上没」, 于是「配置成功但没画面」就成了玄学。 ONVIF 把「配置」和「运行状态」分成两个操作,这个分离在排障时价值极大。

四、本机做到哪一步

诚实地说:本机只实现了「读」,没实现「写」。

操作 本机 说明
GetReceivers 有 返回一个 Receiver_1
GetReceiver 没有 要按 token 调
SetReceiver 没有 要能改配置
GetReceiverState 没有 要有真实的连接状态

最后一行是有意不做的:GetReceiverState 要有「真在拉流」才有意义, 而模拟设备没有真的 RTSP 客户端。 硬编一个 Connected 出来,会让人以为「拉流成功了」——那是假数据,比没有更糟。 所以这一节讲的「四步」和「状态字段」都只有概念,Demo 只能验「读得到配置」。 这个边界在正文里说清,比在 Demo 里糊过去好。

五、动手 Demo:接收器,一台设备去拉另一台

Demo 问一次 GetReceivers,把配置读出来:token、模式、媒体地址。 然后对着那个地址想想「它和摄像机自己的 RTSP 地址有什么不同」。

_a, b, _n = S.call(port, "receiver", "GetReceivers")
r = ET.fromstring(b)
for rv in r.iter("{%s}Receivers" % S.NS_TRV):
    cfg = rv.find("{%s}Configuration" % S.NS_TT)
    mode = cfg.find("{%s}Mode" % S.NS_TT)
    uri = cfg.find("{%s}MediaUri/{%s}Uri" % (S.NS_TT, S.NS_TT))
    print(rv.get("token"), mode.text, uri.text)

实跑输出:

--------------------------------------------------------------------
接收器,一台设备去拉另一台
--------------------------------------------------------------------
**`receiver` 这一组接口问的是「你能接别人的流吗」。**
  **它是「拉流端」的能力声明,和「发流端」(摄像机)正好相反。**

**两种角色:**

  角色                service                 干什么
  ------------------------------------------------------------------------------
  发流端              **`media`**             **给地址、被拉**
  拉流端              **`receiver`**          **收别人的地址、自己拉**

  **一台 NVR 通常两者都有**:
  **它 `media` 那侧给客户端提供回放流,`receiver` 那侧去拉摄像机的流。**
  **所以「NVR 的 ONVIF 地址」和「NVR 上某路摄像机的 RTSP 地址」是两个不同的东西**,
  **很多人第一次配 NVR 时会混淆。**

**`GetReceivers`:本机作为拉流端,配了哪些接收任务**

  Token             模式                    媒体地址
  ------------------------------------------------------------------------------------
  Receiver_1        RTP-Unicast             `rtsp://127.0.0.1:554/live`

  **第二行那个 `RTP-Unicast` 说明它是单播接收**——  **一台拉一路,不和别人共享。**
  **如果同一个源要给十台解码器看,用 `RTP-Multicast` 接收能省九份带宽**——
  **但前提是整条链路都通组播。**
  **第三行是你填进去的源地址**:
  **它可以是 RTSP(`rtsp://...`),也可以是裸 RTP(`rtp://...`)。**
  **后者没有握手,直接收包,延迟更低但更容易乱序。**

**配置一个接收器要四步:**

  步    操作                          产出
  --------------------------------------------------------------------
  1     `GetReceiver`                 看默认配置
  2     `SetReceiver`                 **填媒体地址和模式**
  3     `CreateReceiver`              新开一个接收任务
  4     `GetReceiverState`            **看它连上没有**

  **第四行是最有用的一步**:  **它返回 `Connected`/`NotConnected` 加一个 `Error` 字段。**
  **「拉不到流」的时候,先看这个状态**——
  **`NotConnected` 说明是源地址或网络问题,  `Connected` 但没画面说明是解码问题。**
  **这一个字段就能把排查范围砍一半。**

那个 token 是属性,不是子元素——所以用 rv.get,不是 find。 这个坑在本章出现了三次了(07.02.01、07.02.09、这里), 说明它不是偶然,是 ONVIF 的普遍写法。 媒体地址里的 127.0.0.1:554 是配置值,不是实际连接的地址—— 真实设备这里应该填「摄像机」的地址。 模拟器填本机地址只是为了让字符串看起来像真的,它并没有真的去连。 本节没验「连上没有」, 因为 GetReceiverState 本机没实现——这一点在正文里已经写清了,Demo 里也不假装。 如果真要验,得有第二台设备当「源」,而沙箱里跑两个进程互相拉流不是不能做, 但那就变成「写一个 RTSP 客户端」了,超出这一节的范围。

30 秒实验

  1. 把 Mode 改成 RTP-Multicast,看模拟器接受不接受(它只是返回配置,不改状态)。
  2. 把媒体地址改成 rtp://239.0.0.1:5000,想想这种写法为什么没有握手。
  3. 查一下 GetReceiverState 的响应结构,看它除了 Connected 还返回什么。

常见误区

  1. 把 NVR 的 ONVIF 地址当摄像机的流地址。 前者是管理接口,后者是媒体地址,中间差一层转发。
  2. 以为 receiver 是给摄像机用的。 它属于拉流端,摄像机是发流端,用 media。
  3. 以为配置成功就是连上了。 配置和运行状态是两回事,要看 GetReceiverState。
自测三问(点开对答案) 1. `media` 和 `receiver` 分别是什么角色用的? —— **`media` 是发流端(给地址、被拉),`receiver` 是拉流端(收地址、自己拉)。** **NVR 通常两者都有。** 2. 为什么 `GetReceiverState` 比配置操作更有用? —— **它直接告诉你 `Connected` 还是 `NotConnected`**, **一句就能把「源/网络问题」和「解码问题」分开。** 3. `rtsp://` 和 `rtp://` 作为源地址有什么区别? —— **`rtsp://` 有握手,可靠**;`rtp://` 直接收包,延迟低但更容易乱序。

下一节讲存储:MaximumRetentionTime 为什么是个「时长」而不是「天数」, 以及「保留三天」在几十路项目里意味着几块硬盘。

相关笔记