当前位置: 首页 > 工具软件 > ArCall > 使用案例 >

uni-app技术分享|开源demo视频呼叫arcall uni-app端技术实现

公冶高义
2023-12-01

介绍

基本

uniapp_arcall 是通过 uniapp 实现的语音通话、视频通话以及相关配套的呼叫邀请。
解决向指定用户发起呼叫通知,对方接受后进行通话的场景需求。

呼叫邀请基本流程

发起呼叫
收到被叫接听
收到被叫拒绝
通知主叫接听
通知主叫拒绝
主叫
被叫收到呼叫
主叫执行 RTC
相关逻辑
被叫接听
被叫拒绝
被叫执行 RTC
60s 无操作邀请自动失败

通话基本流程

采集音视频
本地采集音视频
通过回调获取远端音视频
初始化 RTC 实例
加入房间
发布并渲染音视频
渲染远端音视频

arcall 具体实现

demo 源码 地址

呼叫邀请相关逻辑

引入呼叫邀请所需插件

// rtm 实时消息引入
const rtmModule = uni.requireNativePlugin('AR-RtmModule');

呼叫邀请初始化

uniapp_arcall的是项目启动就登陆 RTM,因此写在 onLaunch中,调试时会发生以下错误

Error: [JS Framework] Failed to receiveTasks, instance (1) is not available.

原因是多次将 RTM 实例初始化,会影响到 RTM 相关的使用请将程序杀死后在进行调试

// 初始化回调
await rtmModule.setCallBack(res => {
			switch (res.rtmEvent) {
			// SDK 与 RTM 系统的连接状态发生改变回调。
			case 'onConnectionStateChanged':
				break;
				// 收到点对点消息回调
			case 'onPeerMessageReceived':
			
				break;
				// 被订阅用户在线状态改变
			case 'onPeersOnlineStatusChanged':
				
				break;
				// 返回给主叫:被叫已接受呼叫邀请
			case 'onLocalInvitationAccepted':
				
				break;
				// 返回给主叫:呼叫邀请已被取消
			case 'onLocalInvitationCanceled':
				
				break;
				// 返回给主叫:呼叫邀请进程失败
			case 'onLocalInvitationFailure':
				
				break;
				// 返回给主叫:被叫已收到呼叫邀请
			case 'onLocalInvitationReceivedByPeer':
				
				break;
				// 返回给主叫:被叫已拒绝呼叫邀请
			case 'onLocalInvitationRefused':
			
				break;
				// 返回给被叫:接受呼叫邀请成功
			case 'onRemoteInvitationAccepted':
			
				break;
				// 返回给被叫:主叫已取消呼叫邀请
			case 'onRemoteInvitationCanceled':
				
				break;
				// 返回给被叫:来自主叫的呼叫邀请进程失败
			case 'onRemoteInvitationFailure':
				
				break;
				// 返回给被叫:收到一个呼叫邀请
			case 'onRemoteInvitationReceived':
			
				break;
				// 返回给被叫:拒绝呼叫邀请成功
			case 'onRemoteInvitationRefused':
				
				break;
			default:
				break;
		}
	})
// 初始化实例
await rtmModule.createInstance({
		"appId": "你的 appid"
	}, res => {
		console.log(res);
	})
// 登录 RTM 系统
await rtmModule.login({
	"token": "",
	"userId": "本地用户标识"
}, (res) => {
	console.log("登录 RTM 系统", res);
})
// // 使用 RTM 呼叫邀请(设置邀请呼叫实例的监听器)
await rtmModule.setCallEventListener();

