大华摄像机的 Web 页面把登录、参数配置、文件传输和镜头动作组合成了一个统一界面。接入程序要复现这些操作,需要分别处理 JSON 请求中的 session、HTTP Cookie、服务对象句柄,以及完整配置表的读写。

本文整理常用调用方式和实现顺序,覆盖登录、图像、视频编码、音频、报警音文件及电动镜头。接口行为来自 2026 年 10 月 2 日至 3 日对一台 Web5.0 固件设备的抓包和测试,是该固件的接入记录,不是适用于全部型号的官方协议规范。地址、账号和认证值均使用占位符。

如果更关心这些结论是怎样得到的,可阅读配套文章 大华 RPC2 分析记录 从浏览器操作到独立客户端验证。

请求入口和基本报文

此次观察到的主要入口如下:

HTTP 方法

路径

用途与响应

POST

/RPC2_Login

登录挑战和认证,返回 JSON

POST

/RPC2

配置、能力查询和控制,返回 JSON

POST

/RPC2_UploadFileWithName<音频绝对路径>

multipart 上传,返回 HTML 回调

GET

/RPC2_Loadfile<音频绝对路径>?action=download

下载文件原始字节

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 层有了响应,并不说明配置、播放或文件上传已成功。

获取当前登录挑战

向 /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 中。不要把示例句柄保存成固定常量,重新登录后应重新创建。

服务工厂

主要用途

devVideoInput.factory.instance

图像能力和部分镜头方法

devAudioEncode.factory.instance

音频编码格式能力

speak.factory.instance

报警音能力、播放和停止

也不能推定所有同名前缀的方法都需要 object。例如本机 devVideoInput.setFocus 使用 params.Channel,没有顶层 object。

批量查询使用 system.multicall,其 params 为子请求数组,每项带独立 ID。解析时逐项匹配子 ID 和结果;外层成功不代表所有子调用成功,批量请求也不具备已验证的原子提交或回滚保证。

配置修改采用读取后整表写回

区分查询 临时预览和保存

方法

用途

configManager.getConfig

获取当前完整配置表,读取 params.table

configManager.getDefault

查询默认配置,不应用默认值

configManager.setTemporaryConfig

临时预览,图像设置中常见

configManager.setConfig

保存配置,需要回读确认

configManager.restoreTemporaryConfig

撤销临时覆盖,不等于恢复已保存配置

一次亮度修改虽然只改变 Brightness,浏览器发送的仍然是完整 VideoColor 表。因此客户端应读取原表、复制并修改目标字段,再发送完整表,保留其他通道、profile 和未知字段。

{
  "method": "configManager.setConfig",
  "params": {
    "name": "VideoColor",
    "table": "<READ_MODIFY_WRITE_FULL_TABLE>",
    "options": []
  },
  "id": 201,
  "session": "<SESSION>"
}

这里的 table 占位符必须替换成读取并修改后的原生数组或对象,不能原样发送字符串。没有证据支持直接提交 {"Brightness":51} 这样的稀疏补丁。

图像表按实际结构定位

配置

常用字段

定位方式

VideoColor

Brightness、Contrast、Saturation、Gamma、Style

table[channel][profileIndex],按 Name 定位

VideoInExposure

Mode、AntiFlicker、快门与增益字段

通道内按 Name 定位 profile

VideoInDenoise

3DType、3DManulType

通道内按 Name 定位 profile

VideoInBacklight、VideoInWhiteBalance

背光模式、白平衡模式及参数

通道内按 Name 定位 profile

VideoImageControl

Mirror、Flip、Stable、Rotate90

table[channel]

AISSA、LDCorrection

AI 场景自适应、畸变校正

table[channel]

通道索引、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、码率范围和码率控制模式,再提交配置。

参数

本机解释

Video.Compression、Profile

编码与档次,需要组合校验

Width、Height、CustomResolutionName

分辨率字段,同步修改

FPS

帧率,范围随候选配置变化

BitRate

视频码率,本机界面单位为 kbps

