大华摄像机的 Web 页面把登录、参数配置、文件传输和镜头动作组合成了一个统一界面。接入程序要复现这些操作,需要分别处理 JSON 请求中的 session、HTTP Cookie、服务对象句柄,以及完整配置表的读写。
本文整理常用调用方式和实现顺序,覆盖登录、图像、视频编码、音频、报警音文件及电动镜头。接口行为来自 2026 年 10 月 2 日至 3 日对一台 Web5.0 固件设备的抓包和测试,是该固件的接入记录,不是适用于全部型号的官方协议规范。地址、账号和认证值均使用占位符。
如果更关心这些结论是怎样得到的,可阅读配套文章 大华 RPC2 分析记录 从浏览器操作到独立客户端验证。
请求入口和基本报文
此次观察到的主要入口如下:
JSON 请求使用 UTF-8 编码,普通配置查询可以写成:
{
"method": "configManager.getConfig",
"params": {"name": "VideoColor", "options": []},
"id": 101,
"session": "<SESSION>"
}
method 和参数字段区分大小写。id 用于匹配响应,session 使用登录成功后服务端返回的值,并保留其类型。示例中的 <SESSION> 等字符串只是占位符。
不要把所有方法的 params 都统一转换为对象:本次观测中存在 null、空字符串、数组和对象,它们的含义不同。普通操作通常返回 result=true,工厂方法则可能在 result 中返回整数对象句柄。
判断结果时,应依次检查 HTTP 状态、响应 ID、RPC 结果和业务字段。HTTP 200 只表示请求在 HTTP 层有了响应,并不说明配置、播放或文件上传已成功。
两阶段登录与 Cookie
获取当前登录挑战
向 /RPC2_Login 发送空密码请求,不携带旧 session:
{
"method": "global.login",
"params": {
"userName": "<USER>",
"password": "",
"clientType": "Web5.0"
},
"id": 1
}
测试设备返回 result=false、错误码 268632079,同时给出 realm、random 和临时 session。这个特定响应表示登录挑战,需要继续认证;其他错误不能直接按挑战处理。
计算摘要并完成认证
本次验证的是 Default 模式。字符串按 UTF-8 编码,MD5 结果使用大写十六进制:
H1 = MD5_UPPER(userName + ":" + realm + ":" + plainPassword)
H2 = MD5_UPPER(userName + ":" + random + ":" + H1)
第二次请求仍发往 /RPC2_Login:
{
"method": "global.login",
"params": {
"userName": "<USER>",
"password": "<H2>",
"clientType": "Web5.0",
"realm": "<REALM>",
"random": "<RANDOM>",
"passwordType": "Default",
"authorityType": "Default"
},
"id": 2,
"session": "<CHALLENGE_SESSION>"
}
realm、random 和挑战 session 都取自本次响应。认证成功后,以正式 session 替换临时值。设备返回其他认证模式时,应采用对应流程,不能继续套用这组摘要公式。
RPC 会话与文件认证分别保存
独立密码登录复测确认:第二阶段认证成功响应会下发 WebClientHttpSessionID Cookie。JSON RPC 使用正式 session;文件上传还要求有效 Cookie,只有 RPC 登录成功并不足够。
Python 可以用同一个带 Cookie 处理器的 opener 完成登录和后续文件请求:
import http.cookiejar
import urllib.request
cookie_jar = http.cookiejar.CookieJar()
opener = urllib.request.build_opener(
urllib.request.HTTPCookieProcessor(cookie_jar)
)
# 登录和后续请求都通过同一个 opener 发送
# Cookie 从服务端响应中取得,不根据 RPC session 自行构造
CookieJar 与 HTTPCookieProcessor 用于接收、管理和发送符合条件的 Cookie。Python CookieJar 文档、urllib.request 文档
参考客户端在 /RPC2 请求中同时发送 JSON session 和 X-api-session 头。文件请求沿用当前 Cookie 和会话头。密码、摘要、session 和 Cookie 都不应进入公开日志。
保活方法为 global.keepAlive,已观测参数是 {"timeout":300,"active":true};调度周期应结合登录响应的 keepAliveInterval 和保活响应更新。退出时调用 global.logout,清理本地会话、Cookie 和对象缓存。
服务对象与批量查询
图像和镜头接口中的一部分方法需要先创建服务对象:
{
"method": "devVideoInput.factory.instance",
"params": {"channel": 0},
"id": 110,
"session": "<SESSION>"
}
工厂返回的整数 result 是当前会话内的句柄。后续方法按各自定义传递该句柄,例如放在请求顶层的 object 中。不要把示例句柄保存成固定常量,重新登录后应重新创建。
也不能推定所有同名前缀的方法都需要 object。例如本机 devVideoInput.setFocus 使用 params.Channel,没有顶层 object。
批量查询使用 system.multicall,其 params 为子请求数组,每项带独立 ID。解析时逐项匹配子 ID 和结果;外层成功不代表所有子调用成功,批量请求也不具备已验证的原子提交或回滚保证。
配置修改采用读取后整表写回
区分查询 临时预览和保存
一次亮度修改虽然只改变 Brightness,浏览器发送的仍然是完整 VideoColor 表。因此客户端应读取原表、复制并修改目标字段,再发送完整表,保留其他通道、profile 和未知字段。
{
"method": "configManager.setConfig",
"params": {
"name": "VideoColor",
"table": "<READ_MODIFY_WRITE_FULL_TABLE>",
"options": []
},
"id": 201,
"session": "<SESSION>"
}
这里的 table 占位符必须替换成读取并修改后的原生数组或对象,不能原样发送字符串。没有证据支持直接提交 {"Brightness":51} 这样的稀疏补丁。
图像表按实际结构定位
通道索引、profile 索引和码流记录索引应分别处理。本次设备有 9 个具名图像 profile,不能固定用某个数组下标代表 Day 或 Night。
能力可以通过 devVideoInput.getCaps、getCapsEx 等接口获取。字段存在也不意味着当前组合可写:测试中 Rotate90=1 在 3840×2160 条件下被拒绝;VideoInIRExposure 可以读取,但对应能力为 Support=false。
还应保留固件的原始拼写。比如降噪字段为 3DManulType,透雾手动模式的配置值为 Manul,不能为了统一英文拼写擅自改名。
保存与恢复都要回读关联表
基本顺序为:读取配置和能力 → 备份 → 构造候选 → 检查基线是否变化 → 写入 → 回读目标及关联表。
启用 AISSA.Enable 后,测试设备会在 VideoInBacklight 和 VideoInDefog 中新增 Snapshot。恢复时只把 Enable 改回原值不够,还需要还原关联变化,并区分“原来没有这个键”和“原值为 null”。
多表写入并非事务。写请求超时后,设备可能已经执行,应先回读实际状态,再决定下一步。
视频编码与音频格式
Encode 中的记录不都代表独立码流
本机 Encode[channel] 包含 MainFormat、ExtraFormat 和 SnapFormat。其中 MainFormat 有 4 条配置记录,对应同一主码流的不同记录;ExtraFormat 有 4 条辅码流记录。不能因为 MainFormat 长度为 4,就认为设备提供 4 条独立主码流。
修改编码、分辨率或帧率后,应使用完整候选 Encode 查询动态能力:
{
"method": "encode.getConfigCaps",
"params": {
"config": "<FULL_CANDIDATE_ENCODE_ARRAY>",
"stream": "All",
"channel": 0
},
"id": 301,
"session": "<SESSION>"
}
config 同样要替换为完整候选数组。检查 CompressionTypes、ResolutionTypes、FPSMax、码率范围和码率控制模式,再提交配置。
本机能力返回的 MainFormat 只有 3 项,少于配置记录数。缺失项不能复制其他项能力后声称预检完成,仍需根据保存和回读结果判定。
AI 编码有写入顺序
本次 AI 编码涉及 AICoding 与 Encode 两张表。先写 AICoding 可能触发设备自动修改 Encode,因此顺序应为:
读取并备份两张表,构造完整候选 Encode。
用候选配置查询动态能力。
写 AICoding,回读并检查联动。
写完整 Encode,再回读两张表。
本机开启后的组合包括 encodeType=2、BitRateControl=ABR、SVCTLayer=1。AiGOP 与 FPS 关联,例如 25 FPS、2 秒策略对应 50。具体秒数、码率和编码组合仍以当前能力为准。
码流开关测试验证了 VideoEnable 的关闭、开启与恢复;这不等于已经测量网络媒体连接是否实际停止。
音频按完整格式组合校验
音频编码同样在 Encode 表中,修改时保留全部视频字段和其他记录。创建 devAudioEncode 对象后,通过 devAudioEncode.getFormatCaps 查询格式能力。
Compression、Frequency、Depth 应作为一个完整组合匹配,不能分别从不同行能力中挑选。其他常用配置包括:
这些测试验证了指定参数保存与回读,没有测量音质或实际扬声器响度。
报警音上传 下载与播放
先创建 speak 对象,再调用 speak.getCaps 获取 AudioPlayPath。本机自定义音频目录是 /mnt/appdata/customAudioFiles/,目录能力允许上传 10 个文件,单文件上限为 1048576 字节。这是设备接口路径,不是本地项目目录;其他型号应读取自己的目录能力。
上传使用 multipart,文件字段名为 file,内容是原始文件字节:
POST /RPC2_UploadFileWithName/mnt/appdata/customAudioFiles/example.wav HTTP/1.1
Host: camera.example
Cookie: WebClientHttpSessionID=<COOKIE>
X-api-session: <SESSION>
Content-Type: multipart/form-data; boundary=----BOUNDARY
Content-Length: <BODY_BYTE_LENGTH>
------BOUNDARY
Content-Disposition: form-data; name="file"; filename="example.wav"
Content-Type: audio/wav
<RAW_FILE_BYTES>
------BOUNDARY--
线上分隔行使用 CRLF,Content-Length 按最终请求体字节计算,boundary 必须一致。不要把文件转换成 Base64 填进去。
本机成功响应包含:
<script>if(parent.audioUploadStatue) parent.audioUploadStatue(1);</script>
audioUploadStatue 是固件原始拼写。客户端解析文本,不执行返回的脚本;遇到 audioUploadReason 或没有成功回调时,不能判定上传成功。
上传完成后,用 FileManager.list 确认目标文件,再下载并比较字节数与 SHA256:
/RPC2_Loadfile/mnt/appdata/customAudioFiles/example.wav?action=download
播放流程为 speak.startPlay → 检查 result=true 且 params.Status="OK" → speak.stopPlay → speak.destroy。播放请求的 params 为 {"path":"<ABSOLUTE_AUDIO_PATH>"},顶层 object 使用当前 speak 句柄。
如果要设为报警音,应读取完整 Sound,修改 AlarmSound 为已存在的文件路径,保存后回读。设置报警音与触发报警事件是两个步骤,本次并未验证完整事件联动。
电动镜头控制与完成判定
创建 devVideoInput 对象后,用 getFocusStatus 查询镜头状态。Focus、Zoom 是 0~1 的归一化电机位置,不是毫米焦距或物理倍率。
focus_steps = round(Focus × FocusMotorSteps)
zoom_steps = round(Zoom × ZoomMotorSteps)
电机步数范围应从当前设备读取,不要使用测试机的常量。主要动作如下:
表中的位置仅用于说明报文,重置是独立动作,不应作为普通参数错误后的默认回退。
变倍可能触发自动聚焦,覆盖同时传入的 Focus。因此需要同时设置两个目标时,先设置 Zoom,轮询到状态与位置稳定,再设置 Focus,最后检查两轴位置。
参考客户端以连续两次 Status=Normal 且两轴位置稳定作为完成条件的一部分,并继续核对目标值;单次成功响应不足以说明动作结束。区域聚焦使用 8192 尺度的原生坐标,不能直接填入预览窗口像素。
镜头测试覆盖 9 个案例,验证了动作响应、目标类型和稳定位置回读。物理倍率换算、毫米焦距和光学清晰度需要另行测量。
实现时保留结果层级
客户端可以区分“已接受”“已验证”“部分执行”“结果未知”和“已恢复”。这些是客户端状态,不是设备原生错误码。
请求发送后发生超时或断连,不能直接认定操作未执行;多表保存、播放、镜头重置等动作应先查询现状,再决定是否重试。能力表中的字段存在、原表保存成功和目标字段确实可修改,也应分别记录。
本次整理共观测到 100 种 RPC、52 个配置名称,其中 44 个配置成功读取。这个数量用于说明观察范围,不代表每个字段、所有编码组合或全部型号都已完成验证。接入新固件时,应重新确认登录模式、能力、配置结构和关键操作回读结果。
大华 RPC2 接入实践 从登录会话到图像编码与镜头控制
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法