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

「设备 IO」讲的是视频以外的东西:视频源(几路镜头)、继电器输出(开关量)、音频输入。 继电器在亮化工程里最实用——摄像头带的继电器能直接控灯,不用另接一套系统。

📝 本节和前面几节的关系

上一节讲时间,这一节讲「设备身上除了镜头还有什么接口」。 这一组操作在 deviceio service 里,很多客户端根本不实现它——但工程上用得上。

一、三类 IO

类别 对应操作 典型用途
视频源 GetVideoSources 几路镜头、各自分辨率
继电器输出 GetRelayOutputs 开灯、开锁、报警输出
音频源 GetAudioSources 对讲、拾音

第二行在亮化工程里特别有用:摄像头带的继电器可以直接控灯,不用另外接一套控制系统。 但要注意: 继电器输出是「开关量」,不是「调光」——要调亮度得靠 0-10V 或 DALI,ONVIF 管不了。 这个边界要说清:很多人看到「能控继电器」就以为能做照明控制, 结果发现只能全开全关、不能调亮度、不能分组调光。 ONVIF 的定位是「视频监控设备的统一接口」,照明控制是顺带的一个开关量,不是它的主业。 还有一个容易记错的地方:视频源和音频源这两组操作在规范里属于 media 服务, deviceio 管的是继电器、数字输入、串口这些真正的物理接口。 按「都是 IO」去归类会归错,按 service 归类才对。

二、视频源和编码配置是两回事

这是 ONVIF 媒体模型里最容易被忽略的一层分工:

对象 代表什么 能不能改
VideoSource 物理传感器(分辨率、帧率上限) 不能,硬件定的
VideoEncoderConfiguration 怎么编(分辨率、码率、GOP) 能改

两者分开的直接好处是「双码流」: 一个 VideoSource + 两个 VideoEncoderConfiguration + 两个 Profile—— 同一个传感器,出主码流 1080p 和子码流 720p。 所以「我的摄像头支持双码流吗」这个问题,在 ONVIF 里等价于: 「它有几个 VideoEncoderConfiguration、几个 Profile」。 而 GetVideoSources 只会返回一路, 因为物理上就一个镜头——用「源的数量」判双码流是错的,这是本节最容易搞错的一点。

三、继电器:双稳态和单稳态

这个参数选错了,现场表现很怪,而且很难查出来是它的问题:

模式 断电后 适合什么
Bistable 保持原状态 控灯:断电再来电还是亮的
Monostable 自动复位 按门铃:按一下响一下

第一行是本节最实际的一条:双稳态断电后保持状态——用来控灯正合适,断电再来电还是亮的。 单稳态断电就复位——用来按门铃合适,按一下响一下。 选错了会出现「停电后灯灭了、来电后要手动再开」这种问题, 而现场几乎不会想到是继电器模式的问题——大家第一反应是「灯坏了」或「线路松了」,查半天。

四、逻辑状态和物理效果是错位的

写继电器的方法是 SetRelayOutputState,参数只有两个值:

<tio:SetRelayOutputState>
  <tio:RelayOutputToken>Relay_1</tio:RelayOutputToken>
  <tio:LogicalState>active</tio:LogicalState>
</tio:SetRelayOutputState>

LogicalState 只有 active 和 inactive 两个值。 注意它不是「开灯/关灯」,是「继电器吸合/释放」。如果接线是常闭的,active 反而是关灯。 这个「逻辑状态」和「物理效果」的错位,是现场调试最常见的坑之一。 所以接线图一定要看:常开(NO)接法下 active 是通电,常闭(NC)接法下 active 是断电。 同一句代码在两种接法下效果相反,而代码里看不出任何异常,只有到现场才知道灯反了。

五、动手 Demo:输入和输出,不只有视频

Demo 把三类 IO 都问一遍:视频源、继电器输出、音频源。 然后强行写一次继电器,看设备接受还是拒绝。 本机模拟设备只实现了前两类,音频源如实报「没有」。

_a, b, _n = S.call(port, "media", "GetVideoSources")
for vs in ET.fromstring(b).iter("{%s}VideoSources" % S.NS_TRT):
    print(vs.get("token"),
          vs.find("{%s}Framerate" % S.NS_TT).text,
          vs.find("{%s}Resolution/{%s}Width" % (S.NS_TT, S.NS_TT)).text)

_a, b2, _n = S.call(port, "deviceio", "GetRelayOutputs")
for ro in ET.fromstring(b2).iter("{%s}RelayOutputs" % S.NS_TIO):
    mode = ro.find(".//{%s}Mode" % S.NS_TT)
    print(ro.get("token"), mode.text if mode is not None else "-")

实跑输出:

