Java对接腾讯多人音视频房间示例

最近在对接腾讯的多人音视频房间,做一个类似于腾讯会议的工具,至于为什么不直接用腾讯会议,这个我也不知道,当然我也不敢问

首先是腾讯官方的文档地址:https://cloud.tencent.com/document/product/1690
我是后端所以只负责对接服务端api即可

在这里插入图片描述

从它这个简介可以看出这个REST API最主要的还是拼接请求的这个url

https://xxxxxx/$service/$version/$kind/$command?sdkappid=$SDKAppID&identifier=$identifier&usersig=$usersig&random=99999999&contenttype=json

这里面有三个参数是需要我们去开通账号获取的(sdkappid identifier usersig )
大家先点上面右上角那个控制台登录进去,没有账号的自己开通账号,可以先用体验版的测试,就是下面这个页面 项目管理

在这里插入图片描述

里面没有项目的话自己创建一个,自己搞不定的直接找他们客服,顾客是上帝,他们会热心给你解答的!!!
然后再点操作里面的项目配置
这里面有两个很重要的参数SDKAppId和Secret Key,先记录下来
注意测试版的只能用七天,七天到期了你可以再创建一个项目继续测试,没必要一开始就花钱

在这里插入图片描述

我们还需要拿到admin用户的用户名,这个用户名在另外一个平台才看的到,不过账号是一样的,你登录一个地方就可以直接访问 即时通信 IM 控制台

在这里插入图片描述

里面一开始就有一个默认的administrator,我们identifier直接用这个就可以

这里有个地方要注意,后端调用api的usersig是必须要用admin的用户名才可以的,也就是后端生成usersig固定写死用administrator。但是他们前端用户在进入房间的时候应该是用当前用户的userId生成签名就可以,不然后面回调的时候你就不知道谁进入了房间的

必要的参数拿到了我们就可以开始对接接口了,首先是对接的RoomctlUtil


