腾讯实时音视频(TRTC)研究技术文档

整体流程

在这里插入图片描述
根据业务逻辑在service里签名传到前台,前台根据userId,和签名,房间号(房间号后台管理,每进入房间默认创建房间号,返回前端)进入房间,然后根据前台操作开启视频直播,此房间号后台记录后广播到用户展示的房间列表,其他用户通过点击房间号,进入各个房间,根据操作开启关闭摄像头通讯。

用户同步

2 .0 接口调用说明

2.1 请求URL

https://console.tim.qq.com/v4/im_open_login_svc/account_import?usersig=xxx&identifier=admin&sdkappid=88888888&random=99999999&contenttype=json

2.2 请求参数

URL中各参数的含义以及填写方式参见REST API简介。

2.3 最高调用频率

100次/秒。如需提升调用频率,请根据工单模板提交工单申请处理。

2.4 HTTP请求方式

POST

2.5 HTTP请求包体格式

JSON

2.6 请求包示例

{
   "Identifier":"test",
   "Nick":"test",
   "FaceUrl":"http://www.qq.com"
}

2.7 请求包字段说明

字段类型属性说明
IdentifierString必填用户名,长度不超过 32 字节
NickString选填用户昵称
FaceUrlString选填用户昵称
TypeInteger选填帐号类型,开发者默认无需填写,值0表示普通帐号,1表示机器人帐号。

2.8 应答包体示例

{
   "ActionStatus":"OK",
   "ErrorInfo":"",
   "ErrorCode":0
}

2.9 应答包字段说明

字段类型说明
ActionStatusString请求处理的结果,OK表示处理成功,FAIL表示失败。
ErrorCodeInteger错误码。
ErrorInfoString错误信息。

签名调用:


package com.example.demo.utils.tls_sigature;

import com.example.demo.utils.base64_url.base64_url;
import com.example.demo.utils.tls_dto.GenTLSSignatureResult;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.json.JSONObject;

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.util.Arrays;
import java.util.zip.Deflater;

/**
 * Created by liwenbin on 2018/9/17.
 */
public class Sigature {
    /**
     * @brief 生成 tls 票据,精简参数列表
     * @param skdAppid 应用的 sdkappid
     * @param identifier 用户 id
     * @param privStr 私钥文件内容
     * @param expire 有效期,以秒为单位,推荐时长一个月
     * @return
     * @throws IOException
     */
    public static GenTLSSignatureResult GenTLSSignatureEx(
            long skdAppid,
            String identifier,
            String privStr,
            long expire) throws IOException {

        GenTLSSignatureResult result = new GenTLSSignatureResult();

        Security.addProvider(new BouncyCastleProvider());
        Reader reader = new CharArrayReader(privStr.toCharArray());
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        PEMParser parser = new PEMParser(reader);
        Object obj = parser.readObject();
        parser.close();
        PrivateKey privKeyStruct = converter.getPrivateKey((PrivateKeyInfo) obj);

        String jsonString = "{"
                + "\"TLS.account_type\":\"" + 0 +"\","
                +"\"TLS.identifier\":\"" + identifier +"\","
                +"\"TLS.appid_at_3rd\":\"" + 0 +"\","
                +"\"TLS.sdk_appid\":\"" + skdAppid +"\","
                +"\"TLS.expire_after\":\"" + expire +"\","
                +"\"TLS.version\": \"201512300000\""
                +"}";

        String time = String.valueOf(System.currentTimeMillis()/1000);
        String SerialString =
                "TLS.appid_at_3rd:" + 0 + "\n" +
                        "TLS.account_type:" + 0 + "\n" +
                        "TLS.identifier:" + identifier + "\n" +
                        "TLS.sdk_appid:" + skdAppid + "\n" +
                        "TLS.time:" + time + "\n" +
                        "TLS.expire_after:" + expire +"\n";

        try {
            //Create Signature by SerialString
            Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
            signature.initSign(privKeyStruct);
            signature.update(SerialString.getBytes(Charset.forName("UTF-8")));
            byte[] signatureBytes = signature.sign();

            String sigTLS = Base64.encodeBase64String(signatureBytes);

            //Add TlsSig to jsonString
            JSONObject jsonObject= new JSONObject(jsonString);
            jsonObject.put("TLS.sig", (Object)sigTLS);
            jsonObject.put("TLS.time", (Object)time);
            jsonString = jsonObject.toString();

            //compression
            Deflater compresser = new Deflater();
            compresser.setInput(jsonString.getBytes(Charset.forName("UTF-8")));

            compresser.finish();
            byte [] compressBytes = new byte [512];
            int compressBytesLength = compresser.deflate(compressBytes);
            compresser.end();
            String userSig = new String(base64_url.base64EncodeUrl(Arrays.copyOfRange(compressBytes,0,compressBytesLength)));

            result.urlSig = userSig;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            result.errMessage = "generate usersig failed";
        }

        return result;
    }
}

