• 技术分享| 小程序实现音视频通话


    上一期我们把前期准备工作做完了,这一期就带大家实现音视频通话!

    sdk 二次封装

    为了更好的区分功能,我分成了六个 js 文件

    • config.js 音视频与呼叫邀请配置

    • store.js 实现音视频通话的变量

    • rtc.js 音视频逻辑封装

    • live-code.js 微信推拉流状态码

    • rtm.js 呼叫邀请相关逻辑封装

    • util.js 其他方法

    config.js

    配置 sdk 所需的 AppId,如需私有云可在此配置

    • RTC 音视频相关

    • RTM 实时消息(呼叫邀请)

      module.exports = {
      AppId: "",
      // RTC 私有云配置
        RTC_setParameters: {
          setParameters: {
          //   //配置私有云网关
          //   ConfPriCloudAddr: {
          //     ServerAdd: "",
          //     Port: ,
          //     Wss: true,
          //   },
          },
        },
      // RTM 私有云配置
      RTM_setParameters: {
          setParameters: {
              // //配置内网网关
              // confPriCloudAddr: {
              //     ServerAdd: "",
              //     Port: ,
              //     Wss: true,
              // },
          },
      },
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25

    store.js

    整个通话系统使用的变量设置

    module.exports = {
    // 网络状态
    networkType: "",
    // rtm连接状态
    rtmNetWorkType: "",
    // rtc连接状态
    rtcNetWorkType: "",
    // 视频通话0 语音通话1
    Mode: 0,
    // 当前场景 0:首页 1:呼叫页面 2:通信页面
    State: 0,
    
    // 本地用户uid
    userId: "",
    // 远端用户uid
    peerUserId: "",
    // 频道房间
    channelId: "",
    
    // RTM 客户端
    rtmClient: null,
    // RTC 客户端
    rtcClient: null,
    
    // 本地录制地址(小程序特有推流)
    livePusherUrl: "",
    // 远端播放(小程序特有拉流)
    livePlayerUrl: "",
    
    // 主叫邀请实例
    localInvitation: null,
    // 被叫收到的邀请实例
    remoteInvitation: null,
    
    // 是否正在通话
    Calling: false,
    // 是否是单人通话
    Conference: false,
    
    // 通话计时
    callTime: 0,
    callTimer: null,
    
    // 30s 后无网络取消通话
    networkEndCall: null,
    networkEndCallTime: 30*1000,
    
    // 断网发送查询后检测是否返回消息
    networkSendInfoDetection: null,
    networkSendInfoDetectionTime: 10*1000,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    rtc.js

    音视频 sdk 二测封装,方便调用

    // 引入 RTC
    const ArRTC = require("ar-rtc-miniapp");
    // 引入 until
    const Until = require("./util");
    // 引入 store
    let Store = require("./store");
    // 引入 SDK 配置
    const Config = require("./config");
    
    // 初始化 RTC
    const InItRTC = async () => {
        // 创建RTC客户端 
        Store.rtcClient = new ArRTC.client();
        // 初始化
        await Store.rtcClient.init(Config.AppId);
    
        Config.RTC_setParameters.setParameters && await Store.rtcClient.setParameters(Config.RTC_setParameters.setParameters)
        // 已添加远端音视频流
        Store.rtcClient.on('stream-added', rtcEvent.userPublished);
        // 已删除远端音视频流
        Store.rtcClient.on('stream-removed', rtcEvent.userUnpublished);
        // 通知应用程序发生错误
        Store.rtcClient.on('error', rtcEvent.error);
        // 更新 Url 地址
        Store.rtcClient.on('update-url', rtcEvent.updateUrl);
        // 远端视频已旋转
        Store.rtcClient.on('video-rotation', rtcEvent.videoRotation);
        // 远端用户已停止发送音频流
        Store.rtcClient.on('mute-audio', rtcEvent.muteAudio);
        // 远端用户已停止发送视频流
        Store.rtcClient.on('mute-video', rtcEvent.muteVideo);
        // 远端用户已恢复发送音频流
        Store.rtcClient.on('unmute-audio', rtcEvent.unmuteAudio);
        // 远端用户已恢复发送视频流
        Store.rtcClient.on('unmute-video', rtcEvent.unmuteAudio);
    }
    
    // RTC 监听事件处理
    const rtcEvent = {
        // RTC SDK 监听用户发布
        userPublished: ({
            uid
        }) => {
            console.log("RTC SDK 监听用户发布", uid);
            Store.networkSendInfoDetection && clearTimeout(Store.networkSendInfoDetection);
            if (Store.Mode == 0) {
                wx.showLoading({
                    title: '远端加载中',
                    mask: true,
                })
            }
    
            // 订阅远端用户发布音视频
            Store.rtcClient.subscribe(uid, (url) => {
                console.log("远端用户发布音视频", url);
                // 向视频页面发送远端拉流地址
                Until.emit("livePusherUrlEvent", {
                    livePlayerUrl: url
                });
            }, (err) => {
                console.log("订阅远端用户发布音视频失败", err);
            })
        },
        // RTC SDK 监听用户取消发布
        userUnpublished: ({
            uid
        }) => {
            console.log("RTC SDK 监听用户取消发布", uid);
            Store.networkSendInfoDetection && clearTimeout(Store.networkSendInfoDetection);
            Store.networkSendInfoDetection = setTimeout(() => {
                wx.showToast({
                    title: '对方网络异常',
                    icon: "error"
                });
                setTimeout(() => {
                    rtcInternal.leaveChannel(false);
                }, 2000)
    
            }, Store.networkSendInfoDetectionTime);
    
    
        },
        // 更新 Url 地址
        updateUrl: ({
            uid,
            url
        }) => {
            console.log("包含远端用户的 ID 和更新后的拉流地址", uid, url);
            // 向视频页面发送远端拉流地址
            Until.emit("livePusherUrlEvent", {
                livePlayerUrl: url
            });
        },
        // 视频的旋转信息以及远端用户的 ID
        videoRotation: ({
            uid,
            rotation
        }) => {
            console.log("视频的旋转信息以及远端用户的 ID", uid, rotation);
        },
        // 远端用户已停止发送音频流
        muteAudio: ({
            uid
        }) => {
            console.log("远端用户已停止发送音频流", uid);
        },
        // 远端用户已停止发送视频流
        muteVideo: ({
            uid
        }) => {
            console.log("远端用户已停止发送视频流", uid);
        },
        // 远端用户已恢复发送音频流
        unmuteAudio: ({
            uid
        }) => {
            console.log("远端用户已恢复发送音频流", uid);
        },
        // 远端用户已恢复发送视频流
        unmuteAudio: ({
            uid
        }) => {
            console.log("远端用户已恢复发送视频流", uid);
        },
        // 通知应用程序发生错误。 该回调中会包含详细的错误码和错误信息
        error: ({
            code,
            reason
        }) => {
            console.log("错误码:" + code, "错误信息:" + reason);
        },
    }
    
    // RTC 内部逻辑
    const rtcInternal = {
        // 加入频道
        joinChannel: () => {
            Store.rtcClient.join(undefined, Store.channelId, Store.userId, () => {
                console.log("加入频道成功", Store.rtcClient);
                // 发布视频
                rtcInternal.publishTrack();
                // 加入房间一定时间内无人加入
                Store.networkSendInfoDetection && clearTimeout(Store.networkSendInfoDetection);
                Store.networkSendInfoDetection = setTimeout(() => {
                    wx.showToast({
                        title: '对方网络异常',
                        icon: "error"
                    });
                    setTimeout(() => {
                        rtcInternal.leaveChannel(false);
                    }, 2000)
    
                }, Store.networkSendInfoDetectionTime);
    
    
            }, (err) => {
                console.log("加入频道失败");
            });
        },
        // 离开频道
        leaveChannel: (sendfase = true) => {
            console.log("离开频道", sendfase);
            console.log("RTC 离开频道", Store);
            Store.networkSendInfoDetection && clearTimeout(Store.networkSendInfoDetection);
            if (Store.rtcClient) {
                // 引入 RTM
                const RTM = require("./rtm");
                Store.rtcClient.destroy(() => {
                    console.log("离开频道", RTM);
                    if (sendfase) {
                        // 发送离开信息
                        RTM.rtmInternal.sendMessage(Store.peerUserId, {
                            Cmd: "EndCall",
                        })
                    }
                    Until.clearStore();
                    // 返回首页
                    wx.reLaunch({
                        url: '../index/index',
                        success:function () {
                            wx.showToast({
                              title: '通话结束',
                              icon:'none'
                            })
                        }
                    });
                }, (err) => {
                    console.log("离开频道失败", err);
                })
            } else {
                Until.clearStore();
            }
        },
        // 发布本地音视频
        publishTrack: () => {
            Store.rtcClient.publish((url) => {
                console.log("发布本地音视频", url);
                // 本地录制地址(小程序特有推流)
                Store.livePusherUrl = url;
                // 向视频页面发送本地推流地址
                Until.emit("livePusherUrlEvent", {
                    livePusherUrl: url
                });
            }, ({
                code,
                reason
            }) => {
                console.log("发布本地音视频失败", code, reason);
            })
        },
    
        // 切换静音
        switchAudio: (enableAudio = false) => {
            /**
             * muteLocal 停止发送本地用户的音视频流
             * unmuteLocal 恢复发送本地用户的音视频流
             */
            Store.rtcClient[enableAudio ? 'muteLocal' : 'unmuteLocal']('audio', () => {
                wx.showToast({
                    title: enableAudio ? '关闭声音' : '开启声音',
                    icon: 'none',
                    duration: 2000
                })
            }, ({
                code,
                reason
            }) => {
                console.log("发布本地音视频失败", code, reason);
            })
        },
    }
    
    module.exports = {
        InItRTC,
        rtcInternal,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236

    live-code.js

    微信推拉流状态码

    module.exports = {
        1001: "已经连接推流服务器",
        1002: "已经与服务器握手完毕,开始推流",
        1003: "打开摄像头成功",
        1004: "录屏启动成功",
        1005: "推流动态调整分辨率",
        1006: "推流动态调整码率",
        1007: "首帧画面采集完成",
        1008: "编码器启动",
        "-1301": "打开摄像头失败",
        "-1302": "打开麦克风失败",
        "-1303": "视频编码失败",
        "-1304": "音频编码失败",
        "-1305": "不支持的视频分辨率",
        "-1306": "不支持的音频采样率",
        "-1307": "网络断连,且经多次重连抢救无效,更多重试请自行重启推流",
        "-1308": "开始录屏失败,可能是被用户拒绝",
        "-1309": "录屏失败,不支持的Android系统版本,需要5.0以上的系统",
        "-1310": "录屏被其他应用打断了",
        "-1311": "Android Mic打开成功,但是录不到音频数据",
        "-1312": "录屏动态切横竖屏失败",
        1101: "网络状况不佳:上行带宽太小,上传数据受阻",
        1102: "网络断连, 已启动自动重连",
        1103: "硬编码启动失败,采用软编码",
        1104: "视频编码失败",
        1105: "新美颜软编码启动失败,采用老的软编码",
        1106: "新美颜软编码启动失败,采用老的软编码",
        3001: "RTMP -DNS解析失败",
        3002: "RTMP服务器连接失败",
        3003: "RTMP服务器握手失败",
        3004: "RTMP服务器主动断开,请检查推流地址的合法性或防盗链有效期",
        3005: "RTMP 读/写失败",
        2001: "已经连接服务器",
        2002: "已经连接 RTMP 服务器,开始拉流",
        2003: "网络接收到首个视频数据包(IDR)",
        2004: "视频播放开始",
        2005: "视频播放进度",
        2006: "视频播放结束",
        2007: "视频播放Loading",
        2008: "解码器启动",
        2009: "视频分辨率改变",
        "-2301": "网络断连,且经多次重连抢救无效,更多重试请自行重启播放",
        "-2302": "获取加速拉流地址失败",
        2101: "当前视频帧解码失败",
        2102: "当前音频帧解码失败",
        2103: "网络断连, 已启动自动重连",
        2104: "网络来包不稳:可能是下行带宽不足,或由于主播端出流不均匀",
        2105: "当前视频播放出现卡顿",
        2106: "硬解启动失败,采用软解",
        2107: "当前视频帧不连续,可能丢帧",
        2108: "当前流硬解第一个I帧失败,SDK自动切软解",
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    rtm.js

    实时消息(呼叫邀请)二次封装。使用 p2p 消息发送接受(信令收发),呼叫邀请

    // 引入 anyRTM 
    const ArRTM = require("ar-rtm-sdk");
    // 引入 until
    const Until = require("./util");
    // 引入 store
    let Store = require("./store");
    // 引入 SDK 配置
    const Config = require("../utils/config");
    // 引入 RTC
    const RTC = require("./rtc");
    
    // 本地 uid 随机生成
    Store.userId = Until.generateNumber(4) + '';
    
    
    // 监听网络状态变化事件
    wx.onNetworkStatusChange(function (res) {
        // 网络状态
        Store.networkType = res.networkType
        // 无网络
        if (res.networkType == 'none') {
            wx.showLoading({
                title: '网络掉线了',
                mask: true
            });
            Store.rtmNetWorkType = "";
            // 30s 无网络连接结束当前呼叫
            Store.networkEndCall && clearTimeout(Store.networkEndCall);
            Store.networkEndCall = setTimeout(() => {
                rtmInternal.networkEndCall();
            }, Store.networkEndCallTime);
    
        } else {
            Store.networkEndCall && clearTimeout(Store.networkEndCall);
            wx.hideLoading();
            if (!Store.rtmClient) {
                // 初始化
                InItRtm();
            } else {
                if (!Store.rtcClient) {
                    // 呼叫阶段
                    let oRtmSetInterval = setInterval(() => {
                        // rtm 链接状态
                        if (Store.rtmNetWorkType == "CONNECTED") {
                            clearInterval(oRtmSetInterval);
                            Store.networkSendInfoDetection && clearTimeout(Store.networkSendInfoDetection);
                            // 发送信息,查看对方状态
                            rtmInternal.sendMessage(Store.peerUserId, {
                                Cmd: "CallState",
                            });
                            // 发送无响应
                            Store.networkSendInfoDetection = setTimeout(() => {
                                rtmInternal.networkEndCall();
                            }, Store.networkEndCallTime);
                        }
                    }, 500)
                }
    
            }
        }
    });
    
    // 初始化
    const InItRtm = async () => {
        // 创建 RTM 客户端
        Store.rtmClient = await ArRTM.createInstance(Config.AppId);
        Config.RTM_setParameters.setParameters && await Store.rtmClient.setParameters(Config.RTM_setParameters.setParameters)
        // RTM 版本
        console.log("RTM 版本", ArRTM.VERSION);
        wx.showLoading({
            title: '登录中',
            mask: true
        })
        // 登录 RTM
        await Store.rtmClient.login({
            token: "",
            uid: Store.userId
        }).then(() => {
            wx.hideLoading();
            wx.showToast({
                title: '登录成功',
                icon: 'success',
                duration: 2000
            })
            console.log("登录成功");
        }).catch((err) => {
            Store.userId = "";
            wx.hideLoading();
            wx.showToast({
                icon: 'error',
                title: 'RTM 登录失败',
                mask: true,
                duration: 2000
            });
            console.log("RTM 登录失败", err);
        });
    
        // 监听收到来自主叫的呼叫邀请
        Store.rtmClient.on(
            "RemoteInvitationReceived",
            rtmEvent.RemoteInvitationReceived
        );
        // 监听收到来自对端的点对点消息
        Store.rtmClient.on("MessageFromPeer", rtmEvent.MessageFromPeer);
        // 通知 SDK 与 RTM 系统的连接状态发生了改变
        Store.rtmClient.on(
            "ConnectionStateChanged",
            rtmEvent.ConnectionStateChanged
        );
    
    }
    
    // RTM 监听事件
    const rtmEvent = {
        // 主叫:被叫已收到呼叫邀请
        localInvitationReceivedByPeer: () => {
            console.log("主叫:被叫已收到呼叫邀请");
            // 跳转至呼叫页面
            wx.reLaunch({
                url: '../pageinvite/pageinvite?call=0'
            });
    
            wx.showToast({
                title: '被叫已收到呼叫邀请',
                icon: 'none',
                duration: 2000,
                mask: true,
            });
    
        },
        // 主叫:被叫已接受呼叫邀请
        localInvitationAccepted: async (response) => {
            console.log("主叫:被叫已接受呼叫邀请", response);
            try {
                const oInfo = JSON.parse(response);
                // 更改通话方式
                Store.Mode = oInfo.Mode;
                wx.showToast({
                    title: '呼叫邀请成功',
                    icon: 'success',
                    duration: 2000
                });
                // anyRTC 初始化
                await RTC.InItRTC();
                // 加入 RTC 频道
                await RTC.rtcInternal.joinChannel();
    
                // 进入通话页面
                wx.reLaunch({
                    url: '../pagecall/pagecall',
                });
            } catch (error) {
                console.log("主叫:被叫已接受呼叫邀请 数据解析失败", response);
            }
    
        },
        // 主叫:被叫拒绝了你的呼叫邀请
        localInvitationRefused: (response) => {
            try {
                const oInfo = JSON.parse(response);
                // 不同意邀请后返回首页
                rtmInternal.crosslightgoBack(oInfo.Cmd == "Calling" ? "用户正在通话中" : "用户拒绝邀请");
            } catch (error) {
                rtmInternal.crosslightgoBack("用户拒绝邀请")
            }
        },
        // 主叫:呼叫邀请进程失败
        localInvitationFailure: (response) => {
            console.log("主叫:呼叫邀请进程失败", response);
            // rtmInternal.crosslightgoBack("呼叫邀请进程失败");
        },
        // 主叫:呼叫邀请已被成功取消 (主动挂断)
        localInvitationCanceled: () => {
            console.log("主叫:呼叫邀请已被成功取消 (主动挂断)");
            // 不同意邀请后返回首页
            rtmInternal.crosslightgoBack("已取消呼叫");
        },
    
        // 被叫:监听收到来自主叫的呼叫邀请
        RemoteInvitationReceived: async (remoteInvitation) => {
            if (Store.Calling) {
                // 正在通话中处理
                rtmInternal.callIng(remoteInvitation);
            } else {
                wx.showLoading({
                    title: '收到呼叫邀请',
                    mask: true,
                })
    
                // 解析主叫呼叫信息
                const invitationContent = await JSON.parse(remoteInvitation.content);
                if (invitationContent.Conference) {
                    setTimeout(() => {
                        wx.hideLoading();
                        wx.showToast({
                            title: '暂不支持多人通话(如需添加,请自行添加相关逻辑)',
                            icon: 'none',
                            duration: 3000,
                            mask: true,
                        })
                        // 暂不支持多人通话(如需添加,请自行添加相关逻辑)
                        remoteInvitation.refuse();
                    }, 1500);
                } else {
                    wx.hideLoading();
                    Store = await Object.assign(Store, {
                        // 通话方式
                        Mode: invitationContent.Mode,
                        // 频道房间
                        channelId: invitationContent.ChanId,
                        // 存放被叫实例
                        remoteInvitation,
                        // 远端用户
                        peerUserId: remoteInvitation.callerId,
                        // 标识为正在通话中
                        Calling: true,
                        // 是否是单人通话
                        Conference: invitationContent.Conference,
                    })
    
                    // 跳转至呼叫页面
                    wx.reLaunch({
                        url: '../pageinvite/pageinvite?call=1'
                    });
    
                    // 收到呼叫邀请处理
                    rtmInternal.inviteProcessing(remoteInvitation);
                }
            }
        },
        // 被叫:监听接受呼叫邀请
        RemoteInvitationAccepted: async () => {
            console.log("被叫 接受呼叫邀请", Store);
            wx.showLoading({
                title: '接受邀请',
                mask: true,
            })
            // anyRTC 初始化
            await RTC.InItRTC();
            // 加入 RTC 频道
            await RTC.rtcInternal.joinChannel();
            wx.hideLoading()
            // 进入通话页面
            wx.reLaunch({
                url: '../pagecall/pagecall',
            });
        },
        // 被叫:监听拒绝呼叫邀请
        RemoteInvitationRefused: () => {
            console.log("被叫 拒绝呼叫邀请");
            // 不同意邀请后返回首页
            rtmInternal.crosslightgoBack("成功拒绝邀请");
        },
        // 被叫:监听主叫取消呼叫邀请
        RemoteInvitationCanceled: () => {
            console.log("被叫 取消呼叫邀请");
            // 不同意邀请后返回首页
            rtmInternal.crosslightgoBack("主叫取消呼叫邀请");
        },
        // 被叫:监听呼叫邀请进程失败
        RemoteInvitationFailure: () => {
            console.log("被叫 呼叫邀请进程失败");
            // 不同意邀请后返回首页
            rtmInternal.crosslightgoBack("呼叫邀请进程失败");
        },
    
    
        // 收到来自对端的点对点消息
        MessageFromPeer: (message, peerId) => {
            console.log("收到来自对端的点对点消息", message, peerId);
            message.text = JSON.parse(message.text);
            switch (message.text.Cmd) {
                case "SwitchAudio":
                    // 视频通话页面转语音
                    Until.emit("callModeChange", {
                        mode: 1
                    });
                    break;
                case "EndCall":
                    // 挂断
                    RTC.rtcInternal.leaveChannel(false);
                    break;
                case "CallState":
                    // 对方查询本地状态,返回给对方信息
                    rtmInternal.sendMessage(peerId, {
                        Cmd: "CallStateResult",
                        state: Store.peerUserId !== peerId ? 0 : Store.State,
                        Mode: Store.Mode,
                    })
                    break;
                case "CallStateResult":
                    // 远端用户返回信息处理
                    console.log("本地断网重连后对方状态", message, peerId);
                    Store.networkSendInfoDetection && clearTimeout(Store.networkSendInfoDetection);
                    if (message.text.state == 0 && Store.State != 0) {
                        // 远端停止通话,本地还在通话
                        rtmInternal.networkEndCall();
                    } else if (message.text.state == 2) {
                        Store.Mode = message.text.Mode;
                        // 远端 rtc 通话
                        if (Store.State == 1) {
                            // 本地 rtm 呼叫中进入RTC
                            console.log("本地 rtm 呼叫中进入RTC",Store);
                        } else if (Store.State == 2) {
                            // 本地 rtc 通话
                            if (message.text.Mode == 1) {
                                // 转语音通话
                                Until.emit("callModeChange", {
                                    mode: 1
                                });
                            }
                        }
                    }
                    break;
                default:
                    console.log("收到来自对端的点对点消息", message, peerId);
                    break;
            }
        },
        // 通知 SDK 与 RTM 系统的连接状态发生了改变
        ConnectionStateChanged: (newState, reason) => {
            console.log("系统的连接状态发生了改变", newState);
            Store.rtmNetWorkType = newState;
            switch (newState) {
                case "CONNECTED":
                    wx.hideLoading();
                    //  SDK 已登录 RTM 系统
                    wx.showToast({
                        title: 'RTM 连接成功',
                        icon: 'success',
                        mask: true,
                    })
                    break;
                case "ABORTED":
                    wx.showToast({
                        title: 'RTM 停止登录',
                        icon: 'error',
                        mask: true,
                    });
                    console.log("RTM 停止登录,重新登录");
    
                    break;
                default:
                    wx.showLoading({
                        title: 'RTM 连接中',
                        mask: true,
                    })
                    break;
            }
        }
    }
    
    // RTM 内部逻辑
    const rtmInternal = {
        // 查询呼叫用户是否在线
        peerUserQuery: async (uid) => {
            const oUserStatus = await Store.rtmClient.queryPeersOnlineStatus([uid]);
            if (!oUserStatus[uid]) {
                wx.showToast({
                    title: '用户不在线',
                    icon: 'error',
                    duration: 2000,
                    mask: true,
                });
                return false;
            }
            return true;
        },
    
        // 主叫发起呼叫
        inviteSend: async (callMode) => {
            Store = await Object.assign(Store, {
                // 随机生成频道
                channelId: '' + Until.generateNumber(9),
                // 正在通话中
                Calling: true,
                // 通话方式
                Mode: callMode,
                // 创建呼叫邀请
                localInvitation: Store.rtmClient.createLocalInvitation(
                    Store.peerUserId
                )
            })
    
            // 设置邀请内容
            Store.localInvitation.content = JSON.stringify({
                Mode: callMode, // 呼叫类型 视频通话 0 语音通话 1
                Conference: false, // 是否是多人会议
                ChanId: Store.channelId, // 频道房间
                UserData: "",
                SipData: "",
                VidCodec: ["H264"],
                AudCodec: ["Opus"],
            });
    
    
            // 事件监听
            // 监听被叫已收到呼叫邀请
            Store.localInvitation.on(
                "LocalInvitationReceivedByPeer",
                rtmEvent.localInvitationReceivedByPeer
            );
            // 监听被叫已接受呼叫邀请
            Store.localInvitation.on(
                "LocalInvitationAccepted",
                rtmEvent.localInvitationAccepted
            );
            // 监听被叫拒绝了你的呼叫邀请
            Store.localInvitation.on(
                "LocalInvitationRefused",
                rtmEvent.localInvitationRefused
            );
            // 监听呼叫邀请进程失败
            Store.localInvitation.on(
                "LocalInvitationFailure",
                rtmEvent.localInvitationFailure
            );
            // 监听呼叫邀请已被成功取消
            Store.localInvitation.on(
                "LocalInvitationCanceled",
                rtmEvent.localInvitationCanceled
            );
    
            // 发送邀请
            Store.localInvitation.send();
        },
        // 被叫收到呼叫邀请处理(给收到的邀请实例绑定事件)
        inviteProcessing: async (remoteInvitation) => {
            // 监听接受呼叫邀请
            remoteInvitation.on(
                "RemoteInvitationAccepted",
                rtmEvent.RemoteInvitationAccepted
            );
            // 监听拒绝呼叫邀请
            remoteInvitation.on(
                "RemoteInvitationRefused",
                rtmEvent.RemoteInvitationRefused
            );
            // 监听主叫取消呼叫邀请
            remoteInvitation.on(
                "RemoteInvitationCanceled",
                rtmEvent.RemoteInvitationCanceled
            );
            // 监听呼叫邀请进程失败
            remoteInvitation.on(
                "RemoteInvitationFailure",
                rtmEvent.RemoteInvitationFailure
            );
        },
    
        // 正在通话中处理
        callIng: async (remoteInvitation) => {
            remoteInvitation.response = await JSON.stringify({
                // Reason: "Calling",
                refuseId: Store.ownUserId,
                Reason: "calling",
                Cmd: "Calling",
            });
            await remoteInvitation.refuse();
        },
    
        // 不同意邀请后返回首页
        crosslightgoBack: (message) => {
            // Store 重置
            Until.clearStore();
            // 返回首页
            wx.reLaunch({
                url: '../index/index',
            });
            wx.showToast({
                title: message,
                icon: 'none',
                duration: 2000,
                mask: true,
            });
        },
    
        // 发送消息
        sendMessage: (uid, message) => {
            console.log("发送消息", uid, message);
            Store.rtmClient && Store.rtmClient.sendMessageToPeer({
                text: JSON.stringify(message)
            }, uid).catch(err => {
                console.log("发送消息失败", err);
            });
        },
        // 无网络连接结束当前呼叫
        networkEndCall: () => {
            if (Store.rtcClient) {
                // RTC 挂断
            } else {
                // 呼叫阶段
                let oRtmSetInterval = setInterval(() => {
                    // rtm 链接状态
                    if (Store.rtmNetWorkType == "CONNECTED") {
                        clearInterval(oRtmSetInterval);
                        // RTM 取消/拒绝呼叫
                        if (Store.localInvitation) {
                            // 主叫取消呼叫
                            Store.localInvitation.cancel();
                        } else if (Store.remoteInvitation) {
                            // 被叫拒绝呼叫
                            Store.remoteInvitation.refuse();
                        }
                    }
                }, 500);
            }
        }
    }
    
    module.exports = {
        InItRtm,
        rtmInternal,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514

    util.js

    项目中使用的方法封装:

    • 时间转化

    • 生成随机数

    • 音视频通话变量置空

    • 计时器

    • 深克隆

    • 事件监听封装,类似uniapp的 on,emit,remove(off)

     const formatTime = date => {
      const year = date.getFullYear()
      const month = date.getMonth() + 1
      const day = date.getDate()
      const hour = date.getHours()
      const minute = date.getMinutes()
      const second = date.getSeconds()
    
      return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
    }
    
    const formatNumber = n => {
      n = n.toString()
      return n[1] ? n : `0${n}`
    }
    
    // 随机生成uid
    const generateNumber = (len) => {
      const numLen = len || 8;
      const generateNum = Math.ceil(Math.random() * Math.pow(10, numLen));
      return generateNum < Math.pow(10, numLen - 1) ?
        generateNumber(numLen) :
        generateNum;
    }
    
    // 引入 store
    let Store = require("./store");
    // 本地清除
    const clearStore = () => {
      // 通话计时器
      Store.callTimer && clearInterval(Store.callTimer);
      Store = Object.assign(Store, {
        // 视频通话0 语音通话1
        Mode: 0,
        // 远端用户uid
        peerUserId: "",
        // 频道房间
        channelId: "",
    
        // 是否正在通话
        Calling: false,
        // 是否是单人通话
        Conference: false,
    
        // 通话计时
        callTime: 0,
        callTimer: null,
      })
    }
    // 计时器
    const calculagraph = () => {
      Store.callTime++;
      let oMin = Math.floor(Store.callTime / 60);
      let oSec = Math.floor(Store.callTime % 60);
      oMin >= 10 ? oMin : (oMin = "0" + oMin);
      oSec >= 10 ? oSec : (oSec = "0" + oSec);
      return oMin + ":" + oSec;
    }
    
    
    
    // 深克隆
    function deepClone(obj) {
      if (typeof obj !== 'object') {
        return obj;
      } else {
        const newObj = obj.constructor === Array ? [] : {};
    
        for (const key in obj) {
          if (obj.hasOwnProperty(key)) {
            if (obj[key] && typeof obj[key] === 'object') {
              newObj[key] = deepClone(obj[key]);
            } else {
              newObj[key] = obj[key];
            }
          }
        }
        return newObj;
      }
    }
    
    
    /**
     * 事件传递
     */
    // 用来保存所有绑定的事件
    const events = {};
    
    // 监听事件
    function on(name, self, callback) {
      // self用来保存小程序page的this,方便调用this.setData()修改数据
      const tuple = [self, callback];
      const callbacks = events[name];
      let isCallback = null;
      // 判断事件库里面是否有对应的事件
      if (Array.isArray(callbacks)) {
        // 相同的事件不要重复绑定
        const selfCallbacks = callbacks.filter(item => {
          return self === item[0];
        });
        if (selfCallbacks.length === 0) {
          callbacks.push(tuple);
        } else {
          for (const item of selfCallbacks) {
            if (callback.toString() !== item.toString()) {
              isCallback = true;
            }
          }!isCallback && selfCallbacks[0].push(callback);
        }
      } else {
        // 事件库没有对应数据,就将事件存进去
        events[name] = [tuple];
      }
    }
    
    // 移除监听的事件
    function remove(name, self) {
      const callbacks = events[name];
      if (Array.isArray(callbacks)) {
        events[name] = callbacks.filter(tuple => {
          return tuple[0] !== self;
        });
      }
    }
    
    // 触发监听事件
    function emit(name, data = {}) {
      const callbacks = events[name];
      if (Array.isArray(callbacks)) {
        callbacks.map(tuple => {
          const self = tuple[0];
          for (const callback of tuple) {
            if (typeof callback === 'function') {
              // 用call绑定函数调用的this,将数据传递过去
              callback.call(self, deepClone(data));
            }
          }
        });
      }
    }
    
    module.exports = {
      formatTime,
      generateNumber,
      clearStore,
      on,
      remove,
      emit,
      calculagraph
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150

    呼叫邀请页面 pageinvite

    pageinvite.wxml

    
        
        
            
            
                
                {{uid}}
            
            
            
                
                {{CallFlse ? '收到邀请' : '呼叫中'}} 
            
            
            
                
                
                    
                        
                    
                    
                    
                        
                        转语音
                    
    
                
                
                
                    
                    
                        
                        {{CallFlse ?'挂断':'取消'}}
                    
    
                    
                    
                        
                        接听
                    
                
            
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    pageinvite.js(响铃音乐自行添加)

    响铃音乐自行添加

    // const RTM = require("../../utils/rtm")
    const Store = require("../../utils/store");
    const Until = require("../../utils/util");
    // pages/p2ppage/p2ppage.js
    
    // 响铃
    // const innerAudioContext = wx.createInnerAudioContext();
    // let innerAudioContext = null;
    Page({
    
        /**
         * 页面的初始数据
         */
        data: {
            // 呼叫者
            uid: "",
            // 通话方式
            mode: 0,
            // 主叫/被叫
            CallFlse: false,
            // 响铃
            innerAudioContext: null,
        },
    
        /**
         * 生命周期函数--监听页面加载
         */
        onLoad: function (options) {
             // 响铃音乐
            // const innerAudioContext = wx.createInnerAudioContext();
            // innerAudioContext.src = "/pages/audio/video_request.mp3";
            // innerAudioContext.autoplay = true;
            // innerAudioContext.loop = true;
            // innerAudioContext.play();
            Store.State = 1;
            this.setData({
                uid: Store.peerUserId,
                mode: Store.Mode,
                CallFlse: options.call == 0 ? false : true,
                innerAudioContext
            });
        },
        /**
         * 生命周期函数--监听页面显示
         */
        onShow: function () {
            wx.hideHomeButton();
        },
        onUnload: function () {
            console.log("销毁");
            // 停止响铃
            // this.data.innerAudioContext.destroy();
        },
        // 取消呼叫
        async cancelCall() {
            if (this.data.CallFlse) {
                // 被叫拒绝
                Store.remoteInvitation && await Store.remoteInvitation.refuse();
            } else {
                // 主叫取消
                Store.localInvitation && await Store.localInvitation.cancel();
            }
        },
        // 接受邀请
        async acceptCall() {
            if (Store.remoteInvitation) {
                console.log("接受邀请",Store.remoteInvitation);
                // 设置响应模式
                Store.remoteInvitation.response = await JSON.stringify({
                    Mode: this.data.mode,
                    Conference: false,
                    UserData: "",
                    SipData: "",
                });
                // 本地模式
                Store.Mode = this.data.mode;
                // 接受邀请
                await Store.remoteInvitation.accept();
            }
        },
        // 语音接听
        async voiceCall() {
            if (Store.remoteInvitation) {
                // 设置响应模式
                Store.remoteInvitation.response = await JSON.stringify({
                    Mode: 1,
                    Conference: false,
                    UserData: "",
                    SipData: "",
                });
                // 本地模式
                Store.Mode = 1;
                // 接受邀请
                await Store.remoteInvitation.accept();
            }
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    语音通话页面 pagecall

    pagecall.wxml

    
    
    
        
        
            
                
                
            
        
    
        
        
            
                
                {{calltime}}
                
                
                    
                        
                        切换至语音
                    
                    
                        
                        挂断
                    
                    
                        
                        
                            {{devicePosition == 'front' ? '前' : '后'}}摄像头
                        
                    
                
            
            
        
    
    
    
    
    
    
        
        
        
        
    
        
        
            
            {{peerid}}
        
        
        
            {{calltime}}
        
        
        
            
                
                静音
            
            
                
                挂断
            
            
                
                免提
            
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    pagecall.js

    const Until = require("../../utils/util");
    const Store = require("../../utils/store");
    const RTC = require("../../utils/rtc");
    const RTM = require("../../utils/rtm");
    const liveCode = require("../../utils/live-code");
    Page({
    
        /**
         * 页面的初始数据
         */
        data: {
            // 可用宽度
            windowWidth: "",
            // 通话方式
            mode: 0,
            // 远端uid
            peerid: "",
            // 本地录制地址(小程序特有推流)
            livePusherUrl: "",
            // 远端播放(小程序特有拉流)
            livePlayerUrl: "",
    
            // 前置或后置,值为front, back
            devicePosition: 'front',
            // 开启或关闭麦克风
            enableMic: false,
            // 开启免提
            soundMode: 'speaker',
    
            calltime: "00:00"
    
        },
        // 微信组件状态
        statechange(e) {
            if (e.detail.code == 2004) {
                wx.hideLoading();
            }
            if (e.detail.code != 1006 && e.detail.message) {
                wx.showToast({
                    title: liveCode[e.detail.code] || e.detail.message,
                    icon: 'none',
                })
            }
    
            console.log('live-pusher code:', e.detail)
        },
        // 微信组件错误
        error(e) {
            console.log(e.detail);
            switch (e.detail.errCode) {
                case 10001:
                    wx.showToast({
                        title: '用户禁止使用摄像头',
                        icon: 'none',
                        duration: 2000
                    })
                    break;
                case 10002:
                    wx.showToast({
                        title: '用户禁止使用录音',
                        icon: 'none',
                        duration: 2000
                    })
                    break;
                default:
                    break;
            }
        },
        /**
         * 生命周期函数--监听页面加载
         */
        onLoad: function (options) {
            const _this = this;
            Store.State = 2;
            // 推拉流变更
            Until.on("livePusherUrlEvent", this, (data) => {
                _this.setData({
                    livePusherUrl: data.livePusherUrl ? data.livePusherUrl : _this.data.livePusherUrl,
                    livePlayerUrl: data.livePlayerUrl ? data.livePlayerUrl : _this.data.livePlayerUrl,
                })
            });
            // 通话模式变更
            Until.on("callModeChange", this, (data) => {
                _this.setData({
                    mode: data.mode,
                });
                Store.Mode = data.mode;
            })
            // 可用宽度
            try {
                const oInfo = wx.getSystemInfoSync();
                this.setData({
                    windowWidth: oInfo.windowWidth,
                    mode: Store.Mode,
                    // mode: 1,
                    peerid: Store.peerUserId || '6666',
                })
                // 开启通话计时
                Store.callTimer = setInterval(() => {
                    _this.setData({
                        calltime: Until.calculagraph()
                    })
                }, 1000)
            } catch (error) {
                console.log("error", error);
            }
    
        },
    
        /**
         * 生命周期函数--监听页面卸载
         */
        onUnload: function () {
            Until.remove("livePusherUrlEvent", this);
            Until.remove("callModeChange",this);
        },
        // 切换至语音
        switchAudio() {
            this.setData({
                peerid: Store.peerUserId,
                mode: 1,
            });
            Store.Mode = 1;
            // 发送切换语音消息
            RTM.rtmInternal.sendMessage(Store.peerUserId, {
                Cmd: "SwitchAudio",
            })
        },
        // 挂断
        endCall() {
            RTC.rtcInternal.leaveChannel(true);
        },
        // 翻转摄像头
        switchCamera() {
            wx.createLivePusherContext().switchCamera();
            this.setData({
                devicePosition: this.data.devicePosition == 'front' ? 'back' : 'front'
            })
        },
        // 静音
        muteAudio() {
            this.setData({
                enableMic: this.data.enableMic ? false : true,
            });
            RTC.rtcInternal.switchAudio(this.data.enableMic);
        },
        // 免提
        handsFree() {
            this.setData({
                soundMode: this.data.soundMode == 'speaker' ? 'ear' : 'speaker',
            });
        },
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153

    体验地址

    微信搜索anyRTC视频云点击AR 呼叫即可体验小程序版 ARCall

    代码地址

    文件Call_watch

    在这里插入图片描述

  • 相关阅读:
    [Spark、hadoop]Spark Streaming整合kafka实战
    【Unity】旋转的尽头是使用四元数让物体旋转
    it运维监控主要做什么
    springboot系列(二十四):如何实现Excel文件导出?这你得会 | 超级详细,建议收藏
    js基础笔记学习53-练习2计算水仙花数2
    数字IC笔试千题解--单选题篇(二)
    web大作业:基于html+css+javascript+jquery实现智能分控网站
    跨域问题以及经过网关二次转发重复跨域
    【软件开发面经】大厂面试经验
    工单系统选购指南:解锁高效管理之门的秘密
  • 原文地址:https://blog.csdn.net/anyRTC/article/details/126157049