Cloud Recording Callback
配置信息
超时重试
事件回调服务器在发送消息通知后,5秒内没有收到您的服务器的响应,即认为通知失败。首次通知失败后会立即重试,后续失败会以10秒的间隔继续重试,直到消息存续时间超过1分钟,不再重试。
回调接口
您可以提供一个接收回调的 HTTP/HTTPS 服务网关来订阅回调消息。当相关事件发生时,云录制系统会回调事件通知到您的消息接收服务器。
事件回调消息格式
事件回调消息以 HTTP/HTTPS POST 请求发送给您的服务器,其中:
字符编码格式:UTF-8。
请求:body 格式为 JSON。
应答:HTTP STATUS CODE = 200,服务端忽略应答包具体内容,为了协议友好,建议客户应答内容携带 JSON: {"code":0}。
参数说明
事件回调消息的 header 中包含以下字段:
字段名 | 值 |
Content-Type | application/json |
Sign | 签名值 |
SdkAppId | sdk application id |
事件回调消息的 body 中包含以下字段:
字段名 | 类型 | 含义 |
EventGroupId | Number | 事件组 ID, 云端录制固定为3 |
EventType | Number | 回调通知的事件类型 |
CallbackTs | Number | 事件回调服务器向您的服务器发出回调请求的 Unix 时间戳,单位为毫秒 |
EventInfo | JSON Object | 事件信息 |
事件类型说明:
字段名 | 类型 | 含义 |
EVENT_TYPE_CLOUD_RECORDING_RECORDER_START | 云端录制录制模块启动 | |
EVENT_TYPE_CLOUD_RECORDING_RECORDER_STOP | 云端录制录制模块退出 | |
EVENT_TYPE_CLOUD_RECORDING_UPLOAD_START | 云端录制文件上传任务启动,仅在选择对象存储时回调 | |
EVENT_TYPE_CLOUD_RECORDING_FILE_INFO | 云端录制 生成 m3u8 索引文件,第一次生成并且上传成功后回调,仅在通过 API 录制 选择对象存储时回调 | |
EVENT_TYPE_CLOUD_RECORDING_UPLOAD_STOP | 云端录制文件上传结束,仅在选择对象存储时回调 | |
EVENT_TYPE_CLOUD_RECORDING_FAILOVER | 云端录制发生迁移,原有的录制任务被迁移到新负载上时触发 | |
EVENT_TYPE_CLOUD_RECORDING_FILE_SLICE | 云端录制 生成 m3u8 索引文件(切出第一个 ts 切片)生成后回调,仅在通过 API 录制 选择对象存储时回调 | |
EVENT_TYPE_CLOUD_RECORDING_DOWNLOAD_IMAGE_ERROR | 云端录制下载解码图片文件发生错误 | |
EVENT_TYPE_CLOUD_RECORDING_MP4_STOP | ||
EVENT_TYPE_CLOUD_RECORDING_VOD_COMMIT | 云端录制 VOD 录制任务上传媒体资源完成,在选择云点播时和通过控制台自动录制存储至 cos 时回调(录制文件结束后携带点播索引信息,请订阅此类型回调事件) | |
EVENT_TYPE_CLOUD_RECORDING_VOD_STOP | 云端录制 VOD 录制任务结束,仅在选择云点播时回调 |
注意:
301-309区间的回调状态为实时录制的中间状态,可以更加清晰的知晓录制任务的进行过程并记录状态,实际录制文件上传到点播成功会回调311事件,整体任务结束回调312事件。
事件信息说明:
字段名 | 类型 | 含义 |
RoomId | String/Number | 房间名(类型与客户端房间号类型一致) |
EventTs | Number | 时间发生的 Unix 时间戳,单位为秒 (不建议使用该字段,建议使用EventMsTs) |
EventMsTs | Number | 事件发生的 Unix 时间戳,单位为毫秒 |
UserId | String | 录制机器人的用户 ID |
TaskId | String | 录制 ID,一次云端录制任务唯一的 ID |
Payload | JsonObject | 根据不同事件类型定义不同 |
事件类型为301
(EVENT_TYPE_CLOUD_RECORDING_RECORDER_START)时 Payload 的定义:字段名 | 类型 | 含义 |
Status | Number | 0:代表录制模块启动成功 1:代表录制模块启动失败 |
{"EventGroupId": 3,"EventType": 301,"CallbackTs": 1622186275913,"EventInfo": {"RoomId": "xx","EventTs": "1622186275","EventMsTs": 1622186275757,"UserId": "xx","TaskId": "xx","Payload": {"Status": 0}}}
事件类型为302
(EVENT_TYPE_CLOUD_RECORDING_RECORDER_STOP)时 Payload 的定义:字段名 | 类型 | 含义 |
LeaveCode | Number | 0:代表录制模块正常调用停止录制退出 1:录制机器人被客户踢出房间 2:客户解散房间 3:服务器将录制机器人踢出 4:服务器解散房间 99:代表房间内除了录制机器人没有其他用户流,超过指定时间退出 100:房间超时退出 101:同一用户重复进入相同房间导致机器人退出 |
{"EventGroupId": 3,"EventType": 302,"CallbackTs": 1622186354806,"EventInfo": {"RoomId": "xx","EventTs": "1622186354","EventMsTs": 1622186275757,"UserId": "xx","TaskId": "xx","Payload": {"LeaveCode": 0}}}
事件类型为303
(EVENT_TYPE_CLOUD_RECORDING_UPLOAD_START)时 Payload 的定义:字段名 | 类型 | 含义 |
Status | Number | 0:代表上传模块正常启动
1:代表上传模块初始化失败。 |
事件类型为304
(EVENT_TYPE_CLOUD_RECORDING_FILE_INFO )时 Payload 的定义:字段名 | 类型 | 含义 |
FileList | String | 生成的 M3U8 文件名 |
事件类型为305
(EVENT_TYPE_CLOUD_RECORDING_UPLOAD_STOP)时 Payload 的定义:字段名 | 类型 | 含义 |
LeaveCode | Number | 0:代表此次录制上传任务已经完成,所有的文件均已上传到指定的第三方云存储
1:代表此次录制上传任务已经完成,但至少有一片文件滞留在服务器或者备份存储上
2:代表滞留在服务器或者备份存储上的文件已经恢复上传到指定的第三方云存储 注意:305代表hls文件上传结束事件 |
事件类型为306
(EVENT_TYPE_CLOUD_RECORDING_FAILOVER)时 Payload 的定义:字段名 | 类型 | 含义 |
Status | Number | 0:代表此次迁移已经完成 |
{"EventGroupId": 3,"EventType": 306,"CallbackTs": 1622191989674,"EventInfo": {"RoomId": "20015","EventTs": 1622191989,"EventMsTs": 1622186275757,"UserId": "xx","TaskId": "xx","Payload": {"Status": 0}}}
事件类型为307
(EVENT_TYPE_CLOUD_RECORDING_FILE_SLICE)时 Payload 的定义:字段名 | 类型 | 含义 |
FileName | String | m3u8 文件名 |
UserId | String | 录制文件对应的用户 ID |
TrackType | String | 音视频类型 audio/video/audio_video |
BeginTimeStamp | String | 录制开始时,服务器Unix时间戳(毫秒) |
事件类型为309
(EVENT_TYPE_CLOUD_RECORDING_DOWNLOAD_IMAGE_ERROR)时 Payload 的定义:字段名 | 类型 | 含义 |
Url | String | 下载失败的 URL |
{"EventGroupId": 3,"EventType": 309,"CallbackTs": 1622191989674,"EventInfo": {"RoomId": "20015","EventTs": 1622191989,"EventMsTs": 1622186275757,"UserId": "xx","TaskId": "xx","Payload": {"Url": "http://xx"}}}
事件类型为310
(EVENT_TYPE_CLOUD_RECORDING_MP4_STOP)时 Payload 的定义:说明:
310 是上传mp4文件到客户指定第三方云存储COS完成后的回调事件,一个录制任务可能会回调多个310事件(每个事件对应一个录制文件信息)
段名 | 类型 | 含义 |
Status | Number | 0:代表此次录制 mp4 任务已经正常退出,所有的文件均已上传到指定的第三方云存储 1:代表此次录制 mp4 任务已经正常退出,但至少有一片文件滞留在服务 器或者备份存储上
2:代表此次录制 mp4 任务异常退出(可能原因是拉取 cos 的 hls 文件失败) |
FileList | Array | 所有生成的 mp4 文件名 |
FileMessage | Array | 所有生成的 mp4 文件信息 |
FileName | String | mp4 文件名 |
UserId | String | mp4 文件对应的用户 ID(当录制模式为混流模式时,此字段为空) |
TrackType | String | audio 音频 / video 纯视频 / audio_video 音视频 |
MediaId | String | 主辅流标识,main 代表主流(摄像头),aux 代表辅流(屏幕分享),mix 代表混流录制 |
StartTimeStamp | Number | mp4 文件开始的 Unix 时间戳(毫秒) |
EndTimeStamp | Number | mp4 文件结束的 Unix 时间戳(毫秒) |
{"EventGroupId": 3,"EventType": 310,"CallbackTs": 1622191965320,"EventInfo": {"RoomId": "20015","EventTs": 1622191989,"EventMsTs": 1622186275757,"UserId": "xx","TaskId": "xx","Payload": {"Status": 0,"FileList": ["xxxx1.mp4", "xxxx2.mp4"],"FileMessage": [{"FileName": "xxxx1.mp4","UserId": "xxxx","TrackType": "audio_video","MediaId": "main","StartTimeStamp": 1622186279145,"EndTimeStamp": 1622186282145},{"FileName": "xxxx2.mp4","UserId": "xxxx","TrackType": "audio_video","MediaId": "main","StartTimeStamp": 1622186279153,"EndTimeStamp": 1622186282153}]}}}
事件类型为311
(EVENT_TYPE_CLOUD_RECORDING_VOD_COMMIT)时 Payload 的定义:字段名 | 类型 | 含义 |
Status | Number | 0:代表本录制文件正常上传至点播平台 1:代表本录制文件滞留在服务器或者备份存储上 2:代表本录制文件上传点播任务异常 |
UserId | String | 本录制文件对应的用户 ID(当录制模式为合流模式时,此字段为空) |
TrackType | String | audio 音频 / video 纯视频 / audio_video 音视频 |
MediaId | String | 主辅流标识,main代表主流(摄像头),aux代表辅流(屏幕分享),mix代表混流录制 |
FileId | String | 本录制文件在点播平台的唯一 ID |
VideoUrl | String | 本录制文件在点播平台的播放地址 |
CacheFile | String | 本录制文件对应的 MP4/HLS 文件名 |
StartTimeStamp | Number | 本录制文件开始的 UNIX 时间戳(毫秒) |
EndTimeStamp | Number | 本录制文件结束的 UNIX 时间戳(毫秒) |
Errmsg | String | statue 不为0时,对应的错误信息 |
上传成功的回调:
{"EventGroupId": 3,"EventType": 311,"CallbackTs": 1622191965320,"EventInfo": {"RoomId": "20015","EventTs": 1622191965,"EventMsTs": 1622186275757,"UserId": "xx","TaskId": "xx","Payload": {"Status": 0,"TencentVod": {"UserId": "xx","TrackType": "audio_video","MediaId": "main","FileId": "xxxx","VideoUrl": "http://xxxx","CacheFile": "xxxx.mp4","StartTimeStamp": 1622186279153,"EndTimeStamp": 1622186282153}}}}
上传失败的回调:
{"EventGroupId": 3,"EventType": 311,"CallbackTs": 1622191965320,"EventInfo": {"RoomId": "20015","EventTs": 1622191965,"EventMsTs": 1622186275757,"UserId": "xx","TaskId": "xx","Payload": {"Status": 1,"Errmsg": "xxx","TencentVod": {"UserId": "123","TrackType": "audio_video","CacheFile": "xxx.mp4"}}}}
说明:
录制完成收到311回调后到文件上传完成,根据您本次录制文件的大小不同可能需等待30s-3min。
事件类型为312
(EVENT_TYPE_CLOUD_RECORDING_VOD_STOP)时 Payload 的定义:字段名 | 类型 | 含义 |
Status | Number | 0:代表本次上传 VOD 任务已经正常退出 1:代表本次上传 VOD 任务异常退出 |
{"EventGroupId": 3,"EventType": 312,"CallbackTs": 1622191965320,"EventInfo": {"RoomId": "20015","EventTs": 1622191965,"EventMsTs": 1622186275757,"UserId": "xx","TaskId": "xx","Payload": {"Status": 0}}}
计算签名
签名由 HMAC SHA256 加密算法计算得出,您的事件回调接收服务器收到回调消息后,通过同样的方式计算出签名,相同则说明是腾讯云的实时音视频的事件回调,没有被伪造。签名的计算如下所示:
//签名 Sign 计算公式中 key 为计算签名 Sign 用的加密密钥。Sign = base64(hmacsha256(key, body))
注意:
body 为您收到回调请求的原始包体,不要做任何转化,示例如下:
body="{\n\t\"EventGroupId\":\t1,\n\t\"EventType\":\t103,\n\t\"CallbackTs\":\t1615554923704,\n\t\"EventInfo\":\t{\n\t\t\"RoomId\":\t12345,\n\t\t\"EventTs\":\t1608441737,\n\t\t\"UserId\":\t\"test\",\n\t\t\"UniqueId\":\t1615554922656,\n\t\t\"Role\":\t20,\n\t\t\"Reason\":\t1\n\t}\n}"
签名校验示例
import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import java.util.Base64;//# 功能:第三方回调sign校验//# 参数://# key:控制台配置的密钥key//# body:腾讯云回调返回的body体//# sign:腾讯云回调返回的签名值sign//# 返回值://# Status:OK 表示校验通过,FAIL 表示校验失败,具体原因参考Info//# Info:成功/失败信息public class checkSign {public static String getResultSign(String key, String body) throws Exception {Mac hmacSha256 = Mac.getInstance("HmacSHA256");SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");hmacSha256.init(secret_key);return Base64.getEncoder().encodeToString(hmacSha256.doFinal(body.getBytes()));}public static void main(String[] args) throws Exception {String key = "123654";String body = "{\n" + "\t\"EventGroupId\":\t2,\n" + "\t\"EventType\":\t204,\n" + "\t\"CallbackTs\":\t1664209748188,\n" + "\t\"EventInfo\":\t{\n" + "\t\t\"RoomId\":\t8489,\n" + "\t\t\"EventTs\":\t1664209748,\n" + "\t\t\"EventMsTs\":\t1664209748180,\n" + "\t\t\"UserId\":\t\"user_85034614\",\n" + "\t\t\"Reason\":\t0\n" + "\t}\n" + "}";String Sign = "kkoFeO3Oh2ZHnjtg8tEAQhtXK16/KI05W3BQff8IvGA=";String resultSign = getResultSign(key, body);if (resultSign.equals(Sign)) {System.out.println("{'Status': 'OK', 'Info': '校验通过'}");} else {System.out.println("{'Status': 'FAIL', 'Info': '校验失败'}");}}}
# -*- coding: utf8 -*-import hmacimport base64from hashlib import sha256# 功能:第三方回调sign校验# 参数:# key:控制台配置的密钥key# body:腾讯云回调返回的body体# sign:腾讯云回调返回的签名值sign# 返回值:# Status:OK 表示校验通过,FAIL 表示校验失败,具体原因参考Info# Info:成功/失败信息def checkSign(key, body, sign):temp_dict = {}computSign = base64.b64encode(hmac.new(key.encode('utf-8'), body.encode('utf-8'), digestmod=sha256).digest()).decode('utf-8')print(computSign)if computSign == sign:temp_dict['Status'] = 'OK'temp_dict['Info'] = '校验通过'return temp_dictelse:temp_dict['Status'] = 'FAIL'temp_dict['Info'] = '校验失败'return temp_dictif __name__ == '__main__':key = '123654'body = "{\n" + "\t\"EventGroupId\":\t2,\n" + "\t\"EventType\":\t204,\n" + "\t\"CallbackTs\":\t1664209748188,\n" + "\t\"EventInfo\":\t{\n" + "\t\t\"RoomId\":\t8489,\n" + "\t\t\"EventTs\":\t1664209748,\n" + "\t\t\"EventMsTs\":\t1664209748180,\n" + "\t\t\"UserId\":\t\"user_85034614\",\n" + "\t\t\"Reason\":\t0\n" + "\t}\n" + "}"sign = 'kkoFeO3Oh2ZHnjtg8tEAQhtXK16/KI05W3BQff8IvGA='result = checkSign(key, body, sign)print(result)
<?phpclass TlsEventSig {private $key = false;private $body = false;public function __construct( $key, $body ) {$this->key = $key;$this->body = $body;}private function __hmacsha256() {$hash = hash_hmac( 'sha256', $this->body, $this->key, true );return base64_encode( $hash);}public function genEventSig() {return $this->__hmacsha256();}}$key="789";$data="{\n\t\"EventGroupId\":\t1,\n\t\"EventType\":\t101,\n\t\"CallbackTs\":\t1608086882372,\n\t\"EventInfo\":\t{\n\t\t\"RoomId\":\t20222,\n\t\t\"EventTs\":\t1608086882,\n\t\t\"UserId\":\t\"222222_phone\"\n\t}\n}";$api = new TlsEventSig($key, $data);echo $api->genEventSig();
package mainimport "fmt"import ("crypto/hmac""crypto/sha256""encoding/base64")func main () {var data = "{\n\t\"EventGroupId\":\t1,\n\t\"EventType\":\t101,\n\t\"CallbackTs\":\t1608086882372,\n\t\"EventInfo\":\t{\n\t\t\"RoomId\":\t20222,\n\t\t\"EventTs\":\t1608086882,\n\t\t\"UserId\":\t\"222222_phone\"\n\t}\n}"var key = "789"//JSRUN引擎2.0,支持多达30种语言在线运行,全仿真在线交互输入输出。fmt.Println(hmacsha256(data,key))}func hmacsha256(data string, key string) string {h := hmac.New(sha256.New, []byte(key))h.Write([]byte(data))return base64.StdEncoding.EncodeToString(h.Sum(nil))}