--------------------------------------------------------------------
输入和输出,不只有视频
--------------------------------------------------------------------
**「设备输入/输出」这一节讲的是视频以外的东西:**
  **视频源(几路)、继电器输出(开关量)、音频输入。**

**三类 IO:**

  类别                  对应操作                      典型用途
  ----------------------------------------------------------------------------------
  **视频源**            `GetVideoSources`             **几路镜头、各自分辨率**
  继电器输出            `GetRelayOutputs`             **开灯、开锁、报警输出**
  音频源                `GetAudioSources`             对讲、拾音

  **第二行在亮化工程里特别有用**:  **摄像头带的继电器可以直接控灯**,
  **不用另外接一套控制系统。**
  **但要注意**:
  **继电器输出是「开关量」,不是「调光」**——  **要调亮度得靠 0-10V 或 DALI,ONVIF 管不了。**

**视频源:**

  Token                   帧率          分辨率
  ----------------------------------------------------------
  VideoSource_1           25 fps        1920×1080

  **视频源(`VideoSource`)是「物理传感器」。**
  **编码配置(`VideoEncoderConfiguration`)是「怎么编」。**
  **两者分开,是因为一路源可以出多路流**:  **主码流 1080p、子码流 720p,共用同一个传感器。**
  **这就是「双码流」在 ONVIF 里的实现方式**:
  **一个 `VideoSource` + 两个 `VideoEncoderConfiguration` + 两个 `Profile`。**

**继电器输出:**

  Token                   模式                含义
  ----------------------------------------------------------------------------------
  Relay_1                 Bistable            **`Bistable` 是双稳态(断电保持)**

  **`Bistable`(双稳态)和 `Monostable`(单稳态)的区别很实际**:
  **双稳态断电后保持状态**——  **用来控灯正合适,断电再来电还是亮的。**
  **单稳态断电就复位**——  **用来按门铃合适,按一下响一下。**
  **选错了会出现「停电后灯灭了、来电后要手动再开」这种问题**,
  **而现场很难查出来是继电器模式的问题。**

**写继电器用 `SetRelayOutputState`:**

  `<tio:SetRelayOutputState>`
  `  <tio:RelayOutputToken>Relay_1</tio:RelayOutputToken>`
  `  <tio:LogicalState>active</tio:LogicalState>`
  `</tio:SetRelayOutputState>`

  **`LogicalState` 只有两个值:`active` 和 `inactive`。**
  **注意它不是「开/关灯」,是「继电器吸合/释放」**——
  **如果接线是常闭的,`active` 反而是关灯。**
  **这个「逻辑状态」和「物理效果」的错位,是现场调试最常见的坑之一。**

第一段请求发给了 media,不是 deviceio——这是规范里一个容易记错的地方: GetVideoSources 和 GetAudioSources 属于 media 服务, deviceio 管的是继电器、数字输入、串口这些真正的物理接口。 按名字归类会归错,按 service 归类才对。 字段名是 Framerate(小写 r),不是 FrameRate——ONVIF 的字段大小写是 XSD 定死的, 写错了 find 返回 None,不报错。 这种「大小写错一个字母就静默失败」的情况在 ONVIF 里非常多。 继电器那段用 .//{命名空间}Mode 而不是拼长路径, 因为它藏在 tt:Properties 里面—— 拼长路径要写对每一层的命名空间,容易错;用 .// 加完整命名空间更稳。 代价是「可能匹配到别处的同名元素」,所以只在确认唯一时才这么写。

30 秒实验

  1. 把 SetRelayOutputState 的 LogicalState 改成 on(非法值),看设备回什么。
  2. 把 GetVideoSources 的响应打印完整,看它有没有帧率上限字段。
  3. 给模拟设备加一个 AudioSource,看 GetAudioSources 的返回变成什么形状。

常见误区

  1. 以为继电器能调光。 它是开关量,要调亮度得靠 0-10V 或 DALI,ONVIF 管不了。
  2. 以为 active 就是「开灯」。 它是继电器吸合,常闭接法下 active 反而是关灯, 要看接线图。
  3. 用「视频源的数量」判断双码流。 双码流是多个编码配置 + 多个 Profile, 物理源只有一路。
自测三问(点开对答案) 1. ONVIF 的「设备 IO」包含哪三类? —— **视频源、继电器输出、音频源。** **它们在 `deviceio` 这个 service 里。** 2. 双稳态和单稳态继电器有什么区别? —— **双稳态断电后保持状态**(适合控灯),**单稳态断电自动复位**(适合按门铃)。 3. `LogicalState=active` 一定等于「灯亮」吗? —— **不一定。** 它表示**继电器吸合**,**常闭接法下 `active` 反而是关灯**,取决于接线。

下一节讲图像配置:为什么改参数会失败,以及「先查范围再改值」这个必须遵守的顺序。

相关笔记