APICloud平台使用融云模块实现音视频通话实践经验总结分享

81 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第0天,点击查看活动详情

需求概要:实现视频拨打、接听、挂断、视频界面大小窗口、点击小窗口实现大小窗口互换。

实现思路:一方拨打后,另一方要能收到相应事件,然后接听。接通后,渲染对方视频画面。那么己方视频画面什么时候渲染呢?对于呼叫方,可以在呼叫后开始渲染,也可以接通事件事件发生后再开始渲染。对于接通方可以在点击接听按钮后开始渲染,也可以在接通事件发生后开始渲染。

有了上述思路,在模块文档中查找相应api,编写代码,就可以验证我们的思路是否可以实现。如果遇到问题,再调整实现思路。

以下是融云模块文档链接:docs.apicloud.com/Cli...

简要介绍用到的主要API:

**startCall   发起音视频通话
addCallReceiveListener  音视频来电事件监听
accept 接听来电
addCallSessionListener 音视频通话事件的监听(包含响铃、接通、挂断等多个事件监听)setVideoView  设置视频区域
resetVideoView  重设视频区域
removeVideoView  移除视频区域
hangup 挂断**

下面讲解代码。

要调用音视频通话功能前应先调用 api.hasPermission 接口检查是否有麦克风、相机权限,如果没有,要先申请权限。

api.requestPermission({
            list: ['microphone', 'camera', 'storage', 'photos'],
            code: 1
        })

融云初始化成功之后,可添加相应事件的监听。didReceiveCall 接到来电事件后,弹出接听页面。接听后,会执行到 didConnect 事件, 此时可设置本地窗口 setVideoView ;稍后会执行到remoteUserDidJoin (对端用户加入通话事件),此时可以通过 setVideoView 设置对端用户窗口。通过videoViewBringToFront 接口将本地小窗口调整到最前方。

 apiready = function () {

    rong = api.require('rongCloud2');
    rong.init({
        huaweiPush: false
    }, function (ret, err) {
        if (ret.status == 'error') {
            api.toast({
                msg: err.code
            });
        } else {
            console.log('初始化成功');

            rong.setConnectionStatusListener(function (ret, err) {
                console.log("连接状态监听:" + ret.result.connectionStatus);
            });

            //收到来电事件监听
            rong.addCallReceiveListener({
                target: 'didReceiveCall'
            }, function (ret) {
                console.log('didReceiveCall:' + JSON.stringify(ret))
                callId = ret.callSession.callId;
                api.toast({
                    msg: '来电请接听'
                })

                fnopenbtnframe();     //打开接听、挂断按钮所在的frame
            });

            // 通话连接成功监听

            rong.addCallSessionListener({
                target: 'didConnect'    
            }, function (ret) {
                console.log('didConnect:' + JSON.stringify(ret))

                var myname = api.getPrefs({
                    sync: true,
                    key: 'myname'
                });

                //打开本地窗口
                fnsetVideoView(api.winWidth - 200, 100, 160, 200, myname);

                //将本地窗口显示到最前方
                setTimeout(function () {
                    rong.videoViewBringToFront({
                        userId: myname
                    })
                }, 1000)
            })
            
            //通话已结束的事件
            rong.addCallSessionListener({
                target: 'didDisconnect'
            }, function (ret) {
                console.log('didDisconnect:' + JSON.stringify(ret))
            })

            //对端用户加入了通话的事件
            rong.addCallSessionListener({
                target: 'remoteUserDidJoin'
            }, function (ret) {
                console.log("对端用户加入了通话的事件:" + JSON.stringify(ret));
                var uid = ret.userId;
                //设置远端窗口
                fnsetVideoView(0, 0, api.winWidth, api.winHeight, uid);

            });


            //监听视频区域点击事件,实现大小窗口切换
            rong.addVideoViewListener(function (ret) {

                //判断点击的是否是初始小窗口
                if (ret.userId == myname && meissmall) {

                    fnresetVideoView(0, 0, api.winWidth, api.winHeight, ret.userId);

                    fnresetVideoView(api.winWidth - 200, 100, 160, 200, hename);

                    meissmall = false;

                    setTimeout(function () {
                        rong.videoViewBringToFront({
                            userId: hename
                        })
                    }, 1000)

                    setTimeout(function () {
                        fnopenbtnframe()
                    }, 1200)
                }

                if (ret.userId == hename && !meissmall) {

                    fnresetVideoView(0, 0, api.winWidth, api.winHeight, ret.userId);

                    fnresetVideoView(api.winWidth - 200, 100, 160, 200, myname);

                    meissmall = true;

                    setTimeout(function () {
                        rong.videoViewBringToFront({
                            userId: myname
                        })
                    }, 1000)

                    setTimeout(function () {
                        fnopenbtnframe()
                    }, 1200)

                }

            })

           
        }
    });
};</code></pre><p>实现效果如下:<br/><img alt="" loading="lazy" referrerpolicy="no-referrer" src="/img/bVcYmd7" title=""/></p><p>其他经验总结:</p><p>返回错误码34001,重启loader可解决,可能换账号登录,wifi 同步重启loader 有缓存用户信息导致。</p><p>接听不到来电事件,可尝试用4g 网络测试。有些公司防火墙,或者电脑共享的wifi 热点网络有限制或不稳定。</p><p>以上经验都是无数次排错总结出来的,看了至少能帮你节省两个工作日。</p><p>最后贴下完整代码:</p><pre><code>&lt;!DOCTYPE HTML&gt;