主叫

  • 查询呼叫用户是否在线

    rtmModule.queryPeersOnlineStatus({
    			"peerIds": ["呼叫用户"]
    		}, (res) => {
    			console.log(res);
    		})
    
  • 呼叫用户在线时发起呼叫并订阅

    • 发起呼叫
      把本地创建的频道房间发送过去
    rtmModule.sendLocalInvitation({
    			"calleeId": calleeId, // 被呼叫者的 user ID
    			"content": JSON.stringify(info) // 邀请内容
    		}, (res) => {
    			resolve(res.code);
    		})
    
    • 订阅(获取对方在线状态)
      当结束通话或结束邀请时记得取消订阅
    rtmModule.subscribePeersOnlineStatus({
    		"peerIds": ["对方 uid"]
    	}, (res) => {
    		//smething
    		console.log("订阅指定单个或多个用户的在线状态", res);
    	})
    
  • 主叫取消呼叫

rtmModule.cancelLocalInvitation({
  		"calleeId": calleeId, // 被呼叫者的 user ID
  		"content": JSON.stringify(info) // 邀请内容
  	}, (res) => {
  		console.log("取消给对方的呼叫邀请", res);
  	});

被叫

通过回调 onRemoteInvitationReceived 收到主叫

  • 拒绝呼叫
rtmModule.refuseRemoteInvitation({
			"calleeId": userId,
			"response": JSON.stringify(info) /邀请内容
		}, (res) => {
		});
  • 接受呼叫
rtmModule.acceptRemoteInvitation({
			"calleeId": calleeId, // 供被叫获取主叫的用户 ID
			"response": info ? JSON.stringify(info) : "" // 邀请响应
		}, (res) => {
		});
// 订阅对方在线状态
...

相关提示

相关的提示、逻辑都可通过回调来进行操作

通话相关逻辑

视频组件必须实在 nvue 页面

<AR-CanvasView ref="location" style="flex: 1;" />

不管是主叫还是被叫,收到接听的回调后就可以进入 RTC 的相关逻辑

  • 引入插件
const rtcModule = uni.requireNativePlugin('AR-RtcModule');
  • 初始化
// 初始化回调 
await rtcModule.setCallBack(res => {
		switch (res.engineEvent) {
			case "onConnectionLost":
				console.log("onConnectionLost", res);
				break;
				// 网络连接状态已改变回调
			case "onConnectionStateChanged":
				console.log("网络连接状态已改变回调", res);
				break;
				// 发生警告回调
			case "onWarning":
				
				break;
				// 发生错误回调
			case "onError":
				
				break;
				// 加入频道成功回调
			case "onJoinChannelSuccess":
				// 本地渲染
			
				break;
				// 远端用户加入当前频道回调
			case "onUserJoined":
			
				break;
				// 远端用户离开当前频道回调
			case "onUserOffline":
				console.log("远端用户离开当前频道回调", res);
			
				break;
				// 网络连接状态已改变回调
			case "onConnectionStateChanged":
			
				break;
				// 已显示远端视频首帧回调
			case "onFirstRemoteVideoFrame":
				break;
			case "onFirstRemoteVideoDecoded":
			
				break;
				// 远端用户视频状态发生已变化回调(当频道内的用户超过 17 时,该回调可能不准确)
			case "onRemoteVideoStateChanged":
			
				break;
				// 	// 本地网络类型发生改变回调
				// case "onNetworkTypeChanged":
				// 	break;
				// 	// 网络连接中断
				// case "onConnectionLost":
				// 	break;

				// 	// 远端音频状态发生改变回调
				// case "onRemoteAudioStateChanged":
				// 	break;
				// 	// 本地音频状态发生改变回调
				// case "onLocalAudioStateChanged":
				// 	break;
				// 	// 本地视频状态发生改变回调
				// case "onLocalVideoStateChanged":
				// 	break;
				// 	// 重新加入频道回调
				// case "onRejoinChannelSuccess":
				// 	break;
				// 	// 离开频道回调
				// case "onLeaveChannel":
				// 	break;
				// 已发送本地音频首帧回调
				// case "onFirstLocalAudioFrame":
				// 	break;
				// 	// 已显示本地视频首帧回调
				// case "onFirstLocalVideoFrame":
				// 	break;
				// 	// Token 服务即将过期回调
				// case "onTokenPrivilegeWillExpire":
				// 	break;
				// 	// Token 过期回调
				// case "onRequestToken":
				// 	break;
				// 	// 用户角色已切换回调(直播场景下)
				// case "onClientRoleChanged":
				// 	break;
				// 	// 本地或远端视频大小或旋转信息发生改变回调
				// case "onVideoSizeChanged":
				// 	break;
				// 	// 通话中远端音频流的统计信息回调
				// case "onRemoteAudioStats":
				// 	break;
				// 	// 当前通话统计回调。 该回调在通话中每两秒触发一次
				// case "onRtcStats":
				// 	break;
				// 	// 通话中每个用户的网络上下行 last mile 质量报告回调
				// case "onNetworkQuality":
				// 	break;
				// 	// 通话中本地视频流的统计信息回调
				// case "onLocalVideoStats":
				// 	break;
				// 	// 通话中本地音频流的统计信息回调
				// case "onLocalAudioStats":
				// 	break;
				// 	// 通话中远端视频流的统计信息回调
				// case "onRemoteVideoStats":
				// 	break;
		}
		
});
// 初始化实例
await rtcModule.create({
	"appId": '你的 appid'
}, res => {
	console.log('初始化实例 rtc', res);
});
// 开启智能降噪
await rtcModule.setParameters({
			Cmd: 'SetAudioAiNoise',
			Enable: 1
		}, (res) => {
			console.log('私人定制', res);
		});
  • 采集音视频
    如果是语音通话可以不执行下列代码
