记录--uniapp开发安卓APP视频通话模块初实践
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
视频通话SDK用的即构的,uniapp插件市场地址
推送用的极光的,uniapp插件市场地址
即构音视频SDK
uniapp插件市场的貌似是有些问题,导入不进项目,直接去官网下载,然后放到项目下的 nativeplugins
目录下,在配置文件中填入即构后台的appID和AppSign,接下来就可以开干了
准备两个页面
首页:/pages/index/index
// 新建一个按钮
<button @click="sendVideo">发送视频邀请</button> // 发送事件,主动发送直接进入下一个页面即可
sendVideo(){
uni.navigateTo({
url: '/pages/call/call'
})
}
通话页:pages/call/call
这个页面会复杂一点
注意这个页面为
nvue
页面
先把所有代码都列出来,再一一做说明
<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>
<text class="text" style="margin-top: 10rpx;">邀请您视频聊天</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
中有一个判断语句,这个就是用于判断进入页面时是主动呼叫方还是被动答应方的,显示不同内容
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中进行极光推送的初始化
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的状态下
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
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
记录--uniapp开发安卓APP视频通话模块初实践的更多相关文章
- 【软件配置】JDK+AndroidStudio4.1开发安卓APP环境安装和配置教程详细
目录 一.专业名词 二.搭建前资源准备 2.1 JDK资源下载 2.2 AndroidStudio下载 三.安装 3.1 JDK安装配置 3.2 AndroidStudio安装 四.创建安卓APP工程 ...
- 使用VS2017开发安卓app(1)环境搭建
本人新手,边学习边写笔记,有错误不足之处,望各位博友指正~ 想要用vs开发安卓app,需要在安装时勾选 Xamarin是一个跨平台开发框架.在这一框架内,开发iOS.Android.Windows P ...
- [编译] 7、在Linux下搭建安卓APP的开发烧写环境(makefile版-gradle版)—— 在Linux上用命令行+VIM开发安卓APP
April 18, 2020 6:54 AM - BEAUTIFULZZZZ 目录 0 前言 1 gradle 安装配置 1.1 卸载系统默认装的gradle 1.2 下载对应版本的二进制文件 1.3 ...
- [编译] 5、在Linux下搭建安卓APP的开发烧写环境(makefile版)—— 在Linux上用命令行+VIM开发安卓APP
星期三, 19. 九月 2018 02:19上午 - BEAUTIFULZZZZ 0)前言 本文不讨论用IDE和文本编辑器开发的优劣,是基于以下两点考虑去尝试用命令行编译安卓APP的: 了解安卓APP ...
- QT开发安卓APP的中文字体问题
1.安卓默认的字体为DroidSansFallback:谷歌中文字体 由于手机和PC字体的不一致,导致PC上开发的APP到目的安卓设备中文显示为小方框. 故需要要在qt的main函数中设置系统字体: ...
- 使用VS2017开发安卓app(2)新建项目
安装完成后,在c#下找到Android,选择Android应用(Xamarin),修改项目名称和路径,新建第一个安卓项目! 点击确定后会出现 这里我们选择空白应用和Android 7.1. 创建新项目 ...
- uniapp开发的app打开微信小程序
第一种 <script> export default { data() { return { sweixin: null } }, onLoad() { this.getPlus() } ...
- 当前主流的安卓APP开发IDE
什么是主流的开发安卓APP的方式? 我是去年4月份接触的Android开发,因此特别有感触,可以明显的感受到安卓APP主流开发方式的改变. 去年,2015年年初,各大安卓开发群大部分大牛在用Eclip ...
- 安卓app开发-03-项目的基本开发步骤
android项目的基本开发步骤 这里分享一下开发 安卓 app 的流程,当然有些感觉不必要,其实不然,前期工作也是极为重要的额,就像开发的时候如果目标不对的话,到后期后很迷的,所以一定要提前做好规划 ...
- [编译] 6、开源两个简单且有用的安卓APP命令行开发工具和nRF51822命令行开发工具
星期四, 27. 九月 2018 12:00上午 - BEAUTIFULZZZZ 一.前言 前几天给大家介绍了如何手动搭建安卓APP命令行开发环境和nRF51822命令行开发环境,中秋这几天我把上面篇 ...
随机推荐
- C++ 单例模式以及内存管理
引用: https://zhuanlan.zhihu.com/p/37469260 https://www.cnblogs.com/xiaolincoding/p/11437231.html http ...
- seq2seq模型案例分析
1 seq2seq模型简介 seq2seq 模型是一种基于[ Encoder-Decoder](编码器-解码器)框架的神经网络模型,广泛应用于自然语言翻译.人机对话等领域.目前,[seq2seq+at ...
- Swoole从入门到入土(18)——WebSocket服务器[心跳ping]
由于 WebSocket 是长连接,如果一定时间内没有通讯,连接可能会断开.这时候需要心跳机制,WebSocket 协议包含了 Ping 和 Pong 两个帧,可以定时发送 Ping 帧来保持长连接. ...
- 解决:Not found the kernel library or the kernel library is invalid
问题说明: 今天运行一个E语言写的程序报错, 看样子是缺少核心依赖库. 解决方法 去下载个易语言安装包安装一下即可.比如我安装的是: 易语言5.6完美破解版(精简版).exe 下载地址:https:/ ...
- 使用marquee标签实现文字滚动
代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...
- docker 常用命令 快捷命令
一.查询节点 docker ps -a 二.docker重启停止 systemctl restart docker systemctl stop docker docker restart * 三.一 ...
- windbg 学习
常用的 windbg 命令 .ecxr 用来切换到异常发生时的上下文,主要用在分析静态 dump 文件的时候.当我们使用 .reload 命令去强制加载库的 pdb 文件后,需要执行 .ecxr 命令 ...
- QT - Day 1
Date: 2021/3/12开始学习 教程视频: QT基本介绍: 跨平台图形界面引擎 优点 跨平台 接口简单,容易上手 一定程度上简化了内存回收 创建第一个QT程序 点击创建项目后,选择项目路径 ...
- 学习go语言编程之标准库
标准库包分类 Golang标准库可以大致按其中库的功能进行以下分类: 分类 对应包 描述 输入输出 bufio,fmt,io,log,flag 这个分类包括二进制以及文本格式在屏幕.键盘.文件以及其他 ...
- Kotlin 协程三 —— 数据流 Flow
目录 一.Flow 的基本使用 1.1 Sequence 与 Flow 1.2 Flow 的简单使用 1.3 创建常规 Flow 的常用方式: 1.4 Flow 是冷流(惰性的) 1.5 Flow 的 ...