<html>




<head>
<meta charset="utf-8">
<meta name="viewport"
content="maximum-scale=2.0,minimum-scale=1.0,user-scalable=1,width=device-width,initial-scale=1.0" />
<meta name="format-detection" content="telephone=no,email=no,date=no,address=no">
<title>Hello APP</title>
<link rel="stylesheet" type="text/css" href="../css/api.css" />
<script src="../script/sha1.js"></script>
<style>
body {
margin-top: 90px;



    }

    button {
        padding: 10px
    }

   
&lt;/style&gt;




</head>




<body id="bd">



&lt;button onclick="fnrequestPermission()"&gt;fnrequestPermission&lt;/button&gt;

&lt;input id="useName" placeholder="输入用户名" style="display: block" /&gt;
&lt;div id="stauseName" style="display: none"&gt;
    **用户已登录
&lt;/div&gt;
&lt;input id="fridendName" placeholder="输入好友用户名" style="" /&gt;
&lt;br&gt;
&lt;button onclick="login()"&gt;
    登录
&lt;/button&gt;
&lt;br&gt;
&lt;button onclick="fnstartCall()"&gt;
    fnstartCall
&lt;/button&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;p&gt;
&lt;ul&gt;
    &lt;li&gt;1. 测试步骤&lt;/li&gt;
    &lt;li&gt;2. 准备两部手机A和B&lt;/li&gt;
    &lt;li&gt;3. A手机在【输入用户名】【输入好友用户名】处分别输入a, b;然后点登录&lt;/li&gt;
    &lt;li&gt;4. B手机在【输入用户名】【输入好友用户名】处分别输入b, a;然后点登录&lt;/li&gt;
    &lt;li&gt;5. 一部手机点fnstartCall&lt;/li&gt;
    &lt;li&gt;6. 另一部手机在弹出‘来电请接听提示后’,会弹出底部按钮frame,点击【接听】&lt;/li&gt;
    &lt;li&gt;7. 接通后,弹出大小视频窗口。点击小窗口可实现切换。&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;




</body>
<script type="text/javascript" src="../script/api.js"></script>
<script type="text/javascript">
var rong;
var myname = '';
var hename = '';
var meissmall = true;



function fnrequestPermission() {
    api.requestPermission({
        list: ['microphone', 'camera', 'storage', 'photos'],
        code: 1
    })
}

apiready = function () {

    rong = api.require('rongCloud2');
    rong.init({
        huaweiPush: false
    }, function (ret, err) {
        if (ret.status == 'error') {
            api.toast({
                msg: err.code
            });
        } else {
            console.log('初始化成功');

            rong.setConnectionStatusListener(function (ret, err) {
                alert("setConnectionStatusListener::::::" + ret.result.connectionStatus);
            });

            rong.addCallReceiveListener({
                target: 'didReceiveCall'
            }, function (ret) {
                console.log('didReceiveCall:' + JSON.stringify(ret))
                callId = ret.callSession.callId;
                api.toast({
                    msg: '来电请接听'
                })
                fnopenbtnframe();
            });

            rong.addCallSessionListener({
                target: 'didConnect'
            }, function (ret) {
                console.log('didConnect:' + JSON.stringify(ret))

                var myname = api.getPrefs({
                    sync: true,
                    key: 'myname'
                });
                //打开本地窗口
                fnsetVideoView(api.winWidth - 200, 100, 160, 200, myname);

                setTimeout(function () {
                    rong.videoViewBringToFront({
                        userId: myname
                    })
                }, 1000)
            })


            rong.addCallSessionListener({
                target: 'didDisconnect'
            }, function (ret) {
                console.log('didDisconnect:' + JSON.stringify(ret))
            })

            rong.addCallSessionListener({
                target: 'remoteUserDidJoin'
            }, function (ret) {
                console.log("对端用户加入了通话的事件:" + JSON.stringify(ret));
                var uid = ret.userId;
                fnsetVideoView(0, 0, api.winWidth, api.winHeight, uid);

            });


            rong.addVideoViewListener(function (ret) {

                //判断点击的是否是初始小窗口
                if (ret.userId == myname &amp;&amp; meissmall) {

                    fnresetVideoView(0, 0, api.winWidth, api.winHeight, ret.userId);

                    fnresetVideoView(api.winWidth - 200, 100, 160, 200, hename);

                    meissmall = false;

                    setTimeout(function () {
                        rong.videoViewBringToFront({
                            userId: hename
                        })
                    }, 1000)

                    setTimeout(function () {
                        fnopenbtnframe()
                    }, 1200)
                }

                if (ret.userId == hename &amp;&amp; !meissmall) {

                    fnresetVideoView(0, 0, api.winWidth, api.winHeight, ret.userId);

                    fnresetVideoView(api.winWidth - 200, 100, 160, 200, myname);

                    meissmall = true;

                    setTimeout(function () {
                        rong.videoViewBringToFront({
                            userId: myname
                        })
                    }, 1000)

                    setTimeout(function () {
                        fnopenbtnframe()
                    }, 1200)

                }

            })

           
        }
    });
};