返回类封装


package com.example.demo.utils.tls_dto;

/**
 * Created by liwenbin on 2018/9/17.
 */
public class GenTLSSignatureResult {
    //错误信息
    public String errMessage;
    //签名
    public String urlSig;
    public int expireTime;
    public int initTime;

    public String getErrMessage() {
        return errMessage;
    }

    public void setErrMessage(String errMessage) {
        this.errMessage = errMessage;
    }

    public String getUrlSig() {
        return urlSig;
    }

    public void setUrlSig(String urlSig) {
        this.urlSig = urlSig;
    }

    public int getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(int expireTime) {
        this.expireTime = expireTime;
    }

    public int getInitTime() {
        return initTime;
    }

    public void setInitTime(int initTime) {
        this.initTime = initTime;
    }
}

前端测试代码

客户一:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试</title>
</head>
<body >
<!-- 音视频 -->
<!--
    本地视频流
    muted:
        本地视频流的video必须置为静音(muted),否则会出现啸叫/回声等问题
        Mac / iPhone / iPad 需要用js设置muted属性
    autoplay:必须为激活状态
    playsinline:保证在ios safari中不全屏播放
 -->
<video id="localVideo" muted autoplay playsinline></video>
<!-- 远端视频流 -->
<video id="remoteVideo" autoplay playsinline></video>


<!-- 纯音频 -->
<!-- 本地音频流 / 这种场景下,localaudio 其实没有播放的必要了,可以用来调试 -->
<!-- <audio id="localAudioMedia"  muted autoplay></audio> -->
<!-- 远端音频流 -->
<!-- <audio id="remoteAudioMedia" autoplay ></audio> -->
<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
<script src="https://sqimg.qq.com/expert_qq/webrtc/3.0/WebRTCAPI.min.js"></script>
<script>
    //调试模式
    var RTC = new WebRTCAPI( {
        "userId": "sootower",
        "sdkAppId":  1400134005,
        "userSig": "eJxFkF1vgjAUhv8Lt1uWlloMuyOoodlQh7gEb5qOFuyEtkL92Jb991Wm8ebk5Hly8ua8P17*unpixkhOmaWo496zB7zHAYuzkZ2grLKic9jHoQ-ATR5F10utLhxADH0EwF1KLpSVlRzueq2tPrn13-WydjCdFjF5iyMe1knOkzrOyNyocfOSrILZUS2-dQpFThpQ6E62abM4kW205G02*fxA5cGgAodRsN8tZuspO6Nqv8m27Xvsbx5yMsf9*hbGd3T4zkXCEQAQuYGv0spWXDhGYxhgMAqunJWlPihL7ZcRQx2-fwBhWS0_",
        "debug":{
            "log": true, //是否在控制台打印调试日志 ,默认为false
            "vconsole": true, //是否展示 vconsole (方便在移动端查看日志)
            "uploadLog": true //是否上报日志
        }
    } );

    RTC.getLocalStream({
        video:true,
        audio:true,
        attributes:{
            width:640,
            height:480,
            frameRate:20
        }
    },function( info ){
        // info { stream }
        var stream = info.stream;
        document.getElementById("localVideo").srcObject = stream

        RTC.enterRoom( {
            roomid : 123456,
            role: 'user'
            // privateMapKey: "xxxxxxxxxxxxx" //不必须
        }, function(){
            //进房房间成功
            console.info( "==进入房间成功==" )
            RTC.startRTC({
                role: 'user',
                stream: stream
            }, function(){
                console.info( "==开始推流==" )
                //成功
            },function(){
                console.info( "==推流失败==" )
                //失败
            });
        } ,  function(data){
            console.info( "==进入房间失败==" )
            //进入房间失败
        } );
    },function ( error ){
        console.error( error )
    });

    RTC.on( 'onRemoteStreamUpdate' , function( data ){
        if( data && data.stream){
            var stream1 = data.stream
            console.debug( data.userId + 'enter this room with unique videoId '+ data.videoId  )
            document.querySelector("#remoteVideo").srcObject = stream1
//            document.getElementById("localVideo").srcObject = stream
//            RTC.updateStream({
//                role: "user",
//                stream: stream
//            }, function(){
//                console.debug('updateStream succ')
//            }, function(){
//                console.debug('updateStream failed')
//            });
        }else{
            console.debug( 'somebody enter this room without stream' )
        }
    })


