网络媒体设备控制协议

网络媒体设备控制协议

媒体设备接入 API

摄像机通过 MQTT 协议连接到云平台,可以实现按需推流和云台控制等功能

消息格式

摄像机和平台使用统一的物联网平台消息格式,具体请参考 设备接入协议

{
    "did": String,
    "mid": String,
    "type": String,
    "sign": String,
    "token": String,
    "timestamp": Number,
    "data": {

    }   
}

公共属性:

  • did 必需,设备的 ID,平台范围内须保证唯一,实际情况下常使用 mac 地址或产品序列号等
  • mid 消息 ID,在 RPC 类消息中是必需,如 action 消息,应答消息和请求消息的 mid 必须相同,但不同消息间不能重复
  • type 必需,消息的类型,如 regiser, action, event
  • sign 设备签名,只在注册时需要
  • token 开启权限时必需,访问凭证,访问不同的资源需要提供相应的凭证
  • timestamp 可选,时间戳,表示消息的发送时间
  • data 消息的内容

设备注册

推流摄像机

一台推流设备只能推送一支流

摄像机->MQ: 订阅 (actions/{did})
设备管理服务->MQ: 订阅 (messages/{did})
摄像机->MQ: 注册 (摄像机)
MQ->设备管理服务: 注册
设备管理服务->MQ: 注册结果
MQ->摄像机: 注册结果
  • 推流摄像机通过 MQTT 连接到云平台
  • 推流摄像机向云台平注册并等待推流请求
  • 每次注册有效期期满前需要更新注册
消息上报主题

下面假设摄像机的设备 ID 为 001122334455

PUBLISH mqtt://{domain-name}/messages/001122334455

  • 应用程序 ID: 同 did
  • username 不需要
  • password 不需要
摄像机注册消息
{
    "did": "001122334455",
    "type": "register",
    "sign": "1234567890abcdef",
    "data": {

    }
}
  • did 摄像机设备 ID
  • sign 设备签名
  • type 总是为 register
  • data 摄像机设备描述信息

应答消息:

{
    "did": "001122334455",
    "type": "register",
    "result": {
        "id": "3434689jkjsdf34398095829",
        "expires": 3600,
        "token": "39409080928349028592"
    }
}
  • did 摄像机设备 ID
  • type 总是为 `register
  • result 注册结果
    • id 由服务端会配的 ID
    • token 设备访问令牌
    • expires 访问令牌有效时间,单位为秒

推流网关

一台网关设备可以关联多台摄像机并推送多支流

网络摄像机->推流网关: 绑定
推流网关->MQ: 订阅 'actions/{gateway}'
设备管理服务->MQ: 订阅 'messages/+'
推流网关->MQ: 注册 (网关)
MQ->设备管理服务: 注册
设备管理服务->MQ: 注册结果
MQ->推流网关: 注册结果
推流网关->MQ: 注册子设备 (摄像机)
MQ->设备管理服务: 注册
设备管理服务->MQ: 注册结果
MQ->推流网关: 注册结果
  • 一个网关可以管理多台摄像机,每台摄像机有一个唯一 DID
  • 网关通过 ONVIF 协议连接和管理摄像机
  • 摄像机可以在网关安装时绑定
  • 网关通过 MQTT 协议注册到云端
  • 网关代为每一个摄像机注册到云端并订阅相应 ID 的操作命令接收主题

设备通过注册来激活设备以及获取设备 token.

消息上报主题

下面假设网关的设备 ID 为 001122334455

PUBLISH mqtt://{domain-name}/messages/001122334455

  • 应用程序 ID: 同 did
  • username 不需要
  • password 不需要
网关注册消息
{
    "did": "001122334455",
    "type": "register",
    "sign": "012345678900123456789012:md5",
    "data": {
    }
}
  • did 网关设备 ID
  • sign 设备签名
  • type 总是为 register
  • data 网关设备描述信息

应答消息:

{
    "did": "001122334455",
    "type": "register",
    "result": {
        "id": "3434689jkjsdf34398095829",
        "expires": 3600,
        "token": "39409080928349028592"
    }
}
  • did 网关设备 ID
  • type 总是为 `register
  • result 注册结果
    • id 由服务端会配的 ID
    • token 设备访问令牌
    • expires 访问令牌有效时间,单位为秒