//打开视频区域
function fnsetVideoView(x, y, w, h, uid) {

    rong.setVideoView({
        rect: {
            x: x,
            y: y,
            w: w,
            h: h
        },
        userId: uid,
        bg: '#ff0000',
        renderModel: 'fit',
        fixedOn: '',
        fixed: false
    });
}

function fnresetVideoView(x, y, w, h, uid) {
    rong.resetVideoView({
        rect: {
            x: x,
            y: y,
            w: w,
            h: h
        },
        userId: uid,
        bg: '#ff0000',
        renderModel: 'fit'
    });
}


//移除视频区域
function fnremoveVideoView(ruid) {
    rong.removeVideoView({
        userId: ruid
    });
}

function fnstartCall() {

    myname = api.getPrefs({
        sync: true,
        key: 'myname'
    });

    hename = api.getPrefs({
        sync: true,
        key: 'hename'
    });

    rong.startCall({
        targetId: hename,
        mediaType: 'video',
        conversationType: 'PRIVATE',

        userIdList: [hename]
    }, function (ret) {
        console.log('startCall:' + JSON.stringify(ret))
        callId = ret.callSession.callId;
    });

    fnopenbtnframe();

}

//打开按钮页面
function fnopenbtnframe() {
    api.openFrame({
        name: 'btframe',
        url: 'button.html',
        rect: {
            marginLeft: 0,
            marginBottom: 0,
            h: 100,
            w: 'auto'
        }
    })
}

function fnaccept() {
    //同步返回结果:
    myname = api.getPrefs({
        sync: true,
        key: 'myname'
    });

    hename = api.getPrefs({
        sync: true,
        key: 'hename'
    });

    rong.accept({
        mediaType: 'video',
        callId: callId
    });
}

function fnhangup() {
    rong.hangup();
    fnremoveVideoView(hename);
    fnremoveVideoView(myname);
    api.closeFrame({
        name: 'btframe'
    })
}

function fngetCallSession() {
    rong.getCallSession(function (ret) {
        api.alert({
            msg: JSON.stringify(ret)
        });
    });
}

//请求token
function login() {
    var now = new Date();
    var number = now.getSeconds();
    //这将产生一个基于目前时间的0到59的整数。
    var timestamp = Date.parse(new Date());
    timestamp = timestamp / 1000;
    var AppKey = "pwe86ga5p****"; //填写自己的参数
    var appSecret = "Eo1hnmggH****"; //填写自己的参数
    var Nonce = number;
    var Timestamp = timestamp;
    var Signature = SHA1(appSecret + Nonce + Timestamp);
    var uid = document.getElementById('useName').value;
    var uid2 = document.getElementById('fridendName').value;
    api.setPrefs({
        key: 'myname',
        value: uid
    })

    api.setPrefs({
        key: 'hename',
        value: uid2
    })


    api.ajax({
        url: 'http://api.cn.ronghub.com/user/getToken.json',
        method: 'post',
        headers: {
            "Content-Type": "Application/x-www-form-urlencoded",
            "App-Key": AppKey,
            "Nonce": Nonce,
            "Timestamp": Timestamp,
            "Signature": Signature
        },
        data: {
            'values': {
                userId: uid,
                name: uid,
               
            }
        }
    }, function (ret, err) {
        if (ret) {
            token = ret.token;
            connect();

            var labelUsename = document.getElementById('stauseName');
            labelUsename.style.display = "block";
            labelUsename.innerHTML = uid + "已登录";
        } else {
            api.alert({
                msg: JSON.stringify(err)
            });
        }
    })
}

function logout() {
    rong.logout(function (ret, err) {
        console.log(JSON.stringify(ret));
        if (ret.status == 'error')
            api.toast({
                msg: err.code
            });
    });
}

function connect() {
    rong.connect({
        token: token
    }, function (ret, err) {
        if (ret.status == 'success') {
            console.log(ret.result.userId);
        } else {
            console.log(err.code)
        }
    });
}

function getConnectionStatus() {
    rong.getConnectionStatus(function (ret, err) {
        api.toast({
            msg: ret.result.connectionStatus
        });
    })
}




</script>




</html>

</html>