APICloud平臺使用融雲模組實現音視訊通話實踐經驗總結分享

海的盡頭發表於2022-03-08

需求概要:實現視訊撥打、接聽、結束通話、視訊介面大小視窗、點選小視窗實現大小視窗互換。

實現思路:一方撥打後,另一方要能收到相應事件,然後接聽。接通後,渲染對方視訊畫面。那麼己方視訊畫面什麼時候渲染呢?對於呼叫方,可以在呼叫後開始渲染,也可以接通事件事件發生後再開始渲染。對於接通方可以在點選接聽按鈕後開始渲染,也可以在接通事件發生後開始渲染。

有了上述思路,在模組文件中查詢相應api,編寫程式碼,就可以驗證我們的思路是否可以實現。如果遇到問題,再調整實現思路。

以下是融雲模組文件連結:https://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)

                    }

                })

               
            }
        });
    };

實現效果如下:

其他經驗總結:

返回錯誤碼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>

相關文章