GOP

普通 GOP,单位为帧

VideoEnable

记录级视频开关,不是 AudioEnable

本机能力返回的 MainFormat 只有 3 项,少于配置记录数。缺失项不能复制其他项能力后声称预检完成,仍需根据保存和回读结果判定。

AI 编码有写入顺序

本次 AI 编码涉及 AICoding 与 Encode 两张表。先写 AICoding 可能触发设备自动修改 Encode,因此顺序应为:

  1. 读取并备份两张表,构造完整候选 Encode。

  2. 用候选配置查询动态能力。

  3. 写 AICoding,回读并检查联动。

  4. 写完整 Encode,再回读两张表。

本机开启后的组合包括 encodeType=2、BitRateControl=ABR、SVCTLayer=1。AiGOP 与 FPS 关联,例如 25 FPS、2 秒策略对应 50。具体秒数、码率和编码组合仍以当前能力为准。

码流开关测试验证了 VideoEnable 的关闭、开启与恢复;这不等于已经测量网络媒体连接是否实际停止。

音频按完整格式组合校验

音频编码同样在 Encode 表中,修改时保留全部视频字段和其他记录。创建 devAudioEncode 对象后,通过 devAudioEncode.getFormatCaps 查询格式能力。

Compression、Frequency、Depth 应作为一个完整组合匹配,不能分别从不同行能力中挑选。其他常用配置包括:

配置

作用

AudioInput

输入源,例如本机 LineIn、Mic

AudioInputVolume

按输入索引设置音量

AudioInDenoise

输入降噪,开关字段为小写 enable

AudioOutputVolume

按输出通道设置音量

Sound

全局报警音选择等配置,是对象而非通道数组

这些测试验证了指定参数保存与回读,没有测量音质或实际扬声器响度。

报警音上传 下载与播放

先创建 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)

电机步数范围应从当前设备读取,不要使用测试机的常量。主要动作如下:

动作

方法

params

顶层 object

指定位置

devVideoInput.adjustFocus

{"focus":0.15,"zoom":0.0}

devVideoInput 句柄

混合自动聚焦

devVideoInput.autoFocus

空字符串 ""

devVideoInput 句柄

人脸目标聚焦

devVideoInput.setFocus

{"FocusType":2,"Channel":0}

无

区域聚焦

devVideoInput.focusRegion

{"mode":"InROIRegion","rect":[3000,2000,5000,4500]}

devVideoInput 句柄

面板重置

devVideoInput.adjustFocus

{"focus":-1,"zoom":-1}

devVideoInput 句柄

表中的位置仅用于说明报文,重置是独立动作,不应作为普通参数错误后的默认回退。

变倍可能触发自动聚焦,覆盖同时传入的 Focus。因此需要同时设置两个目标时,先设置 Zoom,轮询到状态与位置稳定,再设置 Focus,最后检查两轴位置。

参考客户端以连续两次 Status=Normal 且两轴位置稳定作为完成条件的一部分,并继续核对目标值;单次成功响应不足以说明动作结束。区域聚焦使用 8192 尺度的原生坐标,不能直接填入预览窗口像素。

镜头测试覆盖 9 个案例,验证了动作响应、目标类型和稳定位置回读。物理倍率换算、毫米焦距和光学清晰度需要另行测量。

实现时保留结果层级

客户端可以区分“已接受”“已验证”“部分执行”“结果未知”和“已恢复”。这些是客户端状态,不是设备原生错误码。

请求发送后发生超时或断连,不能直接认定操作未执行;多表保存、播放、镜头重置等动作应先查询现状,再决定是否重试。能力表中的字段存在、原表保存成功和目标字段确实可修改,也应分别记录。

本次整理共观测到 100 种 RPC、52 个配置名称,其中 44 个配置成功读取。这个数量用于说明观察范围,不代表每个字段、所有编码组合或全部型号都已完成验证。接入新固件时,应重新确认登录模式、能力、配置结构和关键操作回读结果。