设备控制

摄像机->网关: 绑定
网关->MQ: 订阅(actions/{did})
设备管理服务->MQ: 订阅 (messages/+)
应用程序->设备管理服务: 操作命令 (HTTP)
设备管理服务->MQ: 操作命令
MQ->网关: 操作命令
网关->摄像机: 执行操作
摄像机->网关: 返回结果
网关->MQ: 操作结果 (messages/{did})
MQ->设备管理服务: 返回结果
设备管理服务->应用程序: 返回结果 (HTTP)
  • 因为 MQTT 是异常通信,所以操作命令和操作结果消息中的 mid 必需相同,且不要和其他消息重复
  • 网关收到摄像机操作命令后,根据指令内容操作相应 ID 的摄像机并回复操作结果
  • 操作请求消息和回复消息一一对应
  • 应用程序 HTTP 请求如果超时(默认 5 秒)没有收到应答,则操作失败

设备启动后需要订阅下面的主题来接收云端控制命令

SUBSCRIBE mqtt://{domain-name}/actions/001122334455

应答消息回复地址

PUBLISH mqtt://{domain-name}/messages/001122334455

网络摄像机

通用监控摄像机

  • 通过以太网通信
  • 通信协议: ONVIF/RTSP/RTMP
  • 数据格式: JSON/H.264

播放 - play

流媒体服务器通过发送 playstop 操作请求,控制摄像机按需推送音视频流,应用程序则只需直接连接和播放流媒体服务器的地址即可,不需要直接和摄像机通信。

流媒体服务器会定时发送通知,告知前端摄像机当前应用程序播放状态,前端摄像机可以根据请求自动开始或停止推送,以便节省带宽资源。

开始播放

由流媒体服务器发送消息给设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "play-token",
    "type": "action",
    "data": {
        "play": {
            "url": String,
            "players": Number
        }
    }
}
  • did 设备 ID
  • mid 消息 ID
  • type 消息类型,总是为 action
  • data 操作数据
    • play 播放操作
      • url 推流地址, 如 rtmp://{domain-name}/live/001122334455,摄像机需要推流到这个地址
      • players 当前正在播放的客户端数量,摄像机不需要关心这个值, 且不管有多个少客户端同时播放都只需要上传一支流

应答消息:

PUBLISH messages/001122334455

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "play": {
            "code": 0
        }
    }
}
  • 摄像机收到 play 命令后需要立即开始推流
  • play 会定期重复发送直到发送 stop 命令为止
心跳消息

为了使摄像机一直向服务端推送视频流,流媒体服务器需要定时 (每隔 60 秒) 重复发送 play 消息给设备

停止播放

当所有应用程序都停止播放后,流媒体服务器会通知摄像机停止推流 (通常会延时 60 秒后发送,以免频繁关停推流)

由流媒体服务器发送给设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "play-token",
    "type": "action",
    "data": {
        "stop": {
            "reason": "It's all play done"
        }
    }
}
  • did 设备 ID
  • mid 消息 ID
  • type 消息类型,总是为 action
  • data 操作数据
    • stop 停止播放操作
      • reason 建议提供,表示停止推流的原因

应答消息:

PUBLISH messages/001122334455

{
    "did": "001122334455",  // 必需, 设备 ID
    "mid": "1234",          // 必需, 消息 ID
    "type": "action",       // 必需, 消息类型
    "result": {
        "stop": {
            "code": 0       // 必需, 表示调用成功
        }
    }
}
  • 摄像机收到 stop 命令后可以立即停止推流
  • 摄像机在超过 120 秒后没有收到过 play 命令也需要停止推流

云台 - ptz

  • start 转动去台
  • stop 停止转动

开始转动

通过这个命令可以控制摄像机镜头上下左右摆动,或者放大缩小

{
    "did": "001122334455",
    "mid": "1234",
    "token": "ptz-token",
    "type": "action",
    "data": {
        "ptz": {
            "start": {
                "direction": Number,
                "speed": Number
            }
        }
    }
}
  • did 设备 ID
  • mid 消息 ID
  • type 消息类型,总是为 action
  • data 操作数据
    • ptz 云台操作
      • method 云台操作方法, 总是为 start
      • direction 方向
        • 0
        • 1
        • 2
        • 3
        • 4 左上
        • 5 左下
        • 6 右上
        • 7 右下
        • 8 放大
        • 9 缩小
      • speed 云台速度:
        • 0
        • 1 适中
        • 2

应答消息:

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "ptz": {
            "code": 0
        }
    }
}

停止转动

通过这个命令可以控制摄像机镜头停止摆动或缩放,就算摄像机没有成功收到这个命令,也需要在超过一定时间后自动停止摆动或缩放 (巡航等模式除外)

由流媒体服务器发送给设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "ptz-token",
    "type": "action",
    "data": {
        "ptz": {
            "stop": {}
        }
    }
}
  • did 设备 ID
  • mid 消息 ID
  • type 消息类型,总是为 action
  • data 操作数据
    • ptz 云台操作
      • method 云台操作方法, 总是为 stop

应答消息:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "ptz-token",
    "type": "action",
    "result": {
        "ptz": {
            "code": 0
        }
    }
}

错误应答消息

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "ptz": {
            "code": Number,
            "error": String
        }
    }
}
  • did 必需, 设备 ID
  • mid 必需, 消息 ID
  • type 必需, 消息类型,总是为 action
  • result 操作结果
    • ptz 云台操作
      • code 必需, 错误码
      • error 必需, 错误类型

错误码:

CODE ERROR
60000 该设备不支持云台控制
60001 没有云台控制权限
60002 设备云台旋转达到上限位
60003 设备云台旋转达到下限位
60004 设备云台旋转达到左限位
60005 设备云台旋转达到右限位
60006 云台当前操作失败,请稍后再试
60009 正在调用预置点
60020 不支持该命令

预置位 - preset

  • read 预置位列表
  • set 设置预置位
  • remove 删除预置位
  • goto 转到预置位

设置预置位

将当前云台位置设置为指定的预置位

由流媒体服务器发送给设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "preset-token",
    "type": "action",
    "data": {
        "preset": {
            "set": {
                "index": 1,
                "name": "[name]"
            }
        }
    }
}
  • did 摄像机 ID
  • mid 消息 ID, 不同消息不可重复
  • type 总是为 action
  • data 操作数据
    • preset 预置位操作
      • set 设置预置位操作
        • index 必需,预置位索引,1-128
        • name 可选,预置位显示名称

返回结果:

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "preset": {
            "code": 0
        }
    }
}
  • did 摄像机 ID
  • mid 消息 ID, 必须同请求消息一致
  • type 总是为 action
  • result 操作结果
    • preset 预置位操作
      • code 操作结果,0 表示成功

查询预置位列表

查询摄像机已设置的预置位列表

由流媒体服务器发送给设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "preset-token",
    "type": "action",
    "data": {
        "preset": {
            "list": {}
        }
    }
}
  • did 摄像机 ID
  • mid 消息 ID, 不同消息不可重复
  • type 总是为 action
  • data 操作数据
    • preset 预置位操作
      • list 查询预置位列表

返回结果:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "preset-token",
    "type": "action",
    "result": {
        "preset": {
            "presets": [{
                "index": 1,
                "name": "[name]"
            }]
        }
    }
}
  • did 摄像机 ID
  • mid 消息 ID, 必须同请求消息一致
  • type 总是为 action
  • result 操作结果
    • preset 预置位操作
      • presets 预置位数组
        • index 预置位索引
        • name 预置位名称

调用预置位

转到指定的预置位

