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 秒实验
- 把
Mode改成RTP-Multicast,看模拟器接受不接受(它只是返回配置,不改状态)。 - 把媒体地址改成
rtp://239.0.0.1:5000,想想这种写法为什么没有握手。 - 查一下
GetReceiverState的响应结构,看它除了Connected还返回什么。
常见误区
- 把 NVR 的 ONVIF 地址当摄像机的流地址。 前者是管理接口,后者是媒体地址,中间差一层转发。
- 以为
receiver是给摄像机用的。 它属于拉流端,摄像机是发流端,用media。 - 以为配置成功就是连上了。 配置和运行状态是两回事,要看
GetReceiverState。
自测三问(点开对答案)
1. `media` 和 `receiver` 分别是什么角色用的? —— **`media` 是发流端(给地址、被拉),`receiver` 是拉流端(收地址、自己拉)。** **NVR 通常两者都有。** 2. 为什么 `GetReceiverState` 比配置操作更有用? —— **它直接告诉你 `Connected` 还是 `NotConnected`**, **一句就能把「源/网络问题」和「解码问题」分开。** 3. `rtsp://` 和 `rtp://` 作为源地址有什么区别? —— **`rtsp://` 有握手,可靠**;`rtp://` 直接收包,延迟低但更容易乱序。下一节讲存储:MaximumRetentionTime 为什么是个「时长」而不是「天数」,
以及「保留三天」在几十路项目里意味着几块硬盘。
- ☐ (本节完,下一节 07.02.17 存储)