import com.alibaba.fastjson.JSONObject;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.List;/*** 多人视频工具类** @author Sakura* @date 2023/12/14 18:19*/
@Component
@Log
public class RoomctlUtil {@Value("${tencent.roomctl.SDKAppId}")private Long SDKAppId;@Value("${tencent.roomctl.SecretKey}")private String SecretKey;// 这是固定用 administrator即可@Value("${tencent.roomctl.identifier}")private String identifier;// SDKAppID 所在国家/地区对应的专属域名private static final String BASE_URL = "https://roomkit.trtc.tencent-cloud.com/room_api/v1";// 创建房间apiprivate static final String ROOMCTL_CREATE_API = "/roomctl/create";// 修改房间信息apiprivate static final String ROOMCTL_UPDATE_API = "/roomctl/update/room_info";// 销毁房间apiprivate static final String ROOMCTL_DESTROY_API = "/roomctl/destroy";// 获取房间管理员apiprivate static final String ROOMCTL_GET_ADMIN_LIST_API = "/roomctl/get/admin_list";// 获取房间信息apiprivate static final String ROOMCTL_GET_ROOM_INFO_API = "/roomctl/get/room_info";// 配置回调地址apiprivate static final String ROOMCTL_CALLBACK_SET_API = "/callback/set";// 修改回调地址apiprivate static final String ROOMCTL_CALLBACK_UPDATE_API = "/callback/update";@AutowiredTxUrlUtil txUrlUtil;public String createRoomctl(String ownerId, String roomId, Integer maxSeatCount, String roomName,Integer roomType, Integer speechMode, Boolean disableVideo,Boolean disableAudio, Boolean disableMessage, Long scheduleStartTime,Long scheduleEndTime) {// 封装参数JSONObject json = new JSONObject();json.put("ownerId", ownerId); // 房主 IDjson.put("roomId", roomId); // 房间 IDJSONObject roomJson = new JSONObject();if (maxSeatCount != null) {roomJson.put("maxSeatCount", maxSeatCount); // 最大麦位数量,默认为套餐包上限,(直播最大16个 默认16)}if (!StringUtils.isEmpty(roomName)) {roomJson.put("roomName", roomName); // 房间名称。默认为房间 ID}if (roomType != null) {roomJson.put("roomType", roomType); // 房间类型:1为会议,2为直播 默认为:会议1}if (speechMode != null) {roomJson.put("speechMode", speechMode); // 麦控模式:1为自由发言模式,2为举手发言模式,3为就座后发言(抢麦位) 默认为:自由发言模式1}if (disableVideo != null) {roomJson.put("disableVideo", disableVideo); // 全体禁画 默认 false}if (disableAudio != null) {roomJson.put("disableAudio", disableAudio); // 全体静音 默认 false}if (disableMessage != null) {roomJson.put("disableMessage", disableMessage); // 禁止所有成员发送文本消息 默认 false}if (scheduleStartTime != null) {json.put("scheduleStartTime", scheduleStartTime); // 预定会议开始时间戳。如果没有设置会议开始时间,则默认为普通房间。(仅会议场景支持)}if (scheduleEndTime != null) {json.put("scheduleEndTime", scheduleEndTime); // 预定会议结束时间戳。如果没有设置会议结束时间,则默认为45分钟,超时房间30s内无人房间会被自动解散。}json.put("roomInfo", roomJson);String url = txUrlUtil.getTxUrl(BASE_URL, ROOMCTL_CREATE_API, identifier, SDKAppId);log.info("创建多人视频房间URL:" + url);log.info("创建多人视频房间参数:" + json);String resultStr = "ok";try {String result = HttpsRequestUtil.sendPostRequest(url, json.toString());log.info("创建多人视频房间返回:" + result);JSONObject resultJson = JSONObject.parseObject(result);if (!"Success".equals(resultJson.getString("errorMessage"))) {resultStr = resultJson.getString("errorMessage");}} catch (Exception e) {log.info("创建多人视频房间出现异常");e.printStackTrace();return "接口异常,请联系管理员";}return resultStr;}public String updateRoomctl(String roomId, Integer maxSeatCount, String roomName,Integer speechMode, Boolean disableVideo,Boolean disableAudio, Boolean disableMessage, Long scheduleStartTime,Long scheduleEndTime) {// 封装参数JSONObject json = new JSONObject();json.put("roomId", roomId); // 房间 IDif (maxSeatCount != null) {json.put("maxSeatCount", maxSeatCount); // 最大麦位数量,默认为套餐包上限,(直播最大16个 默认16)}if (!StringUtils.isEmpty(roomName)) {json.put("roomName", roomName); // 房间名称。默认为房间 ID}if (speechMode != null) {json.put("speechMode", speechMode); // 麦控模式:1为自由发言模式,2为举手发言模式,3为就座后发言(抢麦位) 默认为:自由发言模式1}if (disableVideo != null) {json.put("disableVideo", disableVideo); // 全体禁画 默认 false}if (disableAudio != null) {json.put("disableAudio", disableAudio); // 全体静音 默认 false}if (disableMessage != null) {json.put("disableMessage", disableMessage); // 禁止所有成员发送文本消息 默认 false}if (scheduleStartTime != null) {json.put("scheduleStartTime", scheduleStartTime); // 预定会议开始时间戳。如果没有设置会议开始时间,则默认为普通房间。(仅会议场景支持)}if (scheduleEndTime != null) {json.put("scheduleEndTime", scheduleEndTime); // 预定会议结束时间戳。如果没有设置会议结束时间,则默认为45分钟,超时房间30s内无人房间会被自动解散。}String url = txUrlUtil.getTxUrl(BASE_URL, ROOMCTL_UPDATE_API, identifier, SDKAppId);log.info("修改多人视频房间URL:" + url);log.info("修改多人视频房间参数:" + json);String resultStr = "ok";try {String result = HttpsRequestUtil.sendPostRequest(url, json.toString());log.info("修改多人视频房间返回:" + result);JSONObject resultJson = JSONObject.parseObject(result);if (!"Success".equals(resultJson.getString("errorMessage"))) {resultStr = resultJson.getString("errorMessage");}} catch (Exception e) {log.info("修改多人视频房间出现异常");e.printStackTrace();return "接口异常,请联系管理员";}return resultStr;}public String deleteRoomctl(String roomId) {// 封装参数JSONObject json = new JSONObject();json.put("roomId", roomId); // 房间 IDString url = txUrlUtil.getTxUrl(BASE_URL, ROOMCTL_DESTROY_API, identifier, SDKAppId);log.info("修改多人视频房间URL:" + url);log.info("修改多人视频房间参数:" + json);String resultStr = "ok";try {String result = HttpsRequestUtil.sendPostRequest(url, json.toString());log.info("修改多人视频房间返回:" + result);JSONObject resultJson = JSONObject.parseObject(result);if (!"Success".equals(resultJson.getString("errorMessage"))) {resultStr = resultJson.getString("errorMessage");}} catch (Exception e) {log.info("修改多人视频房间出现异常");e.printStackTrace();return "接口异常,请联系管理员";}return resultStr;}public String getTxRoomctlIfo(String roomId) {// 封装参数JSONObject json = new JSONObject();json.put("roomId", roomId); // 房间 IDString url = txUrlUtil.getTxUrl(BASE_URL, ROOMCTL_GET_ROOM_INFO_API, identifier, SDKAppId);log.info("获取多人视频房间信息URL:" + url);log.info("获取多人视频房间信息参数:" + json);String resultStr = "ok";try {String result = HttpsRequestUtil.sendPostRequest(url, json.toString());log.info("获取多人视频房间信息返回:" + result);JSONObject resultJson = JSONObject.parseObject(result);if (!"Success".equals(resultJson.getString("errorMessage"))) {resultStr = resultJson.getString("errorMessage");}} catch (Exception e) {log.info("获取多人视频房间信息出现异常");e.printStackTrace();return "接口异常,请联系管理员";}return resultStr;}public String getTxRoomctlAdmins(String roomId) {// 封装参数JSONObject json = new JSONObject();json.put("roomId", roomId); // 房间 IDString url = txUrlUtil.getTxUrl(BASE_URL, ROOMCTL_GET_ADMIN_LIST_API, identifier, SDKAppId);log.info("获取多人视频房间管理员URL:" + url);log.info("获取多人视频房间管理员参数:" + json);String resultStr = "ok";try {String result = HttpsRequestUtil.sendPostRequest(url, json.toString());log.info("获取多人视频房间管理员返回:" + result);JSONObject resultJson = JSONObject.parseObject(result);if (!"Success".equals(resultJson.getString("errorMessage"))) {resultStr = resultJson.getString("errorMessage");}} catch (Exception e) {log.info("获取多人视频房间管理员出现异常");e.printStackTrace();return "接口异常,请联系管理员";}return resultStr;}public String createCallback(String address, List<String> actions) {// 封装参数JSONObject json = new JSONObject();json.put("address", address); // 房间 IDjson.put("actions", actions); // 房间 IDString url = txUrlUtil.getTxUrl(BASE_URL, ROOMCTL_CALLBACK_SET_API, identifier, SDKAppId);log.info("配置回调地址URL:" + url);log.info("配置回调地址参数:" + json);String resultStr = "ok";try {String result = HttpsRequestUtil.sendPostRequest(url, json.toString());log.info("配置回调地址返回:" + result);JSONObject resultJson = JSONObject.parseObject(result);if (!"Success".equals(resultJson.getString("errorMessage"))) {resultStr = resultJson.getString("errorMessage");}} catch (Exception e) {log.info("配置回调地址出现异常");e.printStackTrace();return "接口异常,请联系管理员";}return resultStr;}public String updateCallback(String address, List<String> actions) {// 封装参数JSONObject json = new JSONObject();json.put("address", address); // 房间 IDjson.put("actions", actions); // 房间 IDString url = txUrlUtil.getTxUrl(BASE_URL, ROOMCTL_CALLBACK_UPDATE_API, identifier, SDKAppId);log.info("修改回调地址URL:" + url);log.info("修改回调地址参数:" + json);String resultStr = "ok";try {String result = HttpsRequestUtil.sendPostRequest(url, json.toString());log.info("修改回调地址返回:" + result);JSONObject resultJson = JSONObject.parseObject(result);if (!"Success".equals(resultJson.getString("errorMessage"))) {resultStr = resultJson.getString("errorMessage");}} catch (Exception e) {log.info("修改回调地址出现异常");e.printStackTrace();return "接口异常,请联系管理员";}return resultStr;}}

里面用到的工具类


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** @author Sakura* @date 2023/12/14 20:22*/
@Component
public class TxUrlUtil {@AutowiredUserSigUtil userSigUtil;public String getTxUrl(String baseUrl, String api, String identifier, Long SDKAppId) {StringBuffer urlSB = new StringBuffer();urlSB.append(baseUrl);urlSB.append(api);// 创建应用时即时通信 IM 控制台分配的 SDKAppIDurlSB.append("?sdkappid=");urlSB.append(SDKAppId);// 必须为 App 管理员账号urlSB.append("&identifier=");urlSB.append(identifier);// App 管理员账号生成的签名,具体操作请参见 生成 UserSigurlSB.append("&usersig=");String userSig = userSigUtil.getUserSig(identifier,86400l);urlSB.append(userSig);// 请输入随机的32位无符号整数,取值范围0 - 4294967295urlSB.append("&random=");urlSB.append(RandomNumberGenerator.generateRandomUnsignedInt());// 请求格式固定值为jsonurlSB.append("&contenttype=json");return urlSB.toString();}
}

import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** @author Sakura* @date 2023/12/15 16:29*/
@Component
@Log
public class UserSigUtil {@Value("${tencent.roomctl.SDKAppId}")private Long SDKAppId;@Value("${tencent.roomctl.SecretKey}")private String SecretKey;@Value("${tencent.roomctl.identifier}")private String identifier;public String getUserSig(String identifier, Long expire) {TLSSigAPIv2 tlsSigAPIv2 = new TLSSigAPIv2(SDKAppId, SecretKey);String userSig = tlsSigAPIv2.genUserSig(identifier, expire);return userSig;}
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;/*** @author Sakura* @date 2023/12/14 19:07*/
public class HttpsRequestUtil {public static String sendPostRequest(String urlString, String jsonInputString) throws IOException {URL url = new URL(urlString);HttpURLConnection connection = (HttpURLConnection) url.openConnection();// 设置请求方法为POSTconnection.setRequestMethod("POST");// 设置请求头connection.setRequestProperty("Content-Type", "application/json");connection.setRequestProperty("Accept", "application/json");// 启用输出流,用于发送请求数据connection.setDoOutput(true);try (OutputStream os = connection.getOutputStream()) {byte[] input = jsonInputString.getBytes("utf-8");os.write(input, 0, input.length);}// 获取响应try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) {StringBuilder response = new StringBuilder();String responseLine;while ((responseLine = br.readLine()) != null) {response.append(responseLine.trim());}return response.toString();} finally {// 关闭连接connection.disconnect();}}
}

下面这两个加密的直接从腾讯文档复制过来的


import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.security.*;import java.util.Arrays;
import java.util.zip.Deflater;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;import org.json.JSONObject;public class TLSSigAPIv2 {final private long sdkappid;final private String key;public TLSSigAPIv2(long sdkappid, String key) {this.sdkappid = sdkappid;this.key = key;}/*** 【功能说明】用于签发 TRTC 和 IM 服务中必须要使用的 UserSig 鉴权票据* <p>* 【参数说明】** @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。* @param expire - UserSig 票据的过期时间,单位是秒,比如 86400 代表生成的 UserSig 票据在一天后就无法再使用了。* @return usersig -生成的签名*/public String genUserSig(String userid, long expire) {return genUserSig(userid, expire, null);}/*** 【功能说明】* 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。* PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力:* - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。* - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。* 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】/【应用管理】/【应用信息】中打开“启动权限密钥”开关。* <p>* 【参数说明】** @param userid       - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。* @param expire       - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。* @param roomid       - 房间号,用于指定该 userid 可以进入的房间号* @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关:*                     - 第 1 位:0000 0001 = 1,创建房间的权限*                     - 第 2 位:0000 0010 = 2,加入房间的权限*                     - 第 3 位:0000 0100 = 4,发送语音的权限*                     - 第 4 位:0000 1000 = 8,接收语音的权限*                     - 第 5 位:0001 0000 = 16,发送视频的权限*                     - 第 6 位:0010 0000 = 32,接收视频的权限*                     - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限*                     - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限*                     - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。*                     - privilegeMap == 0010 1010 == 42  代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。* @return usersig - 生成带userbuf的签名*/public String genPrivateMapKey(String userid, long expire, long roomid, long privilegeMap) {byte[] userbuf = genUserBuf(userid, roomid, expire, privilegeMap, 0, "");  //生成userbufreturn genUserSig(userid, expire, userbuf);}/*** 【功能说明】* 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。* PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力:* - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。* - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。* 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】/【应用管理】/【应用信息】中打开“启动权限密钥”开关。* <p>* 【参数说明】** @param userid       - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。* @param expire       - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。* @param roomstr      - 字符串房间号,用于指定该 userid 可以进入的房间号* @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关:*                     - 第 1 位:0000 0001 = 1,创建房间的权限*                     - 第 2 位:0000 0010 = 2,加入房间的权限*                     - 第 3 位:0000 0100 = 4,发送语音的权限*                     - 第 4 位:0000 1000 = 8,接收语音的权限*                     - 第 5 位:0001 0000 = 16,发送视频的权限*                     - 第 6 位:0010 0000 = 32,接收视频的权限*                     - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限*                     - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限*                     - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。*                     - privilegeMap == 0010 1010 == 42  代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。* @return usersig - 生成带userbuf的签名*/public String genPrivateMapKeyWithStringRoomID(String userid, long expire, String roomstr, long privilegeMap) {byte[] userbuf = genUserBuf(userid, 0, expire, privilegeMap, 0, roomstr);  //生成userbufreturn genUserSig(userid, expire, userbuf);}private String hmacsha256(String identifier, long currTime, long expire, String base64Userbuf) {String contentToBeSigned = "TLS.identifier:" + identifier + "\n"+ "TLS.sdkappid:" + sdkappid + "\n"+ "TLS.time:" + currTime + "\n"+ "TLS.expire:" + expire + "\n";if (null != base64Userbuf) {contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";}try {byte[] byteKey = key.getBytes(StandardCharsets.UTF_8);Mac hmac = Mac.getInstance("HmacSHA256");SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256");hmac.init(keySpec);byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes(StandardCharsets.UTF_8));return (Base64.getEncoder().encodeToString(byteSig)).replaceAll("\\s*", "");} catch (NoSuchAlgorithmException | InvalidKeyException e) {return "";}}private String genUserSig(String userid, long expire, byte[] userbuf) {long currTime = System.currentTimeMillis() / 1000;JSONObject sigDoc = new JSONObject();sigDoc.put("TLS.ver", "2.0");sigDoc.put("TLS.identifier", userid);sigDoc.put("TLS.sdkappid", sdkappid);sigDoc.put("TLS.expire", expire);sigDoc.put("TLS.time", currTime);String base64UserBuf = null;if (null != userbuf) {base64UserBuf = Base64.getEncoder().encodeToString(userbuf).replaceAll("\\s*", "");sigDoc.put("TLS.userbuf", base64UserBuf);}String sig = hmacsha256(userid, currTime, expire, base64UserBuf);if (sig.length() == 0) {return "";}sigDoc.put("TLS.sig", sig);Deflater compressor = new Deflater();compressor.setInput(sigDoc.toString().getBytes(StandardCharsets.UTF_8));compressor.finish();byte[] compressedBytes = new byte[2048];int compressedBytesLength = compressor.deflate(compressedBytes);compressor.end();return (new String(Base64URL.base64EncodeUrl(Arrays.copyOfRange(compressedBytes,0, compressedBytesLength)))).replaceAll("\\s*", "");}public byte[] genUserBuf(String account, long dwAuthID, long dwExpTime,long dwPrivilegeMap, long dwAccountType, String RoomStr) {//视频校验位需要用到的字段,按照网络字节序放入buf中/*cVer    unsigned char/1 版本号,填0wAccountLen unsigned short /2   第三方自己的帐号长度account wAccountLen 第三方自己的帐号字符dwSdkAppid  unsigned int/4  sdkappiddwAuthID    unsigned int/4  群组号码dwExpTime   unsigned int/4  过期时间 ,直接使用填入的值dwPrivilegeMap  unsigned int/4  权限位,主播0xff,观众0xabdwAccountType   unsigned int/4  第三方帐号类型*/int accountLength = account.length();int roomStrLength = RoomStr.length();int offset = 0;int bufLength = 1 + 2 + accountLength + 20 ;if (roomStrLength > 0) {bufLength = bufLength + 2 + roomStrLength;}byte[] userbuf = new byte[bufLength];//cVerif (roomStrLength > 0) {userbuf[offset++] = 1;} else {userbuf[offset++] = 0;}//wAccountLenuserbuf[offset++] = (byte) ((accountLength & 0xFF00) >> 8);userbuf[offset++] = (byte) (accountLength & 0x00FF);//accountfor (; offset < 3 + accountLength; ++offset) {userbuf[offset] = (byte) account.charAt(offset - 3);}//dwSdkAppiduserbuf[offset++] = (byte) ((sdkappid & 0xFF000000) >> 24);userbuf[offset++] = (byte) ((sdkappid & 0x00FF0000) >> 16);userbuf[offset++] = (byte) ((sdkappid & 0x0000FF00) >> 8);userbuf[offset++] = (byte) (sdkappid & 0x000000FF);//dwAuthId,房间号//dwAuthId, room numberuserbuf[offset++] = (byte) ((dwAuthID & 0xFF000000) >> 24);userbuf[offset++] = (byte) ((dwAuthID & 0x00FF0000) >> 16);userbuf[offset++] = (byte) ((dwAuthID & 0x0000FF00) >> 8);userbuf[offset++] = (byte) (dwAuthID & 0x000000FF);//expire,过期时间,当前时间 + 有效期(单位:秒)//expire,Expiration time, current time + validity period (unit: seconds)long currTime = System.currentTimeMillis() / 1000;long expire = currTime + dwExpTime;userbuf[offset++] = (byte) ((expire & 0xFF000000) >> 24);userbuf[offset++] = (byte) ((expire & 0x00FF0000) >> 16);userbuf[offset++] = (byte) ((expire & 0x0000FF00) >> 8);userbuf[offset++] = (byte) (expire & 0x000000FF);//dwPrivilegeMap,权限位//dwPrivilegeMap,Permission bitsuserbuf[offset++] = (byte) ((dwPrivilegeMap & 0xFF000000) >> 24);userbuf[offset++] = (byte) ((dwPrivilegeMap & 0x00FF0000) >> 16);userbuf[offset++] = (byte) ((dwPrivilegeMap & 0x0000FF00) >> 8);userbuf[offset++] = (byte) (dwPrivilegeMap & 0x000000FF);//dwAccountType,账户类型//dwAccountType,account typeuserbuf[offset++] = (byte) ((dwAccountType & 0xFF000000) >> 24);userbuf[offset++] = (byte) ((dwAccountType & 0x00FF0000) >> 16);userbuf[offset++] = (byte) ((dwAccountType & 0x0000FF00) >> 8);userbuf[offset++] = (byte) (dwAccountType & 0x000000FF);if (roomStrLength > 0) {//roomStrLenuserbuf[offset++] = (byte) ((roomStrLength & 0xFF00) >> 8);userbuf[offset++] = (byte) (roomStrLength & 0x00FF);//roomStrfor (; offset < bufLength; ++offset) {userbuf[offset] = (byte) RoomStr.charAt(offset - (bufLength - roomStrLength));}}return userbuf;}
}

import java.util.Base64;public class Base64URL {public static byte[] base64EncodeUrl(byte[] input) {byte[] base64 = Base64.getEncoder().encode(input);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 base64;}
}

调用的就随便写一个示例吧
创建房间的时候其实只需要一个ownerId和roomId,ownerId建议用你自己的用户ID,roomId随机生成一个即可


/*** @Description: 多人视频管理* @Author: Sakura* @Date: 2023/12/14 17:44*/
@RestController
@RequestMapping("/txcloud/roomctl")
public class TxRoomctlController {@AutowiredRoomctlUtil roomctlUtil;/** 预约会议* @Description:  * @Author: Sakura* @Date: 2023/12/14 17:31*/@PostMapping("/scheduleMeeting")public Result scheduleMeeting(@RequestBody ScheduleMeetingRequest scheduleMeetingRequest) {// 调用腾讯创建房间接口String str = roomctlUtil.createRoomctl("123456", "123456", null,null, null, null, null, null, null, null, null);if (!"ok".equals(str)) {return Result.ERROR(str);}return Result.SUCCESS();}
}

然后又回到前端调用userSig的问题,一般SecretKey都放在后端不会给前端的, 这里我们写一个方法给他们调就可以

	@GetMapping("/getUserSig")public Result getInfo() {JSONObject jsonObject = new JSONObject();jsonObject.put("SDKAppId", SDKAppId);jsonObject.put("identifier", LoginUserUtils.getUserId());jsonObject.put("userSig", userSigUtil.getUserSig(LoginUserUtils.getUserId(), 86400l));return Result.SUCCESS(jsonObject);}

这样基础接口就对接完了,我们还需写一个回调监听用来接收回调信息,这样可以便于我们收集用户进入房间退出房间这些数据等
Java对接腾讯多人音视频房间回调接口示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/237160.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS自适应分辨率 amfe-flexible 和 postcss-pxtorem:大屏高宽自适应问题

前言 继上篇《CSS自适应分辨率 amfe-flexible 和 postcss-pxtorem》。 发现一个有趣的问题&#xff0c;文件 rem.js 中按照宽度设置自适应&#xff0c;适用于大多数页面&#xff0c;但当遇到大屏就不那么合适了。 问题 使用宽度&#xff0c;注意代码第2 和 4 行&#xff1a;…

JAVA面试题分享一百九十九:RabbitMQ 发布确认高级

目录 一、前言 二、发布确认SpringBoot版本 介绍 实战 添加配置类 消息生产者 消息消费者 消息生产者发布消息后的回调接口 三、回退消息 介绍 四、实战 修改配置文件 修改回调接口 五、备份交换机 介绍 实战 修改高级确认发布 配置类 报警消费者 一、前言 …

基于单片机智能自动浇花系统设计

**单片机设计介绍&#xff0c;基于单片机智能自动浇花系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的智能自动浇花系统是一种可以自动感知周围环境&#xff0c;并执行相应动作的系统。通过使用传感器检测土…

【Netty】NIO与Netty核心概念

目录 NIO编程NIO介绍NIO和BIO的比较缓冲区(Buffer)基本介绍常用API缓冲区对象创建添加数据读取数据 通道(Channel)基本介绍Channel常用类ServerSocketChannelSocketChannel Selector (选择器)基本介绍常用API介绍示例代码 NIO 三大核心原理 Netty核心概念Netty 介绍原生 NIO 存…

【QT表格-6】QTableWidget的currentCellChanged实现中途撤销

背景&#xff1a; 【QT表格-1】QStandardItem的堆内存释放需要单独delete&#xff0c;还是随QStandardItemModel的remove或clear自动销毁&#xff1f;-CSDN博客 【QT表格-2】QTableWidget单元格结束编辑操作endEditting_qtablewidget 单元格编辑事件-CSDN博客 【QT表格-3】Q…

【Chrome】ERR_SSL_PROTOCOL_ERROR问题

文章目录 前言一、下载二、使用步骤总结 前言 Edge升级最新版后&#xff0c;有的https访问不了&#xff0c;报如下错误 发现新版Chrome以及Chromium内核访问nginx ssl时报错&#xff0c;顺着这个思路接着查看到大佬的结论&#xff1a;服务器nginx使用的openssl版本过低&#…

C++入门【12-C++ 数组】

C 数组 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 数组的声明并不是声明一个个单独的变量&#xff0c;比如 number0、number1、...、number99&#xff0…

控制理论simulink+matlab

控制理论下的simulink和matlab使用 根轨迹LQR控制器简单使用状态观测器设计 根轨迹 z [-1]; %开环传递函数的零点 p [0 -2 -3 -4]; %开环传递函数的系统极点 k 1; %开环传递函数的系数&#xff0c;反映在比例上 g zpk(z,p,k); %生成开环传递函数%生成的传递函数如…

社交网络分析(汇总)

这里写自定义目录标题 写在最前面社交网络分析系列文章汇总目录 提纲问题一、社交网络相关定义和概念提纲问题1. 社交网络、社交网络分析&#xff1b;2. 六度分隔理论、贝肯数、顿巴数&#xff1b;3. 网络中的数学方法&#xff1a;马尔科夫过程和马尔科夫链、平均场理论、自组织…

使用JDBC对数据库进行简单操作

用Connection获得了数据库连接对象后&#xff0c;可以用Statement类型进行数据库操作。 在Statement对象中&#xff0c;有三种&#xff0c;分别是Statement&#xff0c;PrepareStatement&#xff0c;CallableStatement。 这三个的区别在于&#xff1a; Statement 用于执行不…

KubePi JWT 默认密钥权限绕过漏洞复现(CVE-2023-22463)

0x01 产品简介 KubePi 是一款简单易用的开源 Kubernetes 可视化管理面板。 0x02 漏洞概述 KubePi 存在权限绕过漏洞,攻击者可通过默认 JWT 密钥获取管理员权限控制整个平台,使用管理员权限操作核心的功能。 0x03 影响范围 KubePi <= 1.6.2 0x04 复现环境 FOFA: ti…

【Jenkins】远程API接口:Java 包装接口使用示例

jenkins-rest 库是一个面向对象的 Java 项目&#xff0c;它通过编程方式提供对 Jenkins REST API 的访问&#xff0c;以访问 Jenkins 提供的一些远程 API。它使用 jclouds 工具包构建&#xff0c;可以轻松扩展以支持更多 REST 端点。其功能集不断发展&#xff0c;用户可以通过拉…

怎么压缩过大的GIF图片?几个步骤轻松搞定!

GIF图片由于其图片格式&#xff0c;本身就会很大&#xff0c;但是微信QQ还有一些其他的社交平台对上传的表情包是有限制的&#xff0c;这个时候就需要借助一些图片处理工具对GIF进行压缩。 下面就向大家介绍三种好用的方法并展示具体的操作步骤。 一、使用嗨格式压缩大师进行压…

RouterSrv-路由功能

2023年全国网络系统管理赛项真题 模块B-Windows解析 题目 安装Remote Access服务开启路由转发,为当前实验环境提供路由功能。启用网络地址转换功能,实现内部客户端访问互联网资源。答题步骤 安装Remote Access服务开启路由转发,为当前实验环境提供路由功能。 配置网卡 加…

Day67力扣打卡

打卡记录 美丽塔 II&#xff08;前缀和 单调栈&#xff09; 链接 class Solution:def maximumSumOfHeights(self, maxHeights: List[int]) -> int:n len(maxHeights)stack collections.deque()pre, suf [0] * n, [0] * nfor i in range(n):while stack and maxHeights…

eNSP拓扑建立:RIP静态路由

实验名称&#xff1a; RIP动态路由协议 实验目的&#xff1a; 1、掌握RIPv1的配置方法 2、查看RIP路由分析过程 3、掌握测试RIP网络连通性的方法 步骤一:建立拓扑图 步骤二&#xff1a;配置PC终端的ip、子网掩码、网关。 步骤三&#xff1a;配置路由器&#xff0c;如图所示 步…

【K8s】3# 使用kuboard管理K8s集群(NFS存储安装)

文章目录 1.NFS是什么2.配置NFS服务器2.1.执行以下命令安装 nfs 服务器所需的软件包2.2.执行命令 vim /etc/exports&#xff0c;创建 exports 文件&#xff0c;文件内容如下2.3.执行以下命令&#xff0c;启动 nfs 服务2.4.检查配置是否生效 3.在客户端测试NFS3.1.执行以下命令安…

easyexcle处理复杂动态单元格合并问题,合并动态行列

GetMapping("getAddDelSummaryExport") ApiOperation("新增删除比例报表--导出") ApiImplicitParams({ApiImplicitParam(name "season", value "季节", paramType "query", dataType "String"),ApiImplicitPa…

Electron Vite打包后,部分图标未显示的解决方案

背景 这个问题&#xff0c;弄了一晚上&#xff0c;头都大了&#xff0c;找了一堆博客也没解决。主要参考这个&#xff1a;https://blog.csdn.net/m0_73845616/article/details/129741099。 下面讲一下我的解决方案。 解决方案 上面链接里的方法&#xff0c;我采用第二、三个都…

C# Onnx Yolov8 Detect 物体检测 多张图片同时推理

目录 效果 模型信息 项目 代码 下载 C# Onnx Yolov8 Detect 物体检测 多张图片同时推理 效果 模型信息 Model Properties ------------------------- date&#xff1a;2023-12-18T11:47:29.332397 description&#xff1a;Ultralytics YOLOv8n-detect model trained on …