「设备 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 秒实验
- 把
SetRelayOutputState的LogicalState改成on(非法值),看设备回什么。 - 把
GetVideoSources的响应打印完整,看它有没有帧率上限字段。 - 给模拟设备加一个
AudioSource,看GetAudioSources的返回变成什么形状。
常见误区
- 以为继电器能调光。 它是开关量,要调亮度得靠 0-10V 或 DALI,ONVIF 管不了。
- 以为
active就是「开灯」。 它是继电器吸合,常闭接法下active反而是关灯, 要看接线图。 - 用「视频源的数量」判断双码流。 双码流是多个编码配置 + 多个 Profile, 物理源只有一路。
自测三问(点开对答案)
1. ONVIF 的「设备 IO」包含哪三类? —— **视频源、继电器输出、音频源。** **它们在 `deviceio` 这个 service 里。** 2. 双稳态和单稳态继电器有什么区别? —— **双稳态断电后保持状态**(适合控灯),**单稳态断电自动复位**(适合按门铃)。 3. `LogicalState=active` 一定等于「灯亮」吗? —— **不一定。** 它表示**继电器吸合**,**常闭接法下 `active` 反而是关灯**,取决于接线。下一节讲图像配置:为什么改参数会失败,以及「先查范围再改值」这个必须遵守的顺序。
- ☐ (本节完,下一节 07.02.08 图像配置)