回调配置用于设置接收各类回调事件通知的信息
房间回调事件:进入房间/退出房间等事件通知。
媒体回调事件:支持开始/停止推送视频数据、开始/停止推送音频数据 等事件通知。
JRTC 后续将支持更多回调类型。
事件回调服务支持将实时音视频业务下的事件,以 HTTP/HTTPS 请求的形式通知到您的服务器。
实时音视频 JRTC 控制台支持自助配置回调地址,添加回调通知秘钥及超时时间(秒),配置完成后即可接收带鉴权的事件回调通知。
1、通知消息体按参数名key的字典顺序排列拼接成字符转,每个参数(key-value)直接用&链接
1.1、消息源
{
"appId": "92bc34004019265a7b1ad17c6c7",
"notifyType": "MEDIA",
"notifyTs": "1625126096801",
"eventName": "EVENT_START_VIDEO",
"eventTs": "1644204008651",
"eventInfo": {
"roomId": 6666,
"userRoomId": "room-6666",
"peerId": 6666,
"userId": "userId-6666",
"nickName": "6666",
"streamInfo": {
"streamId": "6666.6666.2.1.480",
"kind": "VIDEO",
"deviceType": 1
}
}
}
1.2、待签名字符串
appId=92bc34004019265a7b1ad17c6c7&eventInfo={nickName=6666&peerId=6666&roomId=6666&streamInfo={deviceType=1&kind=VIDEO&streamId=6666.6666.2.1.480}&userId=userId-6666&userRoomId=room-6666}&eventName=EVENT_START_VIDEO&eventTs=1644204008651¬ifyType=MEDIA
1.3、签名后
b01NWGFiSi9USHdlQUlFdDQzQ0JSeXgybGpLaSs0dzZveXpaYndpUUtWdz0_
2、根据业务方配置的回调URL,拼接回调鉴权等信息后,通知业务方
3、签名及验证算法
业务方收到回调信息后对回调信息签名后与url中获取的tk(回调鉴权)进行比对,来判断是否为来自京东云视音频通讯服务的合法回调
参数及来源
字段名 | 类型 | 参数来源 |
---|---|---|
appId | String | jrtc应用ID |
notifyKey | String | 官网控制台回调中配置的通知秘钥 |
notifyMessage | String | 接收到的回调消息 |
nonce | String | 随机数-回调地址中获取 |
timestamp | long | 时间戳-回调地址中获取 ts ,单位:毫秒 |
notifyToken | String | 回调鉴权-回调地址中获取tk |
签名校验算法
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*;
/**
* Created on 2月 07, 2022.
*
* @author Zhangxi19
*/
@Slf4j
public class NotifyTokenUtil {
private static final String H_MAC_SHA_256 = "HmacSHA256";
public static Boolean validate(String appId, String notifyKey, String notifyMessage, String nonce, Long timestamp, String notifyToken) {
Long now = Instant.now().toEpochMilli();
if (now < timestamp) {
Map sourceMap = new TreeMap();
sourceMap.put("appId", appId);
sourceMap.put("notifyKey", notifyKey);
sourceMap.put("timestamp", timestamp);
sourceMap.put("notifyMessage", message2UriString(new StringBuffer(), notifyMessage, true));
String check = encrypt(JSONObject.toJSONString(sourceMap), nonce);
return check.equals(notifyToken);
}
log.info("NotifyTokenUtil.validate.warn, timestamp is expire......");
return false;
}
public static String message2UriString(StringBuffer buffer, String notifyMessage, boolean flag) {
if (StringUtils.isEmpty(notifyMessage)) {
return buffer.toString();
}
TreeMap treeMap = JSONObject.parseObject(notifyMessage, TreeMap.class);
Set<Map.Entry<Object, Object>> set = treeMap.entrySet();
Iterator<Map.Entry<Object, Object>> iterator = set.iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> next = iterator.next();
Object key = next.getKey();
Object val = next.getValue();
if (buffer.length() == 0 || flag == true) {
buffer.append(key).append("=");
flag = false;
} else {
buffer.append("&").append(key).append("=");
}
if (val instanceof JSONObject) {
buffer.append("{");
message2UriString(buffer, JSONObject.toJSONString(val), true);
buffer.append("}");
} else {
buffer.append(val);
}
}
return buffer.toString();
}
public static String encrypt(String strSrc, String nonce) {
try {
Mac hmac = Mac.getInstance(H_MAC_SHA_256);
SecretKeySpec keySpec256 = new SecretKeySpec(nonce.getBytes(StandardCharsets.UTF_8), H_MAC_SHA_256);
hmac.init(keySpec256);
byte[] byteSig = hmac.doFinal(strSrc.getBytes(StandardCharsets.UTF_8));
String origin = new String(Base64.getEncoder().encode(byteSig), StandardCharsets.UTF_8);
return replaceCharacter(Base64.getEncoder().encode(origin.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
private static String replaceCharacter(byte[] input) {
byte[] base64 = new String(input, StandardCharsets.UTF_8).getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < base64.length; ++i) {
switch (base64[i]) {
case '+':
base64[i] = '*';
break;
case '/':
base64[i] = '-';
break;
case '=':
base64[i] = '_';
break;
default:
break;
}
}
return new String(base64, StandardCharsets.UTF_8);
}
}
事件回调服务器在发送消息通知后,7秒内没有收到您的服务器的响应,即认为通知失败。首次通知失败后,第一次重试为1秒,第二次为2秒,第三次为4秒。
事件回调消息以 HTTP/HTTPS POST 请求发送给您的服务器,其中:
字符编码格式:UTF-8。
请求:body 格式为 JSON。
应答:HTTP STATUS CODE = 200,服务端忽略应答包具体内容。
示例:下述为“房间通知-进入房间”事件的消息示例
{
"appId": "92bc34004019265a7b1ad17c6c7",
"notifyType": "ROOM",
"notifyTs": "1625127894779",
"eventName": "EVENT_ENTER_ROOM",
"eventTs": "1625127894775",
"eventInfo": {
"roomId": 8926,
"userRoomId":"room-8926",
"peerId": 1222,
"userId":"test-ee124",
"nickName": "1222",
"streamInfo": null
}
}
示例:下述为“媒体通知-开始推送视频流”事件的消息示例
{
"appId": "92bc34004019265a7b1ad17c6c7",
"notifyType": "MEDIA",
"notifyTs": "1625126096801",
"eventName": "EVENT_START_VIDEO",
"eventTs": "1644204008651",
"eventInfo": {
"roomId": 6666,
"userRoomId":"room-6666",
"peerId": 6666,
"userId":"userId-6666",
"nickName": "6666",
"streamInfo": {
"streamId": "6666.6666.2.1.480",
"kind": "VIDEO",
"deviceType": 1
}
}
}
回调消息参数说明
事件回调消息的 body 中包含以下字段:
NotifyMessage
字段名 | 类型 | 含义 |
---|---|---|
appId | String | jrtc应用ID |
notifyType | String | 通知类型 ROOM-房间通知 MEDIA-媒体通知 |
notifyTs | Number | 事件回调服务器向您的服务器发出回调请求的 Unix 时间戳,单位为毫秒 |
eventName | String | 事件名称 |
eventInfo | EventInfo | 事件消息 |
EventInfo
字段名 | 类型 | 含义 |
---|---|---|
userRoomId | String | 用户自定义房间号 |
roomId | Number | 房间ID |
peerId | Number | peerId |
userId | String | 用户自定义id |
nickName | String | 用户在房间内的昵称 |
streamInfo | StreamInfo | 流信息 |
StreamInfo
字段名 | 类型 | 含义 |
---|---|---|
streamId | String | 流ID |
kind | String | 标识音视频 VIDEO-视频 AUDIO-音频 |
deviceType | String | 标识推流设备类型 业务方传递再回调给业务方 |
通知类型及事件说明
通知类型-notifyType
字段名 | 含义 |
---|---|
ROOM | 房间事件通知 |
MEDIA | 媒体事件通知 |
事件名称-eventName
字段名 | 含义 |
---|---|
EVENT_ENTER_ROOM | 进入房间 |
EVENT_EXIT_ROOM | 退出房间 |
EVENT_START_VIDEO | 开始推送视频数据 |
EVENT_STOP_VIDEO | 停止推送视频数据 |
EVENT_START_AUDIO | 开始推送音频数据 |
EVENT_STOP_AUDIO | 停止推送音频数据 |
示例:下述为“录制通知” 事件的消息示例
{
"vFramerate": "24.958",
"ver": "1.0",
"format": "mp4",
"userRoomId": "room-6666",
"bitrate": 4185250,
"aSampleRate": 16000,
"url": "https://xxx.com/meeting/record/92bc34004019265a7b1ad17c6c7/room-6666/room-6666_1cad6904-a399-44c3-95d6-30fbea3cb99a_20250418110100.mp4",
"duration": "1215294",
"aProfile": "LC",
"aChannel": 2,
"fileSize": "635788720",
"appId": "92bc34004019265a7b1ad17c6c7",
"width": 1920,
"event": "record_done",
"taskId": "6h93e1ca829b31676666667f0429",
"aCodec": "aac",
"height": 1080,
"md5": "a94510f2d38c43311114df99ac9ee",
"status": "success",
"vCodec": "h264"
}
字段名 | 类型 | 含义 |
---|---|---|
appId | String | jrtc应用ID |
userRoomId | String | 用户自定义房间号 |
event | String | 事件名 |
taskId | String | 录制任务ID |
url | String | 录制文件地址 https://endpoint/bucket/path |
format | String | 文件格式 |
duration | Long | 录制时长(秒) |
md5 | String | md5 |
status | String | 录制状态 |
fileSize | Long | 录制文件大小 Byte |
width | int | 视频宽 |
height | int | 视频高 |
bitrate | Long | 视频码率 |
vCodec | String | 视频编码 |
vFramerate | Double | 视频帧率 |
aSampleRate | int | 音频采样率 |
aCodec | String | 音频编码 |
aProfile | String | 音频编码档次 |
aChannel | int | 声道 |
ver | String | 版本 |
我们的产品专家为您找到最合适的产品/解决⽅案
1v1线上咨询获取售前专业咨询
专业产品顾问,随时随地沟通