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

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

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

 

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

 

有了上述思路,在模块文档中查找相应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
        })

image.gif

融云初始化成功之后,可添加相应事件的监听。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)
                    }
                })
            }
        });
    };

image.gif

实现效果如下:

08195207_eyBQ.png

 

其他经验总结:

返回错误码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>


目录
相关文章
|
27天前
|
Web App开发 编解码 视频直播
视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术
本文详细介绍了Android端直播技术的全貌,涵盖了从实时音视频采集、编码、传输到解码与播放的各个环节。文章还探讨了直播中音视频同步、编解码器选择、传输协议以及直播延迟优化等关键问题。希望本文能为你提供有关Andriod端直播技术的深入理解和实践指导。
38 0
|
2月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
146 0
|
11月前
|
编解码 算法 大数据
即时通讯技术文集(第25期):实时音视频基础入门 [共20篇]
​为了更好地分类阅读 52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第25 期。
54 1
|
编解码 Java 开发工具
[技术分享]Android平台实时音视频录像模块设计之道
录像有什么难的?无非就是数据过来,编码保存mp4而已,这可能是好多开发者在做录像模块的时候的思考输出。是的,确实不难,但是做好,或者和其他模块有非常好的逻辑配合,确实不容易。
104 0
|
API 开发工具 Android开发
语音聊天源码平台开发小知识
现如今的社交平台中,语音聊天室仍然占据着一席之地,例如语音电台,主播可以在直播间中与给听众讲故事、唱歌,观众也可以申请上麦,与主播聊天互动。主要实现的功能就是语音连麦,之前讲过很多直播源码平台的开发和功能,本篇我们来讲下语音聊天源码平台的开发逻辑是怎么样的。
|
移动开发 JavaScript Java
体育直播源码,基本功能和系统组成
在这篇文章中,我们将会介绍东莞梦幻网络科技的体育直播源码系统的基础功能和系统组成。该源码系统是一款基于体育直播、比分竞猜、赛事数据查看、赛事社区、微短视频等基础模块集成一体的应用程序。
体育直播源码,基本功能和系统组成
|
移动开发 开发框架 前端开发
即时通讯技术文集(第13期):Web端即时通讯技术精华合集 [共15篇]
为了更好地分类阅读52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第13 期。
131 0
|
移动开发 小程序 JavaScript
APICloud平台常用技术点汇总详解
使用 APICloud 可以开发移动 APP、小程序、html5 网页应用。如果要实现编写一套代码编译为多端应用(移动 APP、小程序、html5 ),需使用 avm.js 框架进行开发。如果只开发 APP,则可以使用前端技术(HTML5、Vue、react 等)、avm.js 进行开发,还可以使用模块商店大量的原生模块以及多端组件。
277 0
|
开发框架 开发工具
使用融云SDK在APICloud 平台实现单人多人音频通话
使用融云SDK在APICloud 平台实现单人多人音频通话,使用之前必须先获取 token、init、connect,同时需要到融云后台开通音视频通话功能(开通或者关闭 30 分钟后生效)。单人通话逻辑比较简单,主要会用到 didReceiveCall、didConnect、didDisconnect 等三个事件。
227 0
|
开发工具
使用融云SDK在APICloud平台实现单人多人音频通话
使用之前必须先获取token、init、connect,同时需要到融云后台开通音视频通话功能(开通或者关闭30分钟后生效)。单人通话逻辑比较简单,主要会用到didReceiveCall、didConnect、didDisconnect等三个事件。多人通话逻辑复杂一点,并且只能应用在群组或者讨论组,会用到didReceiveCall、didConnect、remoteUserDidJoin、remoteUserDidLeft、remoteUserDidInvite、didDisconnect等六个事件。
176 0
使用融云SDK在APICloud平台实现单人多人音频通话