这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

视频通话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需要修改为极光后台的masterSecretappKey需要修改为极光后台的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视频通话模块初实践的更多相关文章

  1. 【软件配置】JDK+AndroidStudio4.1开发安卓APP环境安装和配置教程详细

    目录 一.专业名词 二.搭建前资源准备 2.1 JDK资源下载 2.2 AndroidStudio下载 三.安装 3.1 JDK安装配置 3.2 AndroidStudio安装 四.创建安卓APP工程 ...

  2. 使用VS2017开发安卓app(1)环境搭建

    本人新手,边学习边写笔记,有错误不足之处,望各位博友指正~ 想要用vs开发安卓app,需要在安装时勾选 Xamarin是一个跨平台开发框架.在这一框架内,开发iOS.Android.Windows P ...

  3. [编译] 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 ...

  4. [编译] 5、在Linux下搭建安卓APP的开发烧写环境(makefile版)—— 在Linux上用命令行+VIM开发安卓APP

    星期三, 19. 九月 2018 02:19上午 - BEAUTIFULZZZZ 0)前言 本文不讨论用IDE和文本编辑器开发的优劣,是基于以下两点考虑去尝试用命令行编译安卓APP的: 了解安卓APP ...

  5. QT开发安卓APP的中文字体问题

    1.安卓默认的字体为DroidSansFallback:谷歌中文字体 由于手机和PC字体的不一致,导致PC上开发的APP到目的安卓设备中文显示为小方框. 故需要要在qt的main函数中设置系统字体: ...

  6. 使用VS2017开发安卓app(2)新建项目

    安装完成后,在c#下找到Android,选择Android应用(Xamarin),修改项目名称和路径,新建第一个安卓项目! 点击确定后会出现 这里我们选择空白应用和Android 7.1. 创建新项目 ...

  7. uniapp开发的app打开微信小程序

    第一种 <script> export default { data() { return { sweixin: null } }, onLoad() { this.getPlus() } ...

  8. 当前主流的安卓APP开发IDE

    什么是主流的开发安卓APP的方式? 我是去年4月份接触的Android开发,因此特别有感触,可以明显的感受到安卓APP主流开发方式的改变. 去年,2015年年初,各大安卓开发群大部分大牛在用Eclip ...

  9. 安卓app开发-03-项目的基本开发步骤

    android项目的基本开发步骤 这里分享一下开发 安卓 app 的流程,当然有些感觉不必要,其实不然,前期工作也是极为重要的额,就像开发的时候如果目标不对的话,到后期后很迷的,所以一定要提前做好规划 ...

  10. [编译] 6、开源两个简单且有用的安卓APP命令行开发工具和nRF51822命令行开发工具

    星期四, 27. 九月 2018 12:00上午 - BEAUTIFULZZZZ 一.前言 前几天给大家介绍了如何手动搭建安卓APP命令行开发环境和nRF51822命令行开发环境,中秋这几天我把上面篇 ...

随机推荐

  1. springboot 实现拦截的 3 种方式介绍及异步执行的思考

    springboot 拦截方式 实际项目中,我们经常需要输出请求参数,响应结果,方法耗时,统一的权限校验等. 本文首先为大家介绍 HTTP 请求中三种常见的拦截实现,并且比较一下其中的差异. (1)基 ...

  2. 如何在Windows上使用Git创建一个可执行脚本?

    长话短说,今天介绍如何在windows上使用Git上创建一个可执行的shell脚本. 首先我们要知道windows上Git新添加的文件权限是:-rw-r--r--(对应权限值是644),而通常创建的s ...

  3. C++ sentry 如何压缩日志文件

    项目中在使用 sentry 上传事件的 attachment 函数过程中发现,附带的 log 文件是未压缩的,于是有了需求,即需要在 sentry 内部将未压缩的文件流压缩后再上传给服务器 这个需求看 ...

  4. django学习第十五天-modelform的补充

    基于form组件和modelform组件改造图书管理系统 详情可以去图书管理系统分类中查看 基于form组件和modelform组件改造图书管理系统 modelform的补充 class BookMo ...

  5. 【Azure 云服务】指标哪去了?在执行 Swap (交换生产部署和Staging部署) 操作后看不见云服务的旧指标

    问题描述 打开云服务(Cloud Service)的Metrics页面,发现过去了指标不见了? 以虚点构成无数据的图表. 问题解答 查看云服务的活动日志(Activity Logs),发现最近执行的操 ...

  6. 【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn

    问题描述 App Service 配置 Application Settings 访问Storage Account.如下: { "name": "WEBSITE_CON ...

  7. 专访实在智能孙林君:颠覆传统RPA的实在IPA模式如何做到真正人人可用?

      王吉伟对话实在智能孙林君:颠覆传统引领RPA行业的实在IPA模式是如何炼成的? 王吉伟对话实在智能孙林君:为什么第一款颠覆行业的RPA诞生在实在智能? 专访实在智能孙林君:打造出真正人人可用的实在 ...

  8. spring重点后置处理器

    1. DefaultListableBeanFactory的作用: 默认实现了ListableBeanFactory和BeanDefinitionRegistry接口,基于bean definitio ...

  9. 【规范】看看人家Git提交描述,那叫一个规矩

    前言 缘由 没想到玩了多年git,竟然还有提交描述规范 事情起因: 在工作迭代过程中,偶然发现同组小帅哥Git提交描述总是和自己的不大一样,秉承好奇至上的我特意去研究了下.竟然发现提交了这么多年的Gi ...

  10. 在本地搭建 SVN 教程

    SVN 使用教程 以下内容参考自:SVN使用详细教程_大梦谁先觉i的博客-CSDN博客_svn使用教程 一.SVN 安装 1.1 软件下载 服务器:Downloads | VisualSVN 客户端: ...