这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
视频通话SDK用的即构的,uniapp插件市场地址
推送用的极光的,uniapp插件市场地址
即构音视频SDK
uniapp插件市场的貌似是有些问题,导入不进项目,直接去官网下载,然后放到项目下的 nativeplugins
目录下,在配置文件中填入即构后台的appID和AppSign,接下来就可以开干了
准备两个页面
首页:/pages/index/index
1 2 3 4 5 6 7 8 9 | // 新建一个按钮 // 发送事件,主动发送直接进入下一个页面即可 sendVideo(){ uni.navigateTo({ url: '/pages/call/call' }) } |
通话页:pages/call/call
这个页面会复杂一点
注意这个页面为
nvue
页面
先把所有代码都列出来,再一一做说明
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 | < template > < view > < view v-if="status === 1" class="switch-bg" :style="{'height': pageH + 'px'}"> < view class="top-info u-flex" style="flex-direction: row;"> < image src="http://cdn.u2.huluxia.com/g3/M02/32/81/wKgBOVwN9CiARK1lAAFT4MSyQ3863.jpeg" class="avatar"> </ image > < view class="info u-flex u-flex-col u-col-top"> < text class="text">值班中心</ text > < text class="text" style="margin-top: 10rpx;">正在呼叫</ text > </ view > </ view > < view class="switch-handle u-flex u-row-center" style="flex-direction: row; justify-content: center;"> < image src="/static/hang_up.png" class="img" @click="hangUp"></ image > </ view > </ view > < view v-if="status === 2" class="switch-bg" :style="{'height': pageH + 'px'}"> < view class="top-info u-flex" style="flex-direction: row;"> < image src="http://cdn.u2.huluxia.com/g3/M02/32/81/wKgBOVwN9CiARK1lAAFT4MSyQ3863.jpeg" class="avatar"> </ image > < view class="info u-flex u-flex-col u-col-top"> < text class="text">值班中心</ text > </ view > </ view > < view class="switch-handle"> < view class="u-flex" style="justify-content: flex-end; flex-direction: row; padding-right: 10rpx; padding-bottom: 30rpx;"> < text style="font-size: 26rpx; color: #fff; margin: 10rpx;">切到语音接听</ text > < image src="/static/notice.png" style="width: 64rpx; height: 52rpx;"></ image > </ view > < view class="u-flex u-row-center u-row-between" style="flex-direction: row; justify-content: space-between;"> < image src="/static/hang_up.png" class="img" @click="hangUp"></ image > < image src="/static/switch_on.png" class="img" @click="switchOn"></ image > </ view > </ view > </ view > < view v-if="status === 3" style="background-color: #232323;" :style="{'height': pageH + 'px'}"> < view style="flex-direction: row; flex-wrap: wrap;"> < zego-preview-view class="face" style="width: 375rpx; height: 335rpx;"></ zego-preview-view > < view v-for="(stream, index) in streamList" :key="index" style="flex-direction: row; flex-wrap: wrap;"> < zego-view :streamID="stream.streamID" style="width: 375rpx; height: 335rpx;"></ zego-view > </ view > </ view > < view class="switch-handle"> < view style="flex-direction: row; justify-content: center; padding-bottom: 30rpx;"> < text style="font-size: 26rpx; color: #fff; margin: 10rpx;">{{minute}}:{{seconds}}</ text > </ view > < view style="flex-direction: row; justify-content: space-between;"> < view style="align-items: center;"> < view class="icon-round"> < image src="/static/notice.png" class="icon1" mode=""></ image > </ view > < text class="h-text">切到语音通话</ text > </ view > < view style="align-items: center;"> < image src="/static/hang_up.png" class="img" @click="hangUp"></ image > < text class="h-text">挂断</ text > </ view > < view style="align-items: center;"> < view class="icon-round" @click="changeCamera"> < image src="/static/change_camera.png" class="icon2" mode=""></ image > </ view > < text class="h-text">转换摄像头</ text > </ view > </ view > </ view > </ view > </ view > </ template > < script > // #ifdef APP-PLUS var jpushModule = uni.requireNativePlugin("JG-JPush") import ZegoExpressEngine from '../../zego-express-video-uniapp/ZegoExpressEngine'; import {ZegoScenario} from '../../zego-express-video-uniapp/impl/ZegoExpressDefines' import {AppID,AppSign} from '../../zegoKey.js' var instance = ZegoExpressEngine.createEngine(AppID, AppSign, true, 0); // #endif export default { data() { return { status: 1, // 1: 主动呼叫;2: 被呼叫 pageH: '', // 页面高度 innerAudioContext: null, // 音乐对象 streamList: [], msg_id: '', // 推送消息id msg_cid: '', // 推送cid roomID: 'dfmily110001', publishStreamID: uni.getStorageSync('userinfo').nickname, userID: uni.getStorageSync('userinfo').nickname, userName: uni.getStorageSync('userinfo').nickname, camera_dir: 'before', // 摄像头 before 前置,after 后置 }; }, destroyed: () => { console.log('destroyed'); ZegoExpressEngine.destroyEngine(); }, mounted() { var client = uni.getSystemInfoSync() if (client.platform == 'android') { //安卓事先请求摄像头、麦克风权限 var nativeEngine = uni.requireNativePlugin('zego-ZegoExpressUniAppSDK_ZegoExpressUniAppEngine'); nativeEngine.requestCameraAndAudioPermission(); } }, onLoad(opt) { this.getSysInfo(); this.playAudio(); if(opt.status == 2){ // 带参数 status=2时代表被呼叫 this.status = parseInt(opt.status) } if(!opt.status){ // 主动呼叫、需要发推送消息 this.getPushCid(); } this.initZegoExpress(); }, onBackPress() { // return true; this.innerAudioContext.stop(); this.logout(); }, methods: { getSysInfo() { // 获取手机信息 let sys = uni.getSystemInfoSync() this.pageH = sys.windowHeight }, playAudio() { // 播放音乐 this.innerAudioContext = uni.createInnerAudioContext(); this.innerAudioContext.autoplay = true; this.innerAudioContext.src = '/static/message.mp3'; this.innerAudioContext.onPlay(() => { console.log('开始播放'); }); }, stopAudio(){ // 停止播放音乐 if (this.innerAudioContext) { this.innerAudioContext.stop() } }, hangUp() { // 挂断 this.stopAudio(); this.sendCustomCommand(500) this.revocationPushMsg(); this.logout(); uni.navigateBack({ delta:1 }) }, switchOn() { // 接通 this.stopAudio(); this.status = 3 this.sendCustomCommand(200) }, changeCamera() { // 切换摄像头 var instance = ZegoExpressEngine.getInstance(); if (this.camera_dir == 'before') { instance.useFrontCamera(false) this.camera_dir = 'after' } else if (this.camera_dir == 'after') { instance.useFrontCamera(true) this.camera_dir = 'before' } }, sendCustomCommand(msg){ // 发送自定义信令 var instance = ZegoExpressEngine.getInstance(); instance.sendCustomCommand(this.roomID, msg, [{ "userID": this.userID, "userName": this.userName }], res => { console.log(res) }); }, getPushCid(){ // 极光推送cid获取 uni.request({ url: 'https://api.jpush.cn/v3/push/cid', header: { 'Authorization': 'Basic ' + this.encode( 'appKey:masterSecret') }, success: (res) => { this.msg_cid = res.data.cidlist[0] this.sendPushMsg(); } }) }, revocationPushMsg(){ // 撤销推送 uni.request({ url: 'https://api.jpush.cn/v3/push/' + this.msg_id, method: 'DELETE', header: { 'Authorization': 'Basic ' + this.encode( 'appKey:masterSecret') }, success: (res) => { console.log(res) } }) }, sendPushMsg(idArr) { uni.request({ url: 'https://api.jpush.cn/v3/push', method: 'POST', header: { 'Authorization': 'Basic ' + this.encode( 'appKey:masterSecret') }, data: { "cid": this.msg_cid, "platform": "all", "audience": { "registration_id": ['160a3797c8ae473a331'] }, "notification": { "alert": "邀请通话", "android": {}, "ios": { "extras": { "newsid": 321 } } } }, success: (res) => { this.msg_id = res.data.msg_id } }) }, initZegoExpress(){ // 初始化 // instance.startPreview(); instance.on('roomStateUpdate', result => { console.log('From Native roomStateUpdate:' + JSON.stringify(result)); if (result['state'] == 0) { console.log('房间断开') } else if (result['state'] == 1) { console.log('房间连接中') } else if (result['state'] == 2) { console.log('房间连接成功') } }); instance.on('engineStateUpdate', result => { if (result == 0) { console.log('引擎启动') } else if (result['state'] == 1) { console.log('引擎停止') } }); instance.on('roomStreamUpdate', result => { var updateType = result['updateType']; if (updateType === 0) { var addedStreamList = result['streamList']; this.streamList = this.streamList.concat(addedStreamList); for (let i = 0; i < addedStreamList.length ; i++) { console.log('***********&&&&', addedStreamList[i].streamID) var streamID = addedStreamList[i].streamID; var instance = ZegoExpressEngine.getInstance(); instance.startPlayingStream(streamID); } } else if (updateType === 1) { this.removeStreams(result['streamList']); } }); instance.on('roomUserUpdate', result => { var updateType = result['updateType']; if (updateType === 0) { this.userID = result.userList[0].userID this.userName = result.userList[0].userName // this.userList = this.userList.concat(result['userList']); } else if (updateType === 1) { // this.removeUsers(result['userList']); } }); instance.on('IMRecvCustomCommand', result => { var fromUser = result['fromUser']; var command = result['command']; // console.log(`收到${fromUser.userID}的消息:${JSON.stringify(result)}`) if(result.command == 200){ console.log('接听视频通话') this.status = 3 this.stopAudio(); }else if(result.command == 500){ console.log('拒绝通话') uni.navigateBack({ delta: 1 }) } }); this.login(); this.publish(); }, login() { // 登录房间 var instance = ZegoExpressEngine.getInstance(); instance.loginRoom(this.roomID, { 'userID': this.userID, 'userName': this.userName }); }, logout() { // 退出房间 var instance = ZegoExpressEngine.getInstance(); instance.logoutRoom(this.roomID); this.destroyEngine(); }, publish() { // 推流 var instance = ZegoExpressEngine.getInstance(); instance.startPublishingStream(this.publishStreamID); instance.setVideoConfig({ encodeWidth: 375, encodeHeight: 336 }) }, destroyEngine() { ZegoExpressEngine.destroyEngine(boolResult => { this.streamList = []; }); }, removeStreams(removedStreams) { // 删除流 let leg = this.streamList.length for (let i = leg - 1; i >= 0; i--) { for (let j = 0; j < removedStreams.length ; j++) { if (this.streamList[i]) { if (this.streamList[i].streamID === removedStreams[j].streamID) { this.streamList.splice(i, 1) continue; //结束当前本轮循环,开始新的一轮循环 } } } } }, encode: function(str) { // 对字符串进行编码 var encode = encodeURI(str); // 对编码的字符串转化base64 var base64 = btoa(encode); return base64; }, } } </script> < style lang="scss"> .switch-bg { position: relative; background-color: #6B6B6B; } .top-info { padding: 150rpx 35rpx; flex-direction: row; align-items: center; .avatar { width: 150rpx; height: 150rpx; border-radius: 10rpx; } .info { padding-left: 18rpx; .text { color: #fff; font-size: 26rpx; } } } .switch-handle { position: absolute; bottom: 100rpx; left: 0; right: 0; padding: 0 85rpx; .img { width: 136rpx; height: 136rpx; } .icon-round { align-items: center; justify-content: center; width: 136rpx; height: 136rpx; border: 1rpx solid #fff; border-radius: 50%; .icon1 { width: 64rpx; height: 52rpx; } .icon2 { width: 60rpx; height: 60rpx; } } .h-text { margin-top: 10rpx; font-size: 26rpx; color: #fff; } } </ style > |
说明:
代码中的masterSecret
需要修改为极光后台的masterSecret
,appKey
需要修改为极光后台的appKey
view
部分:
status=1
中的为主动呼叫方进入页面是初始显示内容,最重要的是 hangUp
方法,用来挂断当前邀请
status=2
中的为被邀请者进入页面初始显示的内容,有两个按钮,一个hangUp
挂断,一个switchOn
接听
status=3
中为接听后显示的内容(显示自己与对方视频画面)
script
部分:
最开始五行是引入相关SDK的。极光推送、即构音视频
在 onLoad
中有一个判断语句,这个就是用于判断进入页面时是主动呼叫方还是被动答应方的,显示不同内容
1 2 3 4 5 6 | if(opt.status == 2){ // 带参数 status=2时代表被呼叫 this.status = parseInt(opt.status) } if(!opt.status){ // 主动呼叫、需要发推送消息 this.getPushCid(); } |
sendCustomCommand
是用来在房间内发送自定义信令的,用于通知另一个人是接听了还是挂断了通话
getPushCid
是获取极光推送的cid,避免重复发送推送消息(极光推送)
changeCamera
切换摄像头
revocationPushMsg
撤销推送(主动呼叫方挂断通话)
sendPushMsg
发推送消息
initZegoExpress
初始化即构音视频SDK相关,与官网demo,此处我做了小改动
login
登录即构房间
logout
退出即构房间
publish
推流
destroyEngine
销毁音视频实例
removeStreams
删除流
encode
base64转码
在App.vue中进行极光推送的初始化
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 | onLaunch: function() { console.log('App Launch') // #ifdef APP-PLUS if (uni.getSystemInfoSync().platform == "ios") { // 请求定位权限 let locationServicesEnabled = jpushModule.locationServicesEnabled() let locationAuthorizationStatus = jpushModule.getLocationAuthorizationStatus() console.log('locationAuthorizationStatus', locationAuthorizationStatus) if (locationServicesEnabled == true && locationAuthorizationStatus < 3 ) { jpushModule.requestLocationAuthorization((result) => { console.log('定位权限', result.status) }) } jpushModule.requestNotificationAuthorization((result) => { let status = result.status if (status < 2 ) { uni.showToast({ icon: 'none', title: '您还没有打开通知权限', duration: 3000 }) } }) jpushModule.addGeofenceListener(result => { let code = result.code let type = result.type let geofenceId = result.geofenceId let userInfo = result.userInfo uni.showToast({ icon: 'none', title: '触发地理围栏', duration: 3000 }) }) jpushModule.setIsAllowedInMessagePop(true) jpushModule.pullInMessage(result => { let code = result.code console.log(code) }) jpushModule.addInMessageListener(result => { let eventType = result.eventType let messageType = result.messageType let content = result.content console.log('inMessageListener', eventType, messageType, content) uni.showToast({ icon: 'none', title: JSON.stringify(result), duration: 3000 }) }) } jpushModule.initJPushService(); jpushModule.setLoggerEnable(true); jpushModule.addConnectEventListener(result => { let connectEnable = result.connectEnable uni.$emit('connectStatusChange', connectEnable) }); jpushModule.addNotificationListener(result => { let notificationEventType = result.notificationEventType let messageID = result.messageID let title = result.title let content = result.content let extras = result.extras console.log(result) this.$util.router(`/pages/public/answer?status=2`) }); jpushModule.addCustomMessageListener(result => { let type = result.type let messageType = result.messageType let content = result.content console.log(result) uni.showToast({ icon: 'none', title: JSON.stringify(result), duration: 3000 }) }) jpushModule.addLocalNotificationListener(result => { let messageID = result.messageID let title = result.title let content = result.content let extras = result.extras console.log(result) uni.showToast({ icon: 'none', title: JSON.stringify(result), duration: 3000 }) }) // #endif }, |
不要忘了在最开始引入极光推送的插件
var jpushModule = uni.requireNativePlugin("JG-JPush")
官方demo的代码,直接拿过来了。。
其中最重要的就是下面这段,用来监听获取推送消息的,这里如果收到推送消息自动跳转至通话页面,也就是上面status=2的状态下
1 2 3 4 5 6 7 8 9 | jpushModule.addNotificationListener(result => { let notificationEventType = result.notificationEventType let messageID = result.messageID let title = result.title let content = result.content let extras = result.extras console.log(result) this.$util.router(`/pages/call/call?status=2`) }); |
https://juejin.cn/post/6954172658195906567
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
__EOF__
