This commit is contained in:
zoujiandong
2024-01-22 08:55:30 +08:00
parent 22f3da6c33
commit 2bd2fd31ac
3460 changed files with 136738 additions and 0 deletions
@@ -0,0 +1,196 @@
import { TUIStore, StoreName, NAME } from "../index";
const {
CALL_STATUS,
CALL_ROLE,
CALL_MEDIA_TYPE,
LOCAL_USER_INFO,
REMOTE_USER_INFO_LIST,
CALLER_USER_INFO,
IS_GROUP,
CALL_DURATION,
PUSHER,
PLAYER,
BIG_SCREEN_USER_ID,
IS_EAR_PHONE,
LOCAL_VIDEO,
MYSELF,
TOAST_INFO,
} = NAME;
Component({
properties: {},
data: {
callStatus: TUIStore.getData(StoreName.CALL, CALL_STATUS), // 通话状态
isGroupCall: TUIStore.getData(StoreName.CALL, IS_GROUP), // 是否群通话
callMediaType: TUIStore.getData(StoreName.CALL, CALL_MEDIA_TYPE), // 通话类型
callDuration: TUIStore.getData(StoreName.CALL, CALL_DURATION), // 通话时长
callRole: TUIStore.getData(StoreName.CALL, CALL_ROLE), // 通话角色
isEarPhone: TUIStore.getData(StoreName.CALL, IS_EAR_PHONE), // 声音模式 听筒/扬声器
bigScreenUserId: TUIStore.getData(StoreName.CALL, BIG_SCREEN_USER_ID), // 大屏显示的用户
localUserInfo: TUIStore.getData(StoreName.CALL, LOCAL_USER_INFO), // 本地用户信息
callerUserInfo: TUIStore.getData(StoreName.CALL, CALLER_USER_INFO),// 邀请者用户信息
remoteUserInfoList: TUIStore.getData(StoreName.CALL, REMOTE_USER_INFO_LIST), // 远端用户信息
pusher: TUIStore.getData(StoreName.CALL, PUSHER), // TRTC 本地流
playerList: TUIStore.getData(StoreName.CALL, PLAYER), // TRTC 远端流
playerProcess: {}, // 经过处理的的远端流(多人通话)
},
methods: {
// 监听通话状态变更回调
handleCallStatusChange(value) {
this.setData({
callStatus: value,
});
},
// 监听是否群组通话变更回调
handleIsGroupChange(value) {
this.setData({
isGroupCall: value,
});
},
// 监听通话类型变更回调
handleCallMediaTypeChange(value) {
this.setData({
callMediaType: value,
});
},
// 监听通话角色变更回调
handleCallRoleChange(value) {
this.setData({
callRole: value,
});
},
// 监听通话时长变更回调
handleCallDurationChange(value) {
this.setData({
callDuration: value,
});
},
// 监听声音模式变更回调
handleEarPhoneChange(value) {
this.setData({
isEarPhone: value,
});
},
// 监听大屏显示的用户变更回调
handleScreenChange(value) {
if (value === LOCAL_VIDEO) {
this.setData({
bigScreenUserId: true,
});
} else {
this.setData({
bigScreenUserId: false,
});
}
},
// 监听本地用户信息变更回调
handleLocalUserInfoChange(value) {
this.setData({
localUserInfo: value,
});
},
// 监听到邀请者信息变更回调
handleCallerUserInfoChange(value) {
this.setData({
callerUserInfo: value,
});
},
// 监听远端用户信息变更回调
handleRemoteUserInfoListChange(value) {
this.setData({
remoteUserInfoList: value,
});
},
// 监听 TRTC 本地流变更回调
handlePusherChange(value) {
this.setData({
pusher: value,
});
},
// 监听 TRTC 远端流变更回调
handlePlayerListChange(value) {
if (this.data.isGroupCall) {
const convertToPlayer = this.convertToObj(value);
this.setData({
playerProcess: convertToPlayer,
});
} else {
this.setData({
playerList: value,
});
}
},
// 监听到弹窗信息变更回调
handleToastInfoChange(value) {
if (value.text) {
this.showToast(value.text, value.type || "info");
}
},
showToast(value, type) {
switch (type) {
case "info":
wx.showToast({
title: value,
icon: "none",
});
break;
default:
break;
}
},
convertToObj(arr = []) {
const tempObject = {};
for (let i = 0; i < arr.length; i++) {
tempObject[arr[i].userID] = arr[i];
}
return tempObject;
},
},
// 生命周期方法
lifetimes: {
attached() {
let that = this;
TUIStore.watch(
StoreName.CALL,
{
[CALL_STATUS]: this.handleCallStatusChange.bind(that),
[IS_GROUP]: this.handleIsGroupChange.bind(that),
[CALL_MEDIA_TYPE]: this.handleCallMediaTypeChange.bind(that),
[CALL_DURATION]: this.handleCallDurationChange.bind(that),
[CALL_ROLE]: this.handleCallRoleChange.bind(that),
[IS_EAR_PHONE]: this.handleEarPhoneChange.bind(that),
[BIG_SCREEN_USER_ID]: this.handleScreenChange.bind(that),
[LOCAL_USER_INFO]: this.handleLocalUserInfoChange.bind(that),
[CALLER_USER_INFO]: this.handleCallerUserInfoChange.bind(that),
[REMOTE_USER_INFO_LIST]: this.handleRemoteUserInfoListChange.bind(that),
[TOAST_INFO]: this.handleToastInfoChange.bind(that),
[PUSHER]: this.handlePusherChange.bind(that),
[PLAYER]: this.handlePlayerListChange.bind(that),
},
{
notifyRangeWhenWatch: MYSELF,
}
);
},
async detached() {
let that = this;
TUIStore.unwatch(StoreName.CALL, {
[CALL_STATUS]: this.handleCallStatusChange.bind(that),
[IS_GROUP]: this.handleIsGroupChange.bind(that),
[CALL_MEDIA_TYPE]: this.handleCallMediaTypeChange.bind(that),
[CALL_DURATION]: this.handleCallDurationChange.bind(that),
[CALL_ROLE]: this.handleCallRoleChange.bind(that),
[IS_EAR_PHONE]: this.handleEarPhoneChange.bind(that),
[BIG_SCREEN_USER_ID]: this.handleScreenChange.bind(that),
[LOCAL_USER_INFO]: this.handleLocalUserInfoChange.bind(that),
[CALLER_USER_INFO]: this.handleCallerUserInfoChange.bind(that),
[REMOTE_USER_INFO_LIST]: this.handleRemoteUserInfoListChange.bind(that),
[TOAST_INFO]: this.handleToastInfoChange.bind(that),
[PUSHER]: this.handlePusherChange.bind(that),
[PLAYER]: this.handlePlayerListChange.bind(that),
});
},
},
});
@@ -0,0 +1,9 @@
{
"component": true,
"usingComponents": {
"SingleCall":"./component/SingleCall/SingleCall",
"GroupCall":"./component/GroupCall/GroupCall"
},
"navigationStyle": "custom",
"disableScroll": true
}
@@ -0,0 +1,29 @@
<view wx:if="{{callStatus !== 'idle'}}" class="TUICall-container">
<SingleCall
wx:if="{{!isGroupCall}}"
callRole="{{callRole}}"
callStatus ='{{callStatus}}'
pusher="{{pusher}}"
bigScreenUserId="{{bigScreenUserId}}"
playerList="{{playerList}}"
callDuration="{{callDuration}}"
callMediaType="{{callMediaType}}"
localUserInfo="{{localUserInfo}}"
remoteUserInfoList="{{remoteUserInfoList}}"
isEarPhone="{{isEarPhone}}"
></SingleCall>
<GroupCall
wx:if="{{isGroupCall}}"
callRole="{{callRole}}"
callStatus ='{{callStatus}}'
callMediaType="{{callMediaType}}"
callDuration="{{callDuration}}"
pusher="{{pusher}}"
playerList="{{playerList}}"
localUserInfo="{{localUserInfo}}"
callerUserInfo="{{callerUserInfo}}"
remoteUserInfoList="{{remoteUserInfoList}}"
playerProcess="{{playerProcess}}"
isEarPhone="{{isEarPhone}}"
></GroupCall>
</view>
@@ -0,0 +1,10 @@
.TUICall-container {
width: 100vw;
height: 100vh;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
z-index: 10;
margin: 0;
}
@@ -0,0 +1,116 @@
import { TUICallKitServer } from "../../../TUICallService/index";
const PATH = '../../../static';
Component({
properties: {
callRole: {
type: String,
},
callStatus: {
type: String,
},
callMediaType: {
type: Number,
},
callDuration: {
type: String,
},
pusher: {
type: Object,
},
playerList: {
type: Array,
},
localUserInfo: {
type: Object,
},
callerUserInfo: {
type: Object
},
remoteUserInfoList: {
type: Array,
},
playerProcess: {
type: Object,
},
isEarPhone: {
type: Boolean,
},
},
data: {
renderStreamList: [],
IMG_DEFAULT_AVATAR:`${PATH}/default_avatar.png`,
IMG_LOADING:`${PATH}/loading.png`,
IMG_HANGUP:`${PATH}/hangup.png`,
IMG_ACCEPT: `${PATH}/dialing.png`,
IMG_SPEAKER_FALSE:`${PATH}/speaker-false.png`,
IMG_SPEAKER_TRUE:`${PATH}/speaker-true.png`,
IMG_AUDIO_TRUE:`${PATH}/audio-true.png`,
IMG_AUDIO_FALSE:`${PATH}/audio-false.png`,
IMG_CAMERA_TRUE:`${PATH}/camera-true.png`,
IMG_CAMERA_FALSE:`${PATH}/camera-false.png`,
IMG_TRANS:`${PATH}/trans.png`,
IMG_SWITCH_CAMERA:`${PATH}/switch_camera.png`,
},
observers: {
"localUserInfo, remoteUserInfoList": function (
localUserInfo,
remoteUserInfoList
) {
this.setData({
renderStreamList: [localUserInfo, ...remoteUserInfoList],
});
},
},
methods: {
async accept() {
await TUICallKitServer.accept();
},
async hangup() {
await TUICallKitServer.hangup();
},
async reject() {
await TUICallKitServer.reject();
},
async switchCamera() {
await TUICallKitServer.switchCamera();
},
async microPhoneHandler() {
if (this.data.localUserInfo.isAudioAvailable) {
await TUICallKitServer.closeMicrophone();
} else {
await TUICallKitServer.openMicrophone();
}
},
async cameraHandler() {
if (this.data.localUserInfo.isVideoAvailable) {
await TUICallKitServer.closeCamera();
} else {
await TUICallKitServer.openCamera();
}
},
async toggleSoundMode() {
await TUICallKitServer.setSoundMode();
},
pusherStateChangeHandler(e) {
TUICallKitServer._tuiCallEngine._pusherStateChangeHandler(e);
},
pusherNetStatus(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherErrorHandler(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._pusherAudioVolumeNotify(e);
},
playerStateChange(e) {
TUICallKitServer._tuiCallEngine._playerStateChange(e);
},
playNetStatus(e) {
TUICallKitServer._tuiCallEngine._playNetStatus(e);
},
playerAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._playerAudioVolumeNotify(e);
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,285 @@
<!-- 语音通话 -->
<view class="incoming-call audio-call transition-animation" wx:if="{{callMediaType === 1}}">
<!-- 主叫方呼叫状态 -->
<swiper class="swiper" wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{item.userId !== localUserInfo.userId}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</swiper-item>
</block>
</swiper>
<!-- 被叫方呼叫状态 -->
<view wx:if="{{callRole !== 'caller' && callStatus === 'calling' }}">
<view class="invite-calling-single">
<image class="avatar" src="{{callerUserInfo.avatar || IMG_DEFAULT_AVATAR}}" id="{{renderStreamList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{callerUserInfo.nick || callerUserInfo.userId}}</view>
<view class="invite-txt">邀请你参加多人通话</view>
</view>
<view class="invite-other-txt">参与通话的有:</view>
<view class="invite-other-list">
<view class="invite-other-item" wx:for="{{renderStreamList}}" wx:key="item">
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-other-item-name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
<!-- 主叫/被叫方通话状态 -->
<view wx:if="{{callStatus === 'connected' }}">
<view>
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">
{{item.displayUserInfo || item.nick || item.userId}}
</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
<view class="pusher-audio">
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">
{{item.displayUserInfo || item.nick || item.userId}}
</view>
</view>
<view wx:else>
<!-- 本地流 -->
<view wx:if="{{item.userId===localUserInfo.userId}}" class="pusher-audio" data-screen="pusher" catch:tap="toggleViewSize">
<live-pusher class="pusher-audio" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">我</view>
</view>
</view>
<!-- 远端流 -->
<view class="pusher-audio" wx:else>
<live-player wx:if="{{playerProcess[item.userId]}}" wx:if="{{ playerProcess[item.userId].hasAudio || playerProcess[item.userId].hasVideo }}" class="{{callMediaType === 1 ? 'pusher-audio' : 'pusher-ownvideo'}}" id="{{playerProcess[item.userId].id}}" data-userid="{{playerProcess[item.userId].userID}}" data-streamid="{{playerProcess[item.userId].streamID}}" data-streamtype="{{playerProcess[item.userId].streamType}}" src="{{playerProcess[item.userId].src}}" mode="RTC" autoplay="{{playerProcess[item.userId].autoplay}}" mute-audio="{{playerProcess[item.userId].muteAudio}}" mute-video="{{playerProcess[item.userId].muteVideo}}" orientation="{{playerProcess[item.userId].orientation}}" object-fit="{{playerProcess[item.userId].objectFit}}" background-mute="{{playerProcess[item.userId].enableBackgroundMute}}" min-cache="{{playerProcess[item.userId].minCache}}" max-cache="{{playerProcess[item.userId].maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{playerProcess[item.userId].enableRecvMessage}}" auto-pause-if-navigate="{{playerProcess[item.userId].autoPauseIfNavigate}}" auto-pause-if-open-native="{{playerProcess[item.userId].autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
</view>
<!-- 呼叫阶段按钮 -->
<view wx:if="{{callStatus === 'calling'}}" class="footer">
<view wx:if="{{callRole !== 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px;color: #666666">挂断</view>
</view>
<view class="button-container">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<view style="margin-top:10px;color: #666666">接听</view>
</view>
</view>
<view wx:if="{{callRole === 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px;color: #666666">挂断</view>
</view>
</view>
</view>
<!-- 通话阶段按钮 -->
<view wx:if="{{callStatus === 'connected'}}" class="handle-btns">
<view class="other-view black">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view>麦克风</view>
</view>
<view class="button-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view>挂断</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text>扬声器</text>
</view>
</view>
</view>
</view>
<!-- 视频通话 -->
<view class="invite-call audio-call transition-animation" wx:if="{{callMediaType === 2}}">
<!-- 通话状态 -->
<view wx:if="{{callStatus === 'connected' }}">
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<view wx:else>
<!-- 本地流 -->
<view wx:if="{{item.userId === localUserInfo.userId}}" class="play-item" data-screen="pusher">
<live-pusher class="pusher-ownvideo" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">我</view>
</view>
</view>
<!-- 远端流 -->
<view class="play-item" wx:else>
<live-player class="pusher-ownvideo" wx:if="{{playerProcess[item.userId]}}" wx:if="{{ playerProcess[item.userId].hasAudio || playerProcess[item.userId].hasVideo }}" id="{{playerProcess[item.userId].id}}" data-userid="{{playerProcess[item.userId].userID}}" data-streamid="{{playerProcess[item.userId].streamID}}" data-streamtype="{{playerProcess[item.userId].streamType}}" src="{{playerProcess[item.userId].src}}" mode="RTC" autoplay="{{playerProcess[item.userId].autoplay}}" mute-audio="{{playerProcess[item.userId].muteAudio}}" mute-video="{{playerProcess[item.userId].muteVideo}}" orientation="{{playerProcess[item.userId].orientation}}" object-fit="{{playerProcess[item.userId].objectFit}}" background-mute="{{playerProcess[item.userId].enableBackgroundMute}}" min-cache="{{playerProcess[item.userId].minCache}}" max-cache="{{playerProcess[item.userId].maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{playerProcess[item.userId].enableRecvMessage}}" auto-pause-if-navigate="{{playerProcess[item.userId].autoPauseIfNavigate}}" auto-pause-if-open-native="{{playerProcess[item.userId].autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
<!-- 邀请信息 -->
<view class="invite-calling">
<!-- 主叫方 -->
<swiper class="swiper" wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{item.userId !== localUserInfo.userId}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</swiper-item>
</block>
</swiper>
<!-- 被叫方 -->
<view wx:if="{{callRole !== 'caller' && callStatus === 'calling' }}">
<view class="invite-calling-single">
<image class="avatar" src="{{callerUserInfo.avatar || IMG_DEFAULT_AVATAR}}" id="{{renderStreamList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{ callerUserInfo.nick || callerUserInfo.userId }}</view>
<view class="invite-txt">邀请你参加多人通话</view>
</view>
<view class="invite-other-txt">参与通话的有:</view>
<view class="invite-other-list">
<view class="invite-other-item" wx:for="{{renderStreamList}}" wx:key="item">
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-other-item-name">{{ item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
<!-- 呼叫阶段按钮 -->
<view wx:if="{{callStatus === 'calling' }}" class="footer">
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item">
<view class="btn-container">
<view class="call-operate" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
</view>
<text style="color: #666666">挂断</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole !== 'caller'}}">
<view class="btn-operate-item">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<text style="color: #666666">挂断</text>
</view>
<view class="btn-operate-item">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<text style="color: #666666">接听</text>
</view>
</view>
</view>
<!-- 通话阶段按钮 -->
<view wx:if="{{callStatus === 'connected' }}" class="handle-btns">
<view class="other-view white">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable ? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view class="white">麦克风</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text class="white">扬声器</text>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="cameraHandler">
<image class="btn-image" src="{{localUserInfo.isVideoAvailable? IMG_CAMERA_TRUE: IMG_CAMERA_FALSE}} "></image>
</view>
<text class="white">摄像头</text>
</view>
</view>
<view class="btn-list">
<view class="btn-list-item other-view">
<view class="btn-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view wx:if="{{pusher.enableCamera}}" class="invite-calling-header-left">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="toggleSwitchCamera" />
</view>
</view>
<text class="white">挂断</text>
</view>
</view>
</view>
</view>
</view>
@@ -0,0 +1,754 @@
.transition-animation {
transform: translateY(-100%);
animation: slideInDown 0.5s ease forwards;
}
@keyframes slideInDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
.footer {
position: absolute;
bottom: 5vh;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-operate {
display: flex;
justify-content: space-between;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.btn-operate-item text {
font-size: 14px;
color: #f0e9e9;
padding: 5px;
letter-spacing: 0;
font-weight: 400;
}
.call-switch text {
padding: 5px;
color: #f0e9e9;
font-size: 14px;
}
.call-operate {
width: 8vh;
height: 8vh;
border-radius: 8vh;
margin: 0 15vw;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.tips {
font-size: 20px;
color: #ffffff;
letter-spacing: 0;
margin: 0 auto;
font-weight: 600;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tips-subtitle {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #ffffff;
letter-spacing: 0;
text-align: right;
font-weight: 400;
}
.invite-call {
position: absolute;
top: 0;
z-index: 100;
width: 100%;
height: 187px;
}
.invite-call .local-video {
width: 100px;
height: 187px;
}
.invite-call .invite-calling {
position: absolute;
top: 0;
z-index: 101;
width: 100vw;
height: 100vh;
}
.invite-calling-header {
margin-top: 107px;
display: flex;
justify-content: flex-end;
padding: 0 16px;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: 0;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.invite-calling-header-right {
display: flex;
align-items: center;
}
.invite-calling-header-message {
display: flex;
flex-direction: column;
padding: 0 16px;
}
.invite-calling-header-right image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling .footer {
position: absolute;
bottom: 5vh;
width: 100%;
}
.invite-calling .btn-operate {
display: flex;
justify-content: center;
align-items: center;
}
.hidden {
display: none;
}
.trtc-calling {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
z-index: 99;
}
.audio-call {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
z-index: 100;
background: #ffffff;
}
.audio-call > .btn-operate {
display: flex;
justify-content: center;
}
.audio-call > image {
width: 40vw;
height: 40vw;
display: block;
margin: 20vw 30vw;
margin-top: 40vw;
}
.invite-calling-single > image {
width: 120px;
height: 120px;
border-radius: 12px;
display: block;
margin: 120px auto 15px;
}
.invite-calling-single .tips {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.invite-calling-single .tips-subtitle {
height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #97989c;
letter-spacing: 0;
font-weight: 400;
text-align: center;
}
.swiper {
margin-top: 107px;
min-height: 374px;
}
.invite-calling-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: flex-start;
}
.invite-calling-item {
flex: 0.5;
min-width: 50%;
height: 187px;
position: relative;
}
.invite-calling-item image {
width: 100%;
height: 100%;
}
.invite-calling-item-message {
position: absolute;
top: 0;
left: 0;
float: left;
background: rgba(0, 0, 0, 0.6);
width: 100%;
height: 100%;
z-index: 2;
}
.invite-calling-item-loadimg {
position: absolute;
left: calc(50% - 20px);
top: calc(50% - 20px);
width: 40px;
height: 40px;
-webkit-transform: rotate(360deg);
animation: rotation 2s linear infinite;
-moz-animation: rotation 2s linear infinite;
-webkit-animation: rotation 2s linear infinite;
-o-animation: rotation 2s linear infinite;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.invite-calling-item-loadimg image {
width: 100%;
height: 100%;
}
.invite-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.avatar {
background-color: black;
}
/* 被叫者 */
.invite-txt {
width: 126px;
height: 20px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #333333;
letter-spacing: 0;
margin: 16px auto 60px auto;
}
.invite-other-txt {
width: 112px;
height: 20px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #333333;
letter-spacing: 0;
margin: 0 auto 24px auto;
}
.invite-other-list {
position: absolute;
left: 14vw;
margin-top: 0 auto;
display: flex;
flex-wrap: wrap;
width: 272px;
justify-content: center;
flex-wrap: wrap;
}
.invite-other-item {
flex: 0.25;
text-align: center;
max-width: 64px;
margin: 2px;
}
.invite-other-item image {
border-radius: 10%;
max-width: 64px;
height: 64px;
}
.invite-other-item-name {
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #666666;
letter-spacing: 0;
line-height: 18px;
}
/* 全屏设置 */
.TUICalling-connected-layout {
width: 100%;
height: 100%;
}
.TUICalling-connected-video {
width: 100%;
height: 180%;
}
/* 本地音频 */
.stream-box {
float: left;
width: 187px;
height: 187px;
position: absolute;
top: 13vh;
}
/* 远端音频列表 */
.swiper {
margin-top: 107px;
min-height: 374px;
}
.invite-calling-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: flex-start;
}
.invite-calling-item {
flex: 0.5;
min-width: 50%;
height: 187px;
position: relative;
}
.invite-calling-item image {
width: 100%;
height: 100%;
}
/* 本地视频 */
.play-item {
width: 100%;
height: 187px;
position: relative;
background-color: #000000;
}
.pusher-ownvideo {
width: 100%;
height: 100%;
}
/* 远端视频列表 */
.swiper-list {
min-height: 189px;
}
.player-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: center;
}
.player-item {
flex: 0.5;
min-width: 50%;
min-height: 187px;
}
/* 音量图标 */
.player-control {
background-color: rgba(0, 0, 0, 0.4);
border-radius: 0 6px 6px 0;
color: #fff;
z-index: 999;
position: absolute;
bottom: 0px;
left: 0px;
display: flex;
align-items: center;
height: 32px;
max-width: 50%;
z-index: 99;
}
.player-control image {
width: 32px;
height: 32px;
}
.player-control .name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 10px;
margin-right: 10px;
flex: 1;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.control-buttons {
display: flex;
}
.icon-button {
margin-right: 10px;
}
/* 菜单 */
.handle-btns {
position: absolute;
bottom: 44px;
width: 100vw;
z-index: 3;
display: flex;
flex-direction: column;
}
.handle-btns .btn-list {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-normal {
width: 8vh;
height: 8vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
/* background: white; */
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-image {
width: 100%;
height: 100%;
background: none;
}
.btn-hangup {
width: 8vh;
height: 8vh;
/*background: #f75c45;*/
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-hangup > .btn-image {
width: 100%;
height: 100%;
background: none;
}
.TRTCCalling-call-audio {
width: 100%;
height: 100%;
}
.btn-footer {
position: relative;
}
.btn-footer .multi-camera {
width: 32px;
height: 32px;
}
.btn-footer .camera {
width: 64px;
height: 64px;
position: fixed;
left: 16px;
top: 107px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.7);
}
.btn-footer .camera .camera-image {
width: 32px;
height: 32px;
}
.audio {
padding-top: 15vh;
background: #ffffff;
}
.pusher-audio {
width: 0;
height: 0;
}
.player-audio {
width: 0;
height: 0;
}
.other-view {
display: flex;
flex-direction: column;
align-items: center;
font-size: 18px;
letter-spacing: 0;
font-weight: 400;
padding: 16px;
}
.white {
font-weight: 400;
font-size: 14px;
color: #666666;
padding: 5px;
}
.black {
color: #000000;
padding: 5px;
}
.TRTCCalling-call-audio-box {
margin-top: 100px;
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: center;
}
.TRTCCalling-calling-list {
flex: 0.5;
/*设置最小宽度,才会让元素排不下,导致换行排列*/
min-width: 50%;
min-height: 187px;
position: relative;
}
.TRTCCalling-calling-list image {
width: 100%;
height: 100%;
}
.TRTCCalling-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.btn-list-item {
flex: 1;
display: flex;
justify-content: center;
padding: 16px 0;
}
.btn-image-small {
transform: scale(0.7);
}
.avatar {
background: #dddddd;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: -88px;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.call-switch text {
padding: 0;
font-size: 14px;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
}
.btn-operate-item text {
padding: 8px 0;
font-size: 18px;
color: #666666;
letter-spacing: 0;
font-weight: 400;
font-size: 14px;
}
.invite-calling-item-message {
position: absolute;
top: 0;
left: 0;
float: left;
background: rgba(0, 0, 0, 0.6);
width: 100%;
height: 100%;
z-index: 2;
}
.invite-calling-item-loadimg {
position: absolute;
left: calc(50% - 20px);
top: calc(50% - 20px);
width: 40px;
height: 40px;
-webkit-transform: rotate(360deg);
animation: rotation 2s linear infinite;
-moz-animation: rotation 2s linear infinite;
-webkit-animation: rotation 2s linear infinite;
-o-animation: rotation 2s linear infinite;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.invite-calling-item-loadimg image {
width: 100%;
height: 100%;
}
.invite-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
@@ -0,0 +1,107 @@
import { TUICallKitServer } from "../../../TUICallService/index";
const PATH = '../../../static';
Component({
properties: {
callRole: {
type: String,
},
callStatus: {
type: String,
},
callMediaType: {
type: Number,
},
callDuration: {
type: String,
},
pusher: {
type: Object,
},
playerList: {
type: Array,
},
localUserInfo: {
type:Object
},
remoteUserInfoList: {
type: Array,
},
isEarPhone: {
type: Boolean,
},
bigScreenUserId: {
type: Boolean
}
},
data:{
IMG_DEFAULT_AVATAR:`${PATH}/default_avatar.png`,
IMG_HANGUP:`${PATH}/hangup.png`,
IMG_ACCEPT: `${PATH}/dialing.png`,
IMG_SPEAKER_FALSE:`${PATH}/speaker-false.png`,
IMG_SPEAKER_TRUE:`${PATH}/speaker-true.png`,
IMG_AUDIO_TRUE:`${PATH}/audio-true.png`,
IMG_AUDIO_FALSE:`${PATH}/audio-false.png`,
IMG_CAMERA_TRUE:`${PATH}/camera-true.png`,
IMG_CAMERA_FALSE:`${PATH}/camera-false.png`,
IMG_TRANS:`${PATH}/trans.png`,
IMG_SWITCH_CAMERA:`${PATH}/switch_camera.png`,
},
methods: {
async accept() {
await TUICallKitServer.accept();
},
async hangup() {
await TUICallKitServer.hangup();
},
async reject() {
await TUICallKitServer.reject();
},
async switchCamera() {
await TUICallKitServer.switchCamera();
},
async switchCallMediaType() {
await TUICallKitServer.switchCallMediaType();
},
async microPhoneHandler() {
if (this.data.localUserInfo.isAudioAvailable) {
await TUICallKitServer.closeMicrophone();
} else {
await TUICallKitServer.openMicrophone();
}
},
async cameraHandler() {
if (this.data.localUserInfo.isVideoAvailable) {
await TUICallKitServer.closeCamera();
} else {
await TUICallKitServer.openCamera();
}
},
async toggleSoundMode() {
await TUICallKitServer.setSoundMode();
},
toggleViewSize() {
TUICallKitServer.switchScreen(this.data.bigScreenUserId ? 'player':'localVideo')
},
pusherStateChangeHandler(e) {
TUICallKitServer._tuiCallEngine._pusherStateChangeHandler(e);
},
pusherNetStatus(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherErrorHandler(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._pusherAudioVolumeNotify(e);
},
playerStateChange(e) {
TUICallKitServer._tuiCallEngine._playerStateChange(e);
},
playNetStatus(e) {
TUICallKitServer._tuiCallEngine._playNetStatus(e);
},
playerAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._playerAudioVolumeNotify(e);
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,172 @@
<!-- 音频通话 -->
<view class="{{callMediaType === 1?'audio-call':'invite-call' }} transition-animation">
<!-- 本地流 -->
<view class="{{callMediaType === 1 ? 'pusher-audio' : (bigScreenUserId || callStatus === 'calling' ? 'pusher-video' : 'player')}}" data-screen="pusher" catch:tap="toggleViewSize">
<live-pusher class="{{callMediaType === 1?'pusher-audio':'live'}}" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
</view>
<!-- 远端流 -->
<view wx:for="{{playerList}}" wx:key="streamID" class="view-container player-container" catch:tap="toggleViewSize">
<view class="{{callMediaType === 1 ? 'player-audio' : (!bigScreenUserId ? 'pusher-video' : 'player')}}" data-screen="player">
<live-player class="live" wx:if="{{ item.hasAudio || item.hasVideo }}" id="{{item.id}}" data-userid="{{item.userID}}" data-streamid="{{item.streamID}}" data-streamtype="{{item.streamType}}" src="{{item.src}}" mode="RTC" autoplay="{{item.autoplay}}" mute-audio="{{item.muteAudio}}" mute-video="{{item.muteVideo}}" orientation="{{item.orientation}}" object-fit="{{item.objectFit}}" background-mute="{{item.enableBackgroundMute}}" min-cache="{{item.minCache}}" max-cache="{{item.maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{item.enableRecvMessage}}" auto-pause-if-navigate="{{item.autoPauseIfNavigate}}" auto-pause-if-open-native="{{item.autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
</view>
</view>
<!-- 语言通话邀请信息 -->
<view wx:if="{{callMediaType === 1}}" class="invite-calling-single">
<image class="avatar" src="{{remoteUserInfoList[0].avatar || IMG_DEFAULT_AVATAR}}" id="{{remoteUserInfoList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{remoteUserInfoList[0].displayUserInfo}}</view>
<view wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" class="tips-subtitle">等待对方接受</view>
</view>
<!-- 视频通话邀请信息 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 2}}" class="invite-calling">
<view class="invite-calling-header">
<view class="invite-calling-header-right">
<view class="invite-calling-header-message">
<label class="tips">
{{remoteUserInfoList[0].displayUserInfo}}
</label>
<text class="tips-subtitle" wx:if="{{callRole !== 'caller'}}">邀请你视频通话</text>
<text class="tips-subtitle" wx:else>等待对方接受</text>
</view>
<image class="avatar" src="{{remoteUserInfoList[0].avatar || IMG_DEFAULT_AVATAR}}" id="{{remoteUserInfoList[0].userId}}" binderror="handleErrorImage" />
</view>
</view>
</view>
<!-- 语音通话呼叫状态按钮 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 1}}" class="footer">
<view wx:if="{{callRole !== 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px">挂断</view>
</view>
<view class="button-container">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<view style="margin-top:10px">接听</view>
</view>
</view>
<view wx:if="{{callRole === 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px">挂断</view>
</view>
</view>
</view>
<!-- 视频通话呼叫状态按钮 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 2}}" class="invite-calling">
<view class="footer">
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item call-switch" catch:tap="switchCallMediaType">
<view class="call-operate">
<image src="{{IMG_TRANS}}" />
</view>
<text>切到语音通话</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item">
<view class="btn-container">
<view class="call-operate" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
<view class="invite-calling-header-left">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="switchCamera" />
</view>
</view>
<text>挂断</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole !== 'caller'}}">
<view class="btn-operate-item">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<text>挂断</text>
</view>
<view class="btn-operate-item">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<text>接听</text>
</view>
</view>
</view>
</view>
<!-- 语音通话接通状态按钮 -->
<view wx:if="{{callStatus === 'connected' && callMediaType === 1}}" class="handle-btns">
<view class="other-view black">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view>麦克风</view>
</view>
<view class="button-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view>挂断</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text>扬声器</text>
</view>
</view>
</view>
<!-- 视频通话接听状态按钮 -->
<view wx:if="{{callStatus === 'connected' && callMediaType === 2}}" class="invite-calling">
<view class="handle-btns">
<view class="other-view white">
<text>{{callDuration}}</text>
</view>
<view class="btn-operate-item call-switch" catch:tap="switchCallMediaType">
<view class="call-operate">
<image src="{{IMG_TRANS}}" />
</view>
<text>切到语音通话</text>
</view>
<view class="btn-list">
<view class="button-container" bindtap="microPhoneHandler">
<view class="btn-normal">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view class="white">麦克风</view>
</view>
<view class="button-container" bindtap="toggleSoundMode">
<view class="btn-normal">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text class="white">扬声器</text>
</view>
<view class="button-container" bindtap="cameraHandler">
<view class="btn-normal">
<image class="btn-image" src="{{localUserInfo.isVideoAvailable ? IMG_CAMERA_TRUE: IMG_CAMERA_FALSE}} "></image>
</view>
<text class="white">摄像头</text>
</view>
</view>
<view class="btn-list">
<view class="btn-list-item other-view">
<view class="btn-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view wx:if="{{localUserInfo.isVideoAvailable}}" class="invite-calling-connected">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="switchCamera" />
</view>
</view>
<text class="white">挂断</text>
</view>
</view>
</view>
</view>
</view>
@@ -0,0 +1,508 @@
.footer {
position: absolute;
bottom: 5vh;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-operate {
display: flex;
justify-content: space-between;
z-index: 9999;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.btn-operate-item text {
font-size: 14px;
color: #f0e9e9;
padding: 5px;
letter-spacing: 0;
font-weight: 400;
}
.call-switch text {
padding: 5px;
color: #f0e9e9;
font-size: 14px;
}
.call-operate {
width: 8vh;
height: 8vh;
border-radius: 8vh;
margin: 0 15vw;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.tips {
font-size: 20px;
color: #ffffff;
letter-spacing: 0;
margin: 0 auto;
/* text-shadow: 0 1px 2px rgba(0,0,0,0.40); */
font-weight: 600;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tips-subtitle {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #ffffff;
letter-spacing: 0;
text-align: right;
/* text-shadow: 0 1px 2px rgba(0,0,0,0.30); */
font-weight: 400;
}
.invite-call {
position: absolute;
top: 0;
z-index: 100;
width: 100vw;
height: 100vh;
background-color: black;
}
.transition-animation {
transform: translateY(-100%);
animation: slideInDown 0.5s ease forwards;
}
@keyframes slideInDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
.invite-call .local-video {
width: 100vw;
height: 100vh;
}
.invite-call .invite-calling {
position: absolute;
top: 0;
z-index: 101;
width: 100vw;
height: 100vh;
}
.invite-calling-header {
margin-top: 107px;
display: flex;
justify-content: flex-end;
padding: 0 16px;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: 0;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.invite-calling-header-right {
display: flex;
align-items: center;
}
.invite-calling-header-message {
display: flex;
flex-direction: column;
padding: 0 16px;
}
.invite-calling-header-right image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling .footer {
position: absolute;
bottom: 5vh;
width: 100%;
}
.invite-calling .btn-operate {
display: flex;
justify-content: center;
align-items: center;
}
.hidden {
display: none;
}
.trtc-calling {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
z-index: 99;
}
.audio-call {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
z-index: 100;
background: #ffffff;
}
.audio-call > .btn-operate {
display: flex;
justify-content: center;
}
.audio-call > image {
width: 40vw;
height: 40vw;
display: block;
margin: 20vw 30vw;
margin-top: 40vw;
}
.invite-calling-single > image {
width: 120px;
height: 120px;
border-radius: 12px;
display: block;
margin: 140px auto 15px;
/* margin: 20vw 30vw; */
}
.invite-calling-single .tips {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.invite-calling-single .tips-subtitle {
height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #97989c;
letter-spacing: 0;
font-weight: 400;
text-align: center;
}
.invite-calling-list {
justify-content: flex-start;
}
.invite-calling-item {
position: relative;
margin: 0 12px;
}
.invite-calling-item image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling-item-message {
position: absolute;
background: rgba(0, 0, 0, 0.5);
width: 100px;
height: 100px;
top: 0;
left: 0;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.avatar {
background: #dddddd;
}
.player {
position: absolute;
right: 16px;
top: 107px;
width: 100px;
height: 178px;
padding: 16px;
z-index: 999;
}
.pusher-video {
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
}
.stream-box {
position: relative;
float: left;
width: 50vw;
height: 260px;
/* background-color: #f75c45; */
z-index: 3;
}
.handle-btns {
position: absolute;
bottom: 44px;
width: 100vw;
z-index: 3;
display: flex;
flex-direction: column;
}
.handle-btns .btn-list {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-normal {
width: 8vh;
height: 8vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
/* background: white; */
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-image {
width: 100%;
height: 100%;
background: none;
}
.btn-hangup {
width: 8vh;
height: 8vh;
/*background: #f75c45;*/
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-hangup > .btn-image {
width: 100%;
height: 100%;
background: none;
}
.TRTCCalling-call-audio {
width: 100%;
height: 100%;
}
.btn-footer {
position: relative;
}
.btn-footer .multi-camera {
width: 32px;
height: 32px;
}
.btn-footer .camera {
width: 64px;
height: 64px;
position: fixed;
left: 16px;
top: 107px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.7);
}
.btn-footer .camera .camera-image {
width: 32px;
height: 32px;
}
.TUICalling-connected-layout {
width: 100%;
height: 100%;
}
.audio {
padding-top: 15vh;
background: #ffffff;
}
.pusher-audio {
width: 0;
height: 0;
}
.player-audio {
width: 0;
height: 0;
}
.live {
width: 100%;
height: 100%;
}
.other-view {
display: flex;
flex-direction: column;
align-items: center;
font-size: 18px;
letter-spacing: 0;
font-weight: 400;
padding: 16px;
}
.white {
color: #f0e9e9;
padding: 5px;
font-size: 14px;
}
.black {
color: #000000;
padding: 5px;
}
.TRTCCalling-call-audio-box {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.mutil-img {
justify-content: flex-start !important;
}
.TRTCCalling-call-audio-img {
display: flex;
flex-direction: column;
align-items: center;
}
.TRTCCalling-call-audio-img > image {
width: 25vw;
height: 25vw;
margin: 0 4vw;
border-radius: 4vw;
position: relative;
}
.TRTCCalling-call-audio-img text {
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.btn-list-item {
flex: 1;
display: flex;
justify-content: center;
padding: 16px 0;
}
.btn-image-small {
transform: scale(0.7);
}
.avatar {
background: #dddddd;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-connected {
position: absolute;
right: -88px;
}
.invite-calling-connected image {
width: 32px;
height: 32px;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.call-switch text {
padding: 0;
font-size: 14px;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
}
.btn-operate-item text {
padding: 8px 0;
font-size: 18px;
color: #ffffff;
letter-spacing: 0;
font-weight: 400;
font-size: 14px;
}