写点什么

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

作者:APICloud
  • 2022 年 3 月 11 日
  • 本文字数:8064 字

    阅读完需:约 26 分钟

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


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


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


以下是融云模块文档链接:https://docs.apicloud.com/Client-API/Open-SDK/rongCloud2


简要介绍用到的主要 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)
}
})
} }); };
复制代码


实现效果如下:

其他经验总结:


返回错误码 34001,重启 loader 可解决,可能换账号登录,wifi 同步重启 loader 有缓存用户信息导致。


接听不到来电事件,可尝试用 4g 网络测试。有些公司防火墙,或者电脑共享的 wifi 热点网络有限制或不稳定。


以上经验都是无数次排错总结出来的,看了至少能帮你节省两个工作日。


最后贴下完整代码:


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


用户头像

APICloud

关注

一次编码多端运行,移动应用低代码开发平台 2020.12.22 加入

APICloud多端技术遵循标准 Web Components组件化思想,兼容Vue 、React语法特性,一次编码同时发布为Android 、iOS 、小程序、Html5(SPA)多端应用。

评论

发布
暂无评论
APICloud平台使用融云模块实现音视频通话实践经验总结分享_前端开发_APICloud_InfoQ写作平台