由流媒体服务器发送给设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "preset-token",
    "type": "action",
    "data": {
        "preset": {
            "goto": {
                "index": Number
            }
        }
    }
}
  • did 摄像机 ID
  • mid 消息 ID, 不同消息不可重复
  • type 总是为 action
  • data 操作数据
    • preset 预置位操作
      • goto
        • index 预置位索引,1-128

应答消息:

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "preset": {
            "code": 0
        }
    }
}
  • did 摄像机 ID
  • mid 消息 ID, 必须同请求消息一致
  • type 总是为 action
  • result 操作结果
    • preset 预置位操作
      • code 操作结果 0 表示操作成功

删除预置位

删除指定的预置位

由流媒体服务器发送给设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "preset-token",
    "type": "action",
    "data": {
        "preset": {
            "remove": {
                "index": Number
            }
        }
    }
}
  • did 摄像机 ID
  • mid 消息 ID, 不同消息不可重复
  • type 总是为 action
  • data 操作数据
    • preset 预置位操作
      • remove
        • index 预置位索引,1-128

应答消息:

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "preset": {
            "code": 0
        }
    }
}
  • did 摄像机 ID
  • mid 消息 ID, 必须同请求消息一致
  • type 总是为 action
  • result 操作结果
    • preset 预置位操作
      • code 操作结果 0 表示操作成功

错误应答消息

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "{action}": {
            "code": Number,
            "error": String
        }
    }
}
  • did 必需, 设备 ID
  • mid 必需, 消息 ID
  • type 必需, 消息类型,总是为 action
  • result 操作结果
    • action 操作名称
      • code 必需, 错误码
      • error 必需, 错误类型

错误码:

CODE ERROR
60000 该设备不支持云台控制
60001 没有云台控制权限
60006 云台当前操作失败,请稍后再试
60009 正在调用预置点
60020 不支持该命令
60007 预置点个数超过最大值
60010 该预置点已经是当前位置
60011 预置点不存在

图像抓拍

请求消息

由流媒体服务器发送给设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "capture-token",
    "type": "action",
    "data": {
        "capture": {
            "url": "https://{domain-name}/v2/storage/upload"
        }
    }
}
  • did 必需, 设备 ID
  • mid 必需, 消息 ID
  • type 必需, 消息类型,总是为 action
  • result 操作结果
    • capture 抓拍操作
      • url 必需, 用于上传抓拍后的图片的地址

应答消息

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "capture": {
            "url": "https://{domain-name}/v2/storage/file/20191010-101010.jpeg"
        }
    }
}
  • did 必需, 设备 ID
  • mid 必需, 消息 ID
  • type 必需, 消息类型,总是为 action
  • result 操作结果
    • capture 抓拍操作
      • url 必需, 抓拍后的图片的访问地址

参数设置

应用程序可以设置部分最常用的摄像机设置,方便日常使用

读取设置

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "token": "config-token",
    "data": {
        "read": ["config"]
    }
}
  • 图像设置

    • 画面反转
      • 是/否
    • 日夜模式
      • 白天模式
      • 晚上模式
      • 自动
    • 时间水印
      • 是/否
  • 告警

    • 移动侦测
      • 是/否
    • 灵敏度
      • 高/中/低
    • 时间段
      • 全天
        • 是/否
      • 开始时间
      • 结束时间
      • 重复
        • 周一,周二,周三,周四,周五,周六,周日
    • 频率
      • 3/5/10/30/60 一次
    • 检测是否有人
      • 是/否
  • 云台控制

    • 巡航
  • 存储管理

    • 录像模式
      • 不间断模式
      • 触发模式

修改设置

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "token": "config-token",
    "data": {
        "write": {
            "config": {
                "name": <value>
            }
        }
    }
}

网络录像机

录像和回放

摄像机或网络录像机设备可以包含录像组件,通过录像组件可以实现录像查询以及回放等功能。

查询录像时间段列表

查询指定时间范围内有录像的时间段

通过消息队列发送给前端设备:

