createVideoDownloadStreamUrl(获取临时非加密流地址)
createVideoDownloadStreamUrl:获取临时非加密流地址
URL:https://openapi.lechange.cn/openapi/createVideoDownloadStreamUrl
公共请求体与签名:见 调用约定、签名。
为指定设备通道创流并获取 临时 RTSP 拉流地址,用于实时预览、本地录像回放或云录像回放。result.data.url 在 HTTP 响应中为 AES-256-GCM 密文(Base64);须用与请求相同的 params.token 按下文解密,得到 明文 RTSP 后再交给播放器或 FFmpeg。地址 不 写入云直播 liveToken 体系,到期失效后无需调用删除类接口。
请求参数
| 参数名 | 参数类型 | 是否必填 | 默认值 | 合法值 | 参数说明 |
|---|---|---|---|---|---|
| token | String | 是 | 管理员 accessToken(At_ 前缀) | ||
| deviceId | String | 是 | 设备序列号 | ||
| channelId | String | 是 | 非负整数的数字字符串 | 通道号 | |
| productId | String | 否 | IoT 设备产品 ID;非 IoT 设备可省略 | ||
| businessType | String | 是 | real、localRecord、cloudRecord | real 实时预览;localRecord 设备本地录像;cloudRecord 云录像 | |
| streamType | String | 否 | 0 | 0、1 | 码流类型:0 主码流,1 辅码流 |
| beginTime | String | 条件必填 | yyyy-MM-dd HH:mm:ss | businessType 为 localRecord 或 cloudRecord 时必填;录像开始时间 | |
| endTime | String | 条件必填 | yyyy-MM-dd HH:mm:ss | 同上;录像结束时间,不得早于 beginTime |
请求示例
实时预览
{
"system": {
"ver": "1.0",
"appId": "lcdxxxxxxxxx",
"sign": "xjhCQBoJ9hRDsCjyDcHjtDNzRZ3ZJezcawsfWeiaoxU=",
"time": 1603366946,
"nonce": "2e84717c-0f22-4424-8afe-19bab0d6d606"
},
"id": "051824b1-9655-4755-9cc2-adcc9ab5fef1",
"params": {
"token": "<yourAccessToken>",
"deviceId": "XXXXX000000XXXX",
"channelId": "0",
"businessType": "real",
"streamType": "0"
}
}
本地录像回放
{
"system": {
"ver": "1.0",
"appId": "lcdxxxxxxxxx",
"sign": "xjhCQBoJ9hRDsCjyDcHjtDNzRZ3ZJezcawsfWeiaoxU=",
"time": 1603366946,
"nonce": "2e84717c-0f22-4424-8afe-19bab0d6d606"
},
"id": "051824b1-9655-4755-9cc2-adcc9ab5fef1",
"params": {
"token": "<yourAccessToken>",
"deviceId": "XXXXX000000XXXX",
"channelId": "0",
"businessType": "localRecord",
"streamType": "0",
"beginTime": "2026-10-01 10:00:00",
"endTime": "2026-10-01 11:00:00"
}
}
响应字段
| 字段名 | 字段类型 | 字段说明 |
|---|---|---|
| url | String | AES-256-GCM 加密后的 RTSP(Base64,结构见 url 解密);解密结果为临时明文 RTSP |
响应示例
{
"result": {
"msg": "操作成功。",
"code": "0",
"data": {
"url": "Base64AesGcmCiphertextExample="
}
},
"id": "051824b1-9655-4755-9cc2-adcc9ab5fef1"
}
url 解密
步骤
- 使用与创流请求相同的
params.token,计算 AES-256 密钥:key = SHA-256( UTF-8( params.token + "ENCRYPTKEY" ) )(32 字节)。 - 对
url做 Base64 解码;前 12 字节为 IV,后续字节为密文,其中末尾 16 字节为 GCM 认证 tag(128 bit)。 - 使用
AES/GCM/NoPadding、GCM tag 128 bit 解密;明文为 UTF-8 字符串,即 RTSP URL。
Java 示例
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
public static String decryptStreamUrl(String encryptedBase64, String token) throws Exception {
byte[] key = MessageDigest.getInstance("SHA-256")
.digest((token + "ENCRYPTKEY").getBytes(StandardCharsets.UTF_8));
byte[] decode = Base64.getDecoder().decode(encryptedBase64);
byte[] iv = new byte[12];
byte[] encrypted = new byte[decode.length - 12];
System.arraycopy(decode, 0, iv, 0, 12);
System.arraycopy(decode, 12, encrypted, 0, encrypted.length);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
return new String(cipher.doFinal(encrypted), StandardCharsets.UTF_8);
}
Python 示例
依赖:pip install cryptography
import base64
import hashlib
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
def decrypt_stream_url(encrypted_base64: str, token: str) -> str:
key = hashlib.sha256((token + "ENCRYPTKEY").encode("utf-8")).digest()
raw = base64.b64decode(encrypted_base64)
iv, ciphertext_and_tag = raw[:12], raw[12:]
return AESGCM(key).decrypt(iv, ciphertext_and_tag, None).decode("utf-8")
Node.js 示例
const crypto = require('crypto')
function decryptStreamUrl(encryptedBase64, token) {
const key = crypto.createHash('sha256').update(token + 'ENCRYPTKEY', 'utf8').digest()
const buf = Buffer.from(encryptedBase64, 'base64')
const iv = buf.subarray(0, 12)
const body = buf.subarray(12)
const authTag = body.subarray(body.length - 16)
const ciphertext = body.subarray(0, body.length - 16)
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
decipher.setAuthTag(authTag)
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8')
}
错误码
本接口可能返回下列码;完整分组见 错误码。
网关与鉴权(SN / TK / OP)
| 错误码 | 错误码说明 | 下一步 |
|---|---|---|
SN1001 | 签名异常 | 按 签名 核对 sign |
TK1002 | token 无效或过期 | 重新获取 token |
OP1002 | 缺少必填参数 | 补必填字段 |
OP1003 | 参数不合法 | 按本页参数表核对 |
OP1039 | 分钟级限流 | 等到下一分钟再试 |
OP1040 | 调用超限 | 降低频率 |
DV1007 | 设备离线或超时 | 等设备上线 |
子账号(SUB)
| 错误码 | 错误码说明 | 下一步 |
|---|---|---|
SUB1001 | 子账号无指定权限 | 在 子账号策略 中补权限 |
SUB1002 | 子账号无任何设备权限 | 授权设备或调整策略 |
SUB1005 | 子账号 token 过期 | 重新 获取子账号 token |
SUB1006 | 子账号与设备无授权关系 | 核对 Resource 与设备序列号 |
SUB1008 | 子账号未绑定当前应用 | 先创建并绑定子账号 |
SUB1007 | 创建子账号失败 | 核对参数后重试 |
SUB1009 | 子账号创建失败,账号格式错误 | account 须为手机号或邮箱 |
SUB1010 | 子账号创建失败,账号已存在 | 勿重复绑定同一子账号到应用 |
更多见 错误码 · SUB。
设备访问与通道(DV10xx)
| 错误码 | 错误码说明 | 下一步 |
|---|---|---|
DV1007 | 设备离线或超时 | 等设备上线;低电休眠先 唤醒 |
DV1009 | 设备正忙 | 稍后重试 |
DV1012 | 分享/授权设备权限不足 | 核对子账号 Permission / Resource |
DV1019 | 能力集不支持 | 确认型号与 设备能力 |
DV1020 | 通道离线 | 检查通道与设备状态 |
DV1026 | 不支持此功能 | 换接口或升级固件 |
DV1028 | 未绑定或通道不存在 | 核对 deviceId / channelId |
DV1030 | 设备休眠 | 唤醒后再调 |
DV1057 | 通道不存在 | 核对通道号 |
DV1061 | 设备响应超时 | 检查设备网络 |
DV1062 | 需先唤醒设备 | 调用唤醒接口 |
DV1069 | 不满足操作条件 | 见 msg |
直播绑定(LV)
| 错误码 | 错误码说明 | 下一步 |
|---|---|---|
LV1001 | 直播已存在 | 先 解绑 或换源 |
LV1002 | 直播不存在 | 先创建直播 |
LV1005 | 直播信息不存在 | 核对直播 id |
LV1006 | 同 rtsp 源当日仅可绑一次 | 次日再绑 |
LV1008 | 需实名认证 | 在开放平台完善主体信息 |
LV1020 | 已绑定直播无法删除 | 先解绑 |
OP1021 | 获取播放地址失败 | 检查直播状态与套餐 |
OP1022 | 需开启云解码授权 | 按返回说明在 App 中授权 |
更多见 错误码 · LV。
说明
- 解密
url须使用创流时传入的params.token;token 不一致将无法解密。 businessType=real需要实时预览权限;录像类需要回放权限。无录像片或时间非法时可能返回参数或业务错误码。- 与 bindDeviceLive 等云直播接口不同:本接口返回 临时 RTSP(响应字段加密),非 HLS/FLV/RTMP 云直播地址。