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

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

有了上述思路,在模块文档中查找相应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平台使用融云模块实现音视频通话实践经验总结分享的更多相关文章

  1. APICloud平台的融云2.0集成

    融云2.0的官方文档地址:http://docs.apicloud.com/端API/开放SDK/rongCloud2 项目须要IM模块,最后还是选择了融云.在iOS原生开发中,融云sdk集成了聊天界 ...

  2. apicloud+融云实现即时通讯

    请尊重作者的辛勤劳动!!! 使用apicloud开发已经快2个月了,起初的目的就是为了实现安卓和苹果的兼容,属于一个试验项目,究竟apicloud是否能够满足公司的要求?最 终看来还是不错的,使用ap ...

  3. 融云携新版实时音视频亮相 LiveVideoStack 2019

    4 月 19 日,LiveVideoStack 2019 音视频大会在上海隆重开幕,全球多媒体创新专家.音视频技术工程师.产品负责人.高端行业用户等共襄盛会,聚焦音频.视频.图像.AI 等技术的最新探 ...

  4. APICloud框架——融云+UIChatTools实现即时通讯聊天

    今天完成了公司app的聊天界面的收发消息功能,结合融云2和UIChatTools模块实现,只是实现了基本功能,好多细节还没有实现,废话不多说,上代码 输入框页面(win) 先引入所需模块 // 融云模 ...

  5. 融云参加RTC实时互联网大会 现场集成IM SDK

    9月21至22日,由全球实时云服务商声网Agora.io主办的RTC2017实时互联网大会在北京万豪酒店成功举办.作为亚洲最权威的RTC实时通信行业技术盛会,会议吸引了来自全球上千名开发者参加,Goo ...

  6. 融云消息接口apicloud

    融云提供消息发送服务,支持个人消息,群消息,讨论组,聊天室消息, 以下是它涉及到的接口. 初始化,连接之后,可以使用. <!DOCTYPE html> <html> <h ...

  7. 如何在APICloud平台使用腾讯X5引擎

    目前APICloud与腾讯X5引擎已经达成全方位的深度合作,APICloud在多个产品线深度集成X5引擎,广大APICloud开发者们即日起可通过以下几方面在你的APP中使用X5引擎,享受X5引擎带来 ...

  8. 融云(找到“每个App都有沟通的需求”的细分市场)

    近日,国内著名App驾考宝典和融云达成合作,为应用增加IM功能,实现亿级用户之间聊天.消息一出,IM(即时通讯)领域的大佬,同时也是个上线不到两岁的新生力量,再次引发了行业的关注. 对业内人士而言,即 ...

  9. 融云通信云发力教育行业 助在线教育"风口"继续腾云

    4 月 16 日,2019 年AI 在线教育大会在北京站圆满落幕,会上云集超过500位资深教育从业者.200 家机构,共同就 AI 教育落地.在线教育应用经验等主题开展深入探讨.云通信领域的领导企业融 ...

随机推荐

  1. 网络安全(一)主动进攻之DNS基础和ettercap实现DNS流量劫持

    alittlemc,个人原创,个人理解和观点.若有错误.不理解请与我联系,谢谢! 介绍了DNS的解析过程. DNS劫持的思路和实践. DNS 域名 以为live.bilibili.com为例子,从后到 ...

  2. MyBatis(入参的类型和日志记录)

    入参的类型是对象 1. 新增的参数是对象 2. 空值的处理,占位符 字段,jdbcType=VARCHAR          字符串 字段,jdbcType=DATE                  ...

  3. 【题解】CF1720C

    题意简述 给你一个 01 矩阵,每一次你可以在这个矩阵中找到一个 \(L\) 型,将它全部变成 0.\(L\) 型的定义是在一个 \(2\times2\) 矩阵中,除开一个角之外的图形,其中必须包含至 ...

  4. HTML基础知识(3)浮动、塌陷问题

    1.浮动 1.1 代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> & ...

  5. 动词时态=>4.将来时态和过去将来时态构成详解

    将来时态构成详解 使用助动词will构成的将来时态 一般将来时态 与一般过去时态相反(时间上) 如果说 一般过去,我们将其当做一张照片 从这张照片当中,我们无法得知 动作什么时候开始 什么时候结束 只 ...

  6. C# String.IsNullOrEmpty()方法的使用

    IsNullOrEmpty(string)是String类的一个有参的方法,方法需要类的调用,所以String.IsNullOrEmpty(string) IsNullOrEmpty是判断字符串的Nu ...

  7. 题解 UVA10285 最长的滑雪路径 Longest Run on a Snowboard

    Solution 双倍经验 就是记搜嘛. 搞一个二维数组记录一下当前的最长滑雪路径,其他和普通 dfs 没什么两样. 向 \(4\) 个方向搜索,如果高度符合就 \(+1\) . 多测要注意数组初始化 ...

  8. Mysql InnoDB Redo log

    一丶什么是redo innodb是以也为单位来管理存储空间的,增删改查的本质都是在访问页面,在innodb真正访问页面之前,需要将其加载到内存中的buffer pool中之后才可以访问,但是在聊事务的 ...

  9. 【题解】CF1503B 3-Coloring

    题面传送门 解决思路 讲一下 \(\text{VP}\) 时的思路. 首先想到,只要能将棋盘中红色或蓝色部分全部填成同一个数,那么剩下的就不会受限了(可行有两个,限制只有一个): 但考虑到交互库可能有 ...

  10. IIS 配置集中式证书模块实现网站自动绑定证书文件

    在 Windows 环境下如果采用 IIS 作为 网站服务器时,常规的网站绑定 HTTPS 需要一个一个站点手动选择对应的证书绑定,而且证书过期之后更换证书时也是需要一个个重新绑定操作,无法便捷的做到 ...