</script>
</body>
</html>

客户二:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>标题</title>
</head>
<body >
<!-- 音视频 -->
<!--
    本地视频流
    muted:
        本地视频流的video必须置为静音(muted),否则会出现啸叫/回声等问题
        Mac / iPhone / iPad 需要用js设置muted属性
    autoplay:必须为激活状态
    playsinline:保证在ios safari中不全屏播放
 -->
<video id="localVideo" muted autoplay playsinline></video>
<!-- 远端视频流 -->
<video id="remoteVideo" autoplay playsinline></video>


<!-- 纯音频 -->
<!-- 本地音频流 / 这种场景下,localaudio 其实没有播放的必要了,可以用来调试 -->
<!-- <audio id="localAudioMedia"  muted autoplay></audio> -->
<!-- 远端音频流 -->
<!-- <audio id="remoteAudioMedia" autoplay ></audio> -->
<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
<script src="https://sqimg.qq.com/expert_qq/webrtc/3.0/WebRTCAPI.min.js"></script>
<script>

    //调试模式
    var RTC = new WebRTCAPI( {
        "userId": "liwenbin",
        "sdkAppId":  1400134005,
        "userSig": "eJxFkF1PgzAUhv8Ltxhp6Sqbd06aCPNjuGUKN02hnZwIpYG6yYz-XUZYvDkXz5M3b97z42wfN9fCGJBcWE5a6dw6yLkasfo20Cou9la1A-bpwkfoIg*q7aDRZ44wxT5B6F*CVNrCHsZcBUelc9CT6*BjgE8svY*SsKEbGr95D7UqA5uxU3gQxF3jfk1e4z7GK-c9ZC-ecrvaJVF59zzPauMtWZJSM8vBREnVz4*4ZK5X4DyvK*i6LE3lDhaXMvnJx3VDJZ4hhMlw6CQt1OrMKQnwDUV*MHFRFM2Xttz2Ro3v*P0DHgVXxg__",
        "debug":{
            "log": true, //是否在控制台打印调试日志 ,默认为false
            "vconsole": true, //是否展示 vconsole (方便在移动端查看日志)
            "uploadLog": true //是否上报日志
        }
    } );

    RTC.getLocalStream({
        video:true,
        audio:true,
        attributes:{
            width:640,
            height:480,
            frameRate:20
        }
    },function( info ){
        // info { stream }
        var stream = info.stream;
        document.getElementById("localVideo").srcObject = stream

        RTC.enterRoom( {
            roomid : 123456,
            role: 'user'
            // privateMapKey: "xxxxxxxxxxxxx" //不必须
        }, function(){
            //进房房间成功
            console.info( "==进入房间成功==" )
            RTC.startRTC({
                role: 'user',
                stream: stream
            }, function(){
                console.info( "==开始推流==" )
                //成功
            },function(){
                console.info( "==推流失败==" )
                //失败
            });
        } ,  function(data){
            console.info( "==进入房间失败==" )
            //进入房间失败
        } );
    },function ( error ){
        console.error( error )
    });

    RTC.on( 'onRemoteStreamUpdate' , function( data ){
        if( data && data.stream){
            var stream1 = data.stream
            console.debug( data.userId + 'enter this room with unique videoId '+ data.videoId  )
            document.querySelector("#remoteVideo").srcObject = stream1
//            document.getElementById("localVideo").srcObject = stream
//            RTC.updateStream({
//                role: "user",
//                stream: stream
//            }, function(){
//                console.debug('updateStream succ')
//            }, function(){
//                console.debug('updateStream failed')
//            });
        }else{
            console.debug( 'somebody enter this room without stream' )
        }
    })


</script>
</body>
</html>

总结

经测试,h5端IOS微信内置浏览器不支持调用,Android浏览器允许打开摄像头情况下调用正常。

Logo

加入腾讯云活动专属社区不迷路,这里有腾讯云相关活动第一手信息。

更多推荐