{
    "did": "001122334455",
    "mid": "1234",
    "token": "replay-token",
    "type": "action",
    "data": {
        "segment": {
            "find": {
                "start": Date,
                "end": Date,
                "channel": Number,
                "stream": Number,
                "type": Number
            }
        }
    }
}
  • did 必需, 设备 ID
  • mid 必需, 消息 ID
  • type 必需, 消息类型,总是为 action
  • data 操作数据
    • segment 查询录像文件
      • find
        • start 开始时间,Unix 时间戳,单位为毫秒
        • end 结束时间,Unix 时间戳,单位为毫秒
        • channel 录像通道,从 1 开始
        • stream 码流类型,0 表示主码流,1 表示次码流
        • type 录像类型,1 表示普通录像类型

应答消息

{
    "did": "001122334455",
    "mid": "1234",
    "type": "action",
    "result": {
        "segment": {
            "find": {
                "params": {},
                "segments": [{
                    "start": Date,
                    "end": Date,
                    "type": Number
                }]
            }
        }
    }
}
  • did 必需, 设备 ID
  • mid 必需, 消息 ID
  • type 必需, 消息类型,总是为 action
  • result 操作结果
    • segment查询回放文件操作
      • params 查询参数
      • segments 时间段列表
        • start 开始时间,Unix 时间戳,单位为毫秒
        • end 结束时间,Unix 时间戳,单位为毫秒
        • type 录像文件类型

录像回放推流

  • 通过 RTSP 协议从 NVR 拉取录制的音视频流
  • 通过 RTMP 协议将音视频流推送到媒体服务器
  • 通过 MQTT 协议控制回放速度
回放推流地址

rtmp://{domain}:{port}/v2/meida/live/{did}_replay_{channel}_{stream}_{start}_{end}

  • domain 媒体服务器 IP 地址或域名
  • port 媒体服务器端口, 默认为 1935
  • did NVR 设备 ID
  • channel 录像通道, 从 1 开始
  • stream 码流类型, 0 表示主码流, 1 表示次码流
  • start 开始时间,如 20200725112233
  • end 结束时间,如 20200725235959

回放地址

http://{domain}/v2/meida/live/{did}_replay_{channel}_{stream}_{start}_{end}.flv

  • 参数同推流地址

上传录像文件

注意:这个接口还在实验中,随时可能更改

录像文件推送消息

{
    "did": "001122334455",
    "mid": "1234",
    "token": "upload-token",
    "type": "action",
    "data": {
        "upload": {
            "url": 'http://{domain-name}/v2/live/upload/:did/',
            "file": '{filename}'
        }
    }
}

摄像机推送文件

POST http://{domain-name}/v2/media/upload/:did/?token={token}

服务器缓存时间

默认情况下服务器会缓存 7 天,缓存期间可随时访问,期满后会自动删除

拉取录像文件

注意:这个接口还在实验中,随时可能更改

应用程序下载录像文件

GET http://{domain-name}/v2/media/upload/:did/:filename?token={token}

安全

设备密钥

设备密钥由云服务生成,保存在数据库中。同时需要烧录到相应 ID 的摄像机设备中。

设备需要对发往服务端的注册消息使用设备密钥进行签名,这样服务器可以用来验证是否是合法的请求。

同样服务器也可以使用设备密钥对发往设备的消息进行签名,防止其他人仿冒服务器发送消息给摄像机。

注册签名

认证时不可以直接在网上传输设备密钥,而是使用密钥签名的方式:

简单签名方式:

sign = md5string(did + ':' + deviceSecret)

基于时间戳签名方式:

sign = md5string(did + ':' + deviceSecret + ':' + timestamp)

基于时间戳的签名方式更安全,但需要设备有时间同步能力

注册成功后服务器会返回一个设备访问 token, 可以用来和服务器通信,这个 token 只有注册有效期内可用。

推流凭证

推流时使用设备访问 token 即可:

rtmp://{domain-name}/live/test?token={token}

image-modal