// 设置视频编码属性
await rtcModule.setVideoEncoderConfiguration({},res) => {
console.log('RTC 设置视频编码属性 setVideoEncoderConfiguration 方法调用', res.code ===0 ? '成功' :'失败:' + res);
});
// 启用视频
await rtcModule.enableVideo((res) => {
console.log('RTC 启用视频 enableVideo 方法调用', res.code === 0 ? '成功' : '失败:' +res);
});
  • 加入房间
rtcModule.joinChannel({
			"token": '',
			"channelId": 本地创建的频道/通过呼叫邀请传递过来的频道,
			"uid": 本地的userid,
		}, (res) => {
			console.log('RTC joinChannel 方法调用', res.code === 0 ? '成功' : '失败:' + res);
		});
  • 本地视频渲染

    <AR-CanvasView ref="location" style="flex: 1;" />
    

    获取容器

    // 请确保可以获取到容器
    Store.location = this.$refs.location;
    console.log(Store.location);
    // 打印后 类似这种
    {
    	"ref": "68",
    	"type": "AR-CanvasView",
    	"attr": {
    		"@styleScope": "data-v-39c12bd0"
    	},
    	"style": {
    		"flex": "1"
    	}
    }
    
     // 渲染视频
    	await Store.location.setupLocalVideo({
    		"renderMode": 1,
    		"channelId": 加入的频道房间
    		"uid": 本地的userid
    		"mirrorMode": 0
    	}, (res) => {
    		console.log('渲染视频', res);
    	});
    	// 本地预览
    	await Store.location.startPreview((res) => {
    		console.log('本地预览', res);
    	})
    
  • 远端视频渲染
    通过回调 onFirstRemoteVideoDecoded 获取远端用户发布视频
    Store.remote 与 Store.location 类似

    await Store.remote.setupRemoteVideo({
    		"renderMode": 1,
    		"channelId": 加入的频道
    		"uid": res.uid
    		"mirrorMode": 0
    	}, (res) => {
    		console.log('渲染远端视频', res);
    	})
    	// 本地预览
    	await Store.remote.startPreview((res) => {
    		console.log('远端本地预览', res);
    	})
    
  • 挂断

    	// 销毁实例
    		rtcModule.destroyRtc((res) => {
    			console.log("销毁实例", res);
    		});
    

总结

当前逻辑基本实现呼叫邀请+通话
需要更详细,更具体,更全面的代码请前往 demo 源码 地址

  • 注意事项
    • RTM 初始化、RTC 初始化都只需要执行一次,多次执行请杀掉程序
    • RTM 可以在 vue页面, RTC 视频容器必须在 nvue 页面
 类似资料: