第一步:
获取APP_KEY,ACCESS_TOKEN,可以在腾讯云控制台页面获取
第二步:
调用【新建视频流会话接口】获取[sessionId]会话唯一标识,[PlayStreamAddr]播放地址
调用步骤:
public VhNewSessionResultDTO newSession() {
VhNewSessionResultDTO resultDTO = null;
try {
long currentTime = System.currentTimeMillis() / 1000;
String signature = getSignature(currentTime);
VhOperateDTO operateDTO = new VhOperateDTO();
VhNewSessionOperateDTO sessionOperateDTO = new VhNewSessionOperateDTO();
//test zhc
sessionOperateDTO.setUserId(TEST_USER_ID).setSessionId(new StringBuilder().append(SESSION_PREFIX).append(System.currentTimeMillis()).toString());
operateDTO.setPayload(sessionOperateDTO);
log.info("VirtualHumanPerformer.newSession-->operateDTO:{}", operateDTO);
VhResponseDTO<VhNewSessionResultDTO> responseDTO = tencentCloudVhClient.newSession(currentTime, signature, APP_KEY, operateDTO);
log.info("VirtualHumanPerformer.newSession-->responseDTO:{}", responseDTO);
if (ObjectUtils.isNotEmpty(responseDTO)) {
resultDTO = responseDTO.getPayload();
}
} catch (Exception e) {
log.error("VirtualHumanPerformer.newSession-->", e);
}
return resultDTO;
}
/**
* 新建视频流会话接口
*
* @param timestamp
* @param signature
* @param appkey
* @param operateDTO
* @return
*/
@PostMapping(value = "v2/ivh/streammanager/streamservice/newsession", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = "application/json;charset=UTF-8")
VhResponseDTO<VhNewSessionResultDTO> newSession(@RequestParam("timestamp") long timestamp,
@RequestParam("signature") String signature,
@RequestParam("appkey") String appkey,
@RequestBody VhOperateDTO operateDTO);
获取签名函数:
/**
* @param currentTime
* @return
*/
private String getSignature(long currentTime) {
try {
byte[] hashBytes = HmacSha256(new StringBuilder()
.append(TencentContents.APP_KEY)
.append(StringPool.EQUAL)
.append(APP_KEY)
.append(StringPool.AMPERSAND)
.append(TencentContents.TIMESTAMP)
.append(StringPool.EQUAL)
.append(currentTime).toString(), ACCESS_TOKEN);
BASE64Encoder base = new BASE64Encoder();
String encodeStr = base.encode(hashBytes);
String signature = URLEncoder.encode(encodeStr, StringPool.UTF8);
return signature;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @param strText
* @param strKey
* @return
*/
private byte[] HmacSha256(final String strText, final String strKey) {
byte[] strResult = null;
try {
Mac sha256_HMAC = Mac.getInstance(Constants.H_MAC_SHA256);
SecretKeySpec secretKey = new SecretKeySpec(strKey.getBytes(), Constants.H_MAC_SHA256);
sha256_HMAC.init(secretKey);
strResult = sha256_HMAC.doFinal(strText.getBytes());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return strResult;
}
,第三步:建立会话长链接(websocket)-监听数智人播报内容,以及播报状态
关键代码:
/**
* 建立会话长链接(websocket)
*
* @param sessionId
*/
public void commandChannelWebsocket(String sessionId) {
log.info("VirtualHumanPerformer.commandChannelWebsocket-->sessionId:{}", sessionId);
String url = new StringBuilder()
.append(WSS_HOST)
.append(WEB_SOCKET_URL)
.append(StringPool.QUESTION)
.append(TencentContents.REQUEST_ID)
.append(StringPool.EQUAL)
.append(sessionId)
.toString();
//签名
String urlSign = this.getSignature(url);
try {
VirtualHumanWebSocketClient virtualHumanWebSocketClient = new VirtualHumanWebSocketClient(new URI(urlSign), sessionId, interviewSessionUserRelaManger, interviewStatusManager);
virtualHumanWebSocketClient.connect();
} catch (Exception e) {
log.error("VirtualHumanPerformer.commandChannelWebsocket-->e", e);
}
}
签名函数:
/**
* 获取websocket连接路径及加密信息
*
* @param urlStr
* @return
*/
private String getSignature(String urlStr) {
try {
URI url = new URI(urlStr);
String timeStamp = String.valueOf(new Date().getTime() / 1000);
Map<String, String> paramMap = new HashMap<>();
paramMap.put(TencentContents.APP_KEY, APP_KEY);
paramMap.put(TencentContents.TIMESTAMP, timeStamp);
String queryParam = url.getQuery();
if (StringUtils.isNoneBlank(queryParam)) {
String[] arr = queryParam.split(StringPool.AMPERSAND);
for (int i = 0; i < arr.length; i++) {
String key = arr[i].substring(Constants.ZERO, arr[i].indexOf(StringPool.EQUAL));
String value = arr[i].substring(arr[i].indexOf(StringPool.EQUAL) + 1);
paramMap.put(key, value);
}
}
List<String> paramList = paramMap.keySet().stream().collect(Collectors.toList());
//字典排序
Collections.sort(paramList);
//拼接参数
String paramString = paramList.stream().map(p -> p + StringPool.EQUAL + paramMap.get(p)).collect(Collectors.joining(StringPool.AMPERSAND));
String signature =
Base64.encodeBase64String(this.HmacSha256(paramString, ACCESS_TOKEN));
//urlEncoder
String signature_encode = URLEncoder.encode(signature, StandardCharsets.UTF_8.toString());
paramString += new StringBuilder().append(StringPool.AMPERSAND).append(TencentContents.SIGNATURE).append(StringPool.EQUAL).append(signature_encode).toString();
if (url.toString().indexOf(StringPool.QUESTION) >= Constants.ZERO) {
return url.toString().substring(Constants.ZERO, url.toString().indexOf(StringPool.QUESTION)) + StringPool.QUESTION + paramString;
}
return url + StringPool.QUESTION + paramString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
websocket监听回调类
/**
* 腾讯云数智人websocket调用客户端
*/
@Slf4j
public class VirtualHumanWebSocketClient extends WebSocketClient {
private String sessionId;
public static ConcurrentHashMap<String, VirtualHumanWebSocketClient> sessionClientMap = new ConcurrentHashMap<>();
public VirtualHumanWebSocketClient(URI serverUri,
String sessionId) {
super(serverUri);
this.sessionId = sessionId;
}
/**
* 监听开启
*
* @param serverHandshake
*/
@Override
public void onOpen(ServerHandshake serverHandshake) {
log.info("VirtualHumanWebSocketClient.onOpen-->sessionId:{}", sessionId);
VirtualHumanWebSocketClient virtualHumanWebSocketClient = sessionClientMap.get(sessionId);
if (!Objects.isNull(virtualHumanWebSocketClient)) {
virtualHumanWebSocketClient.close();
}
sessionClientMap.put(sessionId, this);
}
/**
* 监听消息
*
* @param message
*/
@Override
public void onMessage(String message) {
log.info("VirtualHumanWebSocketClient.onMessage-->sessionId:{},message:{}", sessionId, message);
}
/**
* 监听关闭
*
* @param i
* @param s
* @param b
*/
@Override
public void onClose(int i, String s, boolean b) {
log.info("VirtualHumanWebSocketClient.onClose-->sessionId:{}", sessionId);
sessionClientMap.remove(sessionId);
}
/**
* 监听错误
*
* @param e
*/
@Override
public void onError(Exception e) {
log.info("VirtualHumanWebSocketClient.onError-->sessionId:{},e:{}", sessionId, e);
}
/**
* 发消息
*
* @param sessionId
* @param text
*/
public static void sendText(String sessionId, String text) {
VirtualHumanWebSocketClient virtualHumanWebSocketClient = sessionClientMap.get(sessionId);
if (Objects.isNull(virtualHumanWebSocketClient)) {
log.warn("VirtualHumanWebSocketClient.sendText-->virtualHumanWebSocketClient is null!");
return;
}
virtualHumanWebSocketClient.send(text);
}
/**
* 关闭
*
* @param sessionId
*/
public static void close(String sessionId) {
VirtualHumanWebSocketClient virtualHumanWebSocketClient = sessionClientMap.get(sessionId);
if (Objects.isNull(virtualHumanWebSocketClient)) {
log.warn("VirtualHumanWebSocketClient.close-->virtualHumanWebSocketClient is null!");
return;
}
virtualHumanWebSocketClient.close();
log.info("VirtualHumanWebSocketClient.close-->sessionId:{}", sessionId);
}
}
第四步:发送指令-进行播报
代码示例:
/**
* 发送指令
*
* @param sessionId
* @param text
* @return
*/
public Boolean sendCommand(String sessionId, String text) {
Boolean result = false;
try {
long currentTime = System.currentTimeMillis() / 1000;
String signature = getSignature(currentTime);
VhOperateDTO operateDTO = new VhOperateDTO();
operateDTO.setPayload(VhCommandOperateDTO.builder()
.sessionId(sessionId)
.command(TencentContents.VH_SEND_TEXT_COMMAND)
.data(VhCommandDataDTO.builder().text(text).interrupt(false).build())
.build());
log.info("VirtualHumanPerformer.sendCommand-->operateDTO:{}", operateDTO);
VhResponseDTO<Object> responseDTO = tencentCloudVhClient.command(currentTime, signature, APP_KEY, operateDTO);
log.info("VirtualHumanPerformer.sendCommand-->responseDTO:{}", responseDTO);
result = true;
} catch (Exception e) {
log.error("VirtualHumanPerformer.sendCommand-->", e);
}
return result;
}
/**
* 文本驱动接口
*
* @param timestamp
* @param signature
* @param appkey
* @param operateDTO
* @return
*/
@PostMapping(value = "v2/ivh/streammanager/streamservice/command", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = "application/json;charset=UTF-8")
VhResponseDTO<Object> command(@RequestParam("timestamp") long timestamp,
@RequestParam("signature") String signature,
@RequestParam("appkey") String appkey,
@RequestBody VhOperateDTO operateDTO);
第五步:关闭会话-数智人停止播放
代码示例:
/**
* 关闭会话接口
*
* @param sessionId
* @return
*/
public Boolean closeSession(String sessionId) {
Boolean result = false;
try {
long currentTime = System.currentTimeMillis() / 1000;
String signature = getSignature(currentTime);
VhOperateDTO operateDTO = new VhOperateDTO();
operateDTO.setPayload(VhSessionOperateDTO.builder().sessionId(sessionId).build());
log.info("VirtualHumanPerformer.startSession-->operateDTO:{}", operateDTO);
VhResponseDTO<Object> responseDTO = tencentCloudVhClient.closeSession(currentTime, signature, APP_KEY, operateDTO);
log.info("VirtualHumanPerformer.startSession-->responseDTO:{}", responseDTO);
result = true;
} catch (Exception e) {
log.error("VirtualHumanPerformer.startSession-->", e);
}
return result;
}
/**
* 关闭会话接口
*
* @param timestamp
* @param signature
* @param appkey
* @param operateDTO
* @return
*/
@PostMapping(value = "v2/ivh/streammanager/streamservice/closesession", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = "application/json;charset=UTF-8")
VhResponseDTO<Object> closeSession(@RequestParam("timestamp") long timestamp,
@RequestParam("signature") String signature,
@RequestParam("appkey") String appkey,
@RequestBody VhOperateDTO operateDTO);
注意:
1、多个播报指令播报时,需要监听该会话下播报【结束】的状态再发送,否则播报指令将可能出现丢失,数智人只播报最新的一条指令。
2、播放流采用rtmp协议时,视频会存在3s左右的延迟,请妥善处理播报监听状态,以及数智人结束时的操作时机