修改
284
pagesmeeting/calling.vue
Normal file
@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="trtc-demo-container">
|
||||
<!-- <view class='title' >
|
||||
<view>多人会议</view>
|
||||
</view> -->
|
||||
<view class="input-box">
|
||||
<input type="number" v-model="roomID" maxlength="10" placeholder="请输入房间号"
|
||||
placeholder-style="opacity: 0.55;" />
|
||||
</view>
|
||||
<view class="choice-content">
|
||||
<view class="label">
|
||||
<text>开启摄像头</text>
|
||||
<u-switch inactiveColor="#999999" activeColor="#00B38A" v-model="localVideo"
|
||||
@change="switchHandler" />
|
||||
</view>
|
||||
<view class="label">
|
||||
<text>开启麦克风</text>
|
||||
<u-switch inactiveColor="#999999" activeColor="#00B38A" v-model="localAudio"
|
||||
@change="switchHandler2" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class='bottom-btn'>
|
||||
<button class="btn" @click="enterRoom" hover-class="none">进入房间</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
genTestUserSig
|
||||
} from './debug/GenerateTestUserSig'
|
||||
import {
|
||||
mapState
|
||||
} from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
roomID: '',
|
||||
localVideo: true,
|
||||
localAudio: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['userInfo'])
|
||||
},
|
||||
onLaunch() {
|
||||
|
||||
},
|
||||
onLoad() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
enterRoom() {
|
||||
const nowTime = new Date()
|
||||
if (nowTime - this.tapTime < 1000) {
|
||||
return
|
||||
}
|
||||
if (!this.roomID) {
|
||||
uni.showToast({
|
||||
title: '请输入房间号',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
return
|
||||
}
|
||||
if (/^\d*$/.test(this.roomID) === false) {
|
||||
uni.showToast({
|
||||
title: '房间号只能为数字',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
return
|
||||
}
|
||||
if (this.roomID > 4294967295 || this.roomID < 1) {
|
||||
uni.showToast({
|
||||
title: '房间号取值范围为 1~4294967295',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
return
|
||||
}
|
||||
const userID = '123'; //userID
|
||||
const Signature = genTestUserSig(userID)
|
||||
const url =
|
||||
`./room/room?roomID=${this.roomID}&localVideo=${this.localVideo}&localAudio=${this.localAudio}&userID=${userID}&sdkAppID=${Signature.sdkAppID}&userSig=${Signature.userSig}`
|
||||
this.tapTime = nowTime
|
||||
this.checkDeviceAuthorize().then((result) => {
|
||||
console.log('授权成功', result)
|
||||
wx.navigateTo({
|
||||
url
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('没有授权', error)
|
||||
})
|
||||
},
|
||||
checkDeviceAuthorize() {
|
||||
this.hasOpenDeviceAuthorizeModal = false
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!wx.getSetting || !wx.getSetting()) {
|
||||
// 微信测试版 获取授权API异常,目前只能即使没授权也可以通过
|
||||
resolve()
|
||||
}
|
||||
wx.getSetting().then((result) => {
|
||||
console.log('getSetting', result)
|
||||
this.authorizeMic = result.authSetting['scope.record']
|
||||
this.authorizeCamera = result.authSetting['scope.camera']
|
||||
if (result.authSetting['scope.camera'] && result.authSetting['scope.record']) {
|
||||
// 授权成功
|
||||
resolve()
|
||||
} else {
|
||||
// 没有授权,弹出授权窗口
|
||||
// 注意: wx.authorize 只有首次调用会弹框,之后调用只返回结果,如果没有授权需要自行弹框提示处理
|
||||
console.log('getSetting 没有授权,弹出授权窗口', result)
|
||||
wx.authorize({
|
||||
scope: 'scope.record',
|
||||
}).then((res) => {
|
||||
console.log('authorize mic', res)
|
||||
this.authorizeMic = true
|
||||
if (this.authorizeCamera) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('authorize mic error', error)
|
||||
this.authorizeMic = false
|
||||
})
|
||||
wx.authorize({
|
||||
scope: 'scope.camera',
|
||||
}).then((res) => {
|
||||
console.log('authorize camera', res)
|
||||
this.authorizeCamera = true
|
||||
if (this.authorizeMic) {
|
||||
resolve()
|
||||
} else {
|
||||
this.openConfirm()
|
||||
reject(new Error('authorize fail'))
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('authorize camera error', error)
|
||||
this.authorizeCamera = false
|
||||
this.openConfirm()
|
||||
reject(new Error('authorize fail'))
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
openConfirm() {
|
||||
if (this.hasOpenDeviceAuthorizeModal) {
|
||||
return
|
||||
}
|
||||
this.hasOpenDeviceAuthorizeModal = true
|
||||
return uni.showModal({
|
||||
content: '您没有打开麦克风和摄像头的权限,是否去设置打开?',
|
||||
confirmText: '确认',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
this.hasOpenDeviceAuthorizeModal = false
|
||||
console.log(res)
|
||||
// 点击“确认”时打开设置页面
|
||||
if (res.confirm) {
|
||||
console.log('用户点击确认')
|
||||
wx.openSetting({
|
||||
success: (res) => {},
|
||||
})
|
||||
} else {
|
||||
console.log('用户点击取消')
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
switchHandler(e) {
|
||||
this.localVideo = e;
|
||||
},
|
||||
switchHandler2(e) {
|
||||
this.localAudio = e;
|
||||
},
|
||||
onBack() {
|
||||
wx.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #F5F5F5;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.trtc-demo-container {
|
||||
/* background-image: url(https://mc.qcloudimg.com/static/img/7da57e0050d308e2e1b1e31afbc42929/bg.png); */
|
||||
/* background-color: #333; */
|
||||
/* background-repeat:no-repeat;
|
||||
background-size: cover; */
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.trtc-demo-container .title {
|
||||
color: #FFFFFF;
|
||||
padding-top: 65rpx;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
|
||||
.trtc-demo-container .input-box {
|
||||
background-color: transparent;
|
||||
color: #333;
|
||||
padding: 2vw 5vw 1vw;
|
||||
border-bottom: 1px solid #577785;
|
||||
margin: 100rpx 0 40rpx 0;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
width: 80vw;
|
||||
}
|
||||
|
||||
.trtc-demo-container .input-box input {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.choice-content {
|
||||
margin-top: 20rpx;
|
||||
width: 80vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* justify-content: space-between;
|
||||
flex-wrap: wrap; */
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.choice-content switch {
|
||||
color: #00B38A;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
|
||||
.bottom-btn {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
bottom: 5vh;
|
||||
}
|
||||
|
||||
.bottom-btn .btn {
|
||||
width: 80%;
|
||||
background-color: #00B38A;
|
||||
border-radius: 50px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
padding-left: 5vw;
|
||||
padding-right: 5vw;
|
||||
width: 50rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
</style>
|
||||
59
pagesmeeting/debug/GenerateTestUserSig.js
Normal file
@ -0,0 +1,59 @@
|
||||
import LibGenerateTestUserSig from './lib-generate-test-usersig-es.min.js';
|
||||
/**
|
||||
* 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
|
||||
*
|
||||
* 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
|
||||
* 它是腾讯云用于区分客户的唯一标识。
|
||||
*/
|
||||
|
||||
const SDKAPPID = 1600006944; //请设置为实际的 SDKAppID
|
||||
/**
|
||||
* 签名过期时间,建议不要设置的过短
|
||||
* <p>
|
||||
* 时间单位:秒
|
||||
* 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
|
||||
*/
|
||||
|
||||
const EXPIRETIME = 604800;
|
||||
/**
|
||||
* 计算签名用的加密密钥,获取步骤如下:
|
||||
*
|
||||
* step1. 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ),如果还没有应用就创建一个,
|
||||
* step2. 单击“应用配置”进入基础配置页面,并进一步找到“帐号体系集成”部分。
|
||||
* step3. 点击“查看密钥”按钮,就可以看到计算 UserSig 使用的加密的密钥了,请将其拷贝并复制到如下的变量中
|
||||
*
|
||||
* 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
|
||||
* 文档:https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
|
||||
const SECRETKEY = '6ddbc3e7e4aa128b52898df27a35f8f3d5acdca6f34ffa17b87ebc33e83314f1'; //将 密钥 粘贴到此处
|
||||
/*
|
||||
* Module: GenerateTestUserSig
|
||||
*
|
||||
* Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
|
||||
* 其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
|
||||
*
|
||||
* Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
|
||||
*
|
||||
* 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
|
||||
* 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
|
||||
* 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
|
||||
*
|
||||
* 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
|
||||
* 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
|
||||
*
|
||||
* Reference:https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
|
||||
function genTestUserSig(userID) {
|
||||
const generator = new LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME);
|
||||
const userSig = generator.genTestUserSig(userID);
|
||||
return {
|
||||
sdkAppID: SDKAPPID,
|
||||
userSig
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
genTestUserSig
|
||||
};
|
||||
6217
pagesmeeting/debug/lib-generate-test-usersig-es.min.js
vendored
Normal file
869
pagesmeeting/room/room.vue
Normal file
@ -0,0 +1,869 @@
|
||||
<template>
|
||||
<view class="template-grid">
|
||||
<view class="column-1">
|
||||
<!-- :class="playerList.length !=0? 'fullscreen':'fullscreen2'" -->
|
||||
<view class="view-container fullscreen">
|
||||
<live-pusher class="pusher" :data-userid="pusher.userID" :data-streamid="pusher.streamID"
|
||||
:data-streamtype="pusher.streamType" :url="pusher.url" :mode="pusher.mode"
|
||||
:autopush="pusher.autopush" :enable-camera="pusher.enableCamera" :enable-mic="pusher.enableMic"
|
||||
:muted="!pusher.enableMic" :enable-agc="pusher.enableAgc" :enable-ans="pusher.enableAns"
|
||||
: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" :debug="debug" :beauty-style="pusher.beautyStyle"
|
||||
:filter="pusher.filter" @statechange="_pusherStateChangeHandler"
|
||||
@netstatus="_pusherNetStatusHandler" @error="_pusherErrorHandler" @bgmstart="_pusherBGMStartHandler"
|
||||
@bgmprogress="_pusherBGMProgressHandler" @bgmcomplete="_pusherBGMCompleteHandler"
|
||||
@audiovolumenotify="_pusherAudioVolumeNotify" />
|
||||
<view class="no-video" v-if="!pusher.enableCamera">
|
||||
<image class="image" :src="require('../static/images/mute-camera-white.png')"></image>
|
||||
</view>
|
||||
<view class="no-audio" v-if="!pusher.enableMic">
|
||||
<image class="image" :src="require('../static/images/mute-mic-white.png')"></image>
|
||||
</view>
|
||||
<view class="audio-volume" v-if="pusher.enableMic">
|
||||
<image class="image" :src="require('../static/images/micro-open.png')"></image>
|
||||
<view class="audio-active" :style="'height:' + pusher.volume + '%'">
|
||||
<image class="image" :src="require('../static/images/audio-active.png')"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<swiper v-show="show_memberList" class="swiper" :indicator-dots="true" indicatorActiveColor="#00B38A">
|
||||
<swiper-item class="swiper-item" v-for="(items, index) in playerList" :key="index">
|
||||
<view v-for="(item, streamID) in items" :key="streamID" class="player-container"
|
||||
:id="'player-' + item.streamID">
|
||||
<live-player class="player" :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="item.soundMode"
|
||||
:enable-recv-message="item.enableRecvMessage" :auto-pause-if-navigate="item.autoPauseIfNavigate"
|
||||
:auto-pause-if-open-native="item.autoPauseIfOpenNative" :debug="debug"
|
||||
@statechange="_playerStateChange" @fullscreenchange="_playerFullscreenChange"
|
||||
@netstatus="_playerNetStatus" @audiovolumenotify="_playerAudioVolumeNotify" />
|
||||
<view class="no-video" v-if="item.muteVideo">
|
||||
<image class="image" :src="require('../static/images/display-pause-white.png')"></image>
|
||||
<view class="text">
|
||||
<p>{{ item.userID }}</p>
|
||||
</view>
|
||||
</view>
|
||||
<view class="no-video" v-if="!item.hasVideo && !item.muteVideo">
|
||||
<image class="image" :src="require('../static/images/mute-camera-white.png')"></image>
|
||||
<view class="text">
|
||||
<p>{{ item.userID }}</p>
|
||||
</view>
|
||||
<view class="text">
|
||||
<p>对方摄像头未打开</p>
|
||||
</view>
|
||||
</view>
|
||||
<view class="no-audio" v-if="!item.hasAudio">
|
||||
<image class="image" :src="require('../static/images/mute-mic-white.png')"></image>
|
||||
</view>
|
||||
<view class="audio-volume" v-if="item.hasAudio">
|
||||
<image class="image" :src="require('../static/images/micro-open.png')"></image>
|
||||
<view class="audio-active" :style="'height:' + item.volume + '%'">
|
||||
<image class="image" :src="require('../static/images/audio-active.png')"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sub-box">
|
||||
<image class="audio-image" @click="_mutePlayerAudio(item)"
|
||||
:src="item.muteAudio? require('../static/images/mute-mic-white.png') : require('../static/images/micro-open.png')">
|
||||
</image>
|
||||
<image class="audio-image" @click="_mutePlayerVideo(item)"
|
||||
:src="item.muteVideo? require('../static/images/mute-camera-white.png') : require('../static/images/camera.png')">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="bottom-box">
|
||||
<view class="bottom-btns">
|
||||
<view class="btn-normal" @click="_pusherAudioHandler">
|
||||
<image class="btn-image"
|
||||
:src="pusher.enableMic? require('../static/images/audio-true.png') : require('../static/images/audio-false.png')">
|
||||
</image>
|
||||
</view>
|
||||
<view class="btn-normal" @click="_pusherVideoHandler">
|
||||
<image class="btn-image"
|
||||
:src="pusher.enableCamera? require('../static/images/camera-true.png') : require('../static/images/camera-false.png')">
|
||||
</image>
|
||||
</view>
|
||||
<view class="btn-hangup" @click="_hangUp">
|
||||
<image class="btn-image" :src="require('../static/images/hangup.png')"></image>
|
||||
</view>
|
||||
<view class="btn-normal" @click="_setPusherBeautyHandle">
|
||||
<image class="btn-image"
|
||||
:src="pusher.beautyLevel == 9? require('../static/images/beauty-true.png') : require('../static/images/beauty-false.png')">
|
||||
</image>
|
||||
</view>
|
||||
<view class="btn-normal" @click="_switchMemberListPanel">
|
||||
<image class="btn-image" :src="require('../static/images/list.png')"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import TRTC from '../static/trtc-wx';
|
||||
import {
|
||||
mapState
|
||||
} from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
RtcConfig: {
|
||||
sdkAppID: '1600006944', // 必要参数 开通实时音视频服务创建应用后分配的 sdkAppID
|
||||
userID: '', // 必要参数 用户 ID 可以由您的帐号系统指定
|
||||
userSig: '', // 必要参数 身份签名,相当于登录密码的作用
|
||||
},
|
||||
pusher: {
|
||||
enableCamera: false,
|
||||
},
|
||||
//切换后的主频
|
||||
pushed: {
|
||||
enableCamera: false,
|
||||
},
|
||||
playerList: [],
|
||||
show_memberList: true,
|
||||
localAudio: true,
|
||||
localVideo: true,
|
||||
myshow: true,
|
||||
shownum: true,
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
|
||||
computed: {
|
||||
...mapState(['userInfo'])
|
||||
},
|
||||
onLoad(options) {
|
||||
console.log('room onload', options)
|
||||
wx.setKeepScreenOn({
|
||||
keepScreenOn: true,
|
||||
})
|
||||
this.TRTC = new TRTC(this)
|
||||
this.TRTC.setLocalVideoView({
|
||||
streamType: TUIVideoStreamType.kCameraStream,
|
||||
view: 'preview-camera',
|
||||
});
|
||||
// case 1: 桌面端打开摄像头
|
||||
this.TRTC.openLocalCamera();
|
||||
// case 2: 移动端使用前置摄像头打开视频预览
|
||||
this.TRTC.openLocalCamera({
|
||||
isFrontCamera: true
|
||||
});
|
||||
// case 3: 移动端使用后置摄像头打开视频预览
|
||||
this.TRTC.openLocalCamera({
|
||||
isFrontCamera: false
|
||||
});
|
||||
// 将String 类型的 true false 转换成 boolean
|
||||
Object.getOwnPropertyNames(options).forEach((key) => {
|
||||
if (options[key] === 'true') {
|
||||
options[key] = true
|
||||
}
|
||||
if (options[key] === 'false') {
|
||||
options[key] = false
|
||||
}
|
||||
})
|
||||
// this.playerList = this.sliceIntoChunks(this.playerList,2)//测试
|
||||
this.init(options)
|
||||
this.bindTRTCRoomEvent()
|
||||
this.enterRoom({
|
||||
roomID: options.roomID
|
||||
})
|
||||
},
|
||||
onReady() {
|
||||
console.log('room ready')
|
||||
},
|
||||
onUnload() {
|
||||
console.log('room unload')
|
||||
},
|
||||
methods: {
|
||||
init(options) {
|
||||
// pusher 初始化参数
|
||||
const pusherConfig = {
|
||||
beautyLevel: 9,
|
||||
}
|
||||
const pusher = this.TRTC.createPusher(pusherConfig)
|
||||
console.log("pusher", pusher)
|
||||
this.RtcConfig.userID = options.userID;
|
||||
this.RtcConfig.sdkAppID = '1600006944';
|
||||
this.RtcConfig.userSig = options.userSig;
|
||||
this.pusher = pusher.pusherAttributes;
|
||||
},
|
||||
|
||||
enterRoom(options) {
|
||||
const roomID = options.roomID
|
||||
const config = Object.assign(this.RtcConfig, {
|
||||
roomID
|
||||
})
|
||||
console.log(config)
|
||||
this.pusher = this.TRTC.enterRoom(config);
|
||||
console.log("this.pusher", this.pusher)
|
||||
if (this.pusher) {
|
||||
this.TRTC.getPusherInstance().start() // 开始推流
|
||||
}
|
||||
},
|
||||
exitRoom() {
|
||||
const result = this.TRTC.exitRoom();
|
||||
this.pusher = result.pusher;
|
||||
this.playerList = this.sliceIntoChunks(result.playerList, 2);
|
||||
},
|
||||
// 设置 pusher 属性
|
||||
setPusherAttributesHandler(options) {
|
||||
this.pusher = this.TRTC.setPusherAttributes(options);
|
||||
},
|
||||
// 设置某个 player 属性
|
||||
setPlayerAttributesHandler(player, options) {
|
||||
console.log("123", player, options, )
|
||||
//this.playerList = this.TRTC.setPlayerAttributes(player.streamID, options);
|
||||
let playerList = this.TRTC.setPlayerAttributes(player.streamID, options)
|
||||
this.playerList = this.sliceIntoChunks(playerList, 2);
|
||||
console.log("12345678:", this.playerList)
|
||||
},
|
||||
// 事件监听
|
||||
bindTRTCRoomEvent() {
|
||||
const TRTC_EVENT = this.TRTC.EVENT
|
||||
console.log("xxxxxxxxxxxx", TRTC_EVENT)
|
||||
// 初始化事件订阅
|
||||
this.TRTC.on(TRTC_EVENT.LOCAL_JOIN, (event) => {
|
||||
console.log('xxxxxxxxxxxxxxxxxxxxxxxxxxx', event)
|
||||
this.setPusherAttributesHandler({
|
||||
enableCamera: true,
|
||||
enableMic: true
|
||||
})
|
||||
})
|
||||
this.TRTC.on(TRTC_EVENT.LOCAL_LEAVE, (event) => {
|
||||
console.log('* room LOCAL_LEAVE', event)
|
||||
})
|
||||
this.TRTC.on(TRTC_EVENT.ERROR, (event) => {
|
||||
console.log('* room ERROR', event)
|
||||
})
|
||||
this.TRTC.on(TRTC_EVENT.REMOTE_USER_JOIN, (event) => {
|
||||
console.log('* room REMOTE_USER_JOIN', event)
|
||||
const {
|
||||
userID
|
||||
} = event.data;
|
||||
uni.showToast({
|
||||
title: `${userID} 进入了房间`,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
// 远端用户退出
|
||||
this.TRTC.on(TRTC_EVENT.REMOTE_USER_LEAVE, (event) => {
|
||||
console.log('* room REMOTE_USER_LEAVE', event)
|
||||
const {
|
||||
userID,
|
||||
playerList
|
||||
} = event.data
|
||||
this.playerList = this.sliceIntoChunks(playerList, 2);
|
||||
uni.showToast({
|
||||
title: `${userID} 离开了房间`,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
// 远端用户推送视频
|
||||
this.TRTC.on(TRTC_EVENT.REMOTE_VIDEO_ADD, (event) => {
|
||||
console.log('* room REMOTE_VIDEO_ADD', event)
|
||||
const {
|
||||
player
|
||||
} = event.data
|
||||
// 开始播放远端的视频流,默认是不播放的
|
||||
this.setPlayerAttributesHandler(player, {
|
||||
muteVideo: false
|
||||
})
|
||||
})
|
||||
// 远端用户取消推送视频
|
||||
this.TRTC.on(TRTC_EVENT.REMOTE_VIDEO_REMOVE, (event) => {
|
||||
console.log('* room REMOTE_VIDEO_REMOVE', event)
|
||||
const {
|
||||
player
|
||||
} = event.data
|
||||
console.log("234", player)
|
||||
this.setPlayerAttributesHandler(player, {
|
||||
muteVideo: true
|
||||
})
|
||||
})
|
||||
// 远端用户推送音频
|
||||
this.TRTC.on(TRTC_EVENT.REMOTE_AUDIO_ADD, (event) => {
|
||||
console.log('* room REMOTE_AUDIO_ADD', event)
|
||||
const {
|
||||
player
|
||||
} = event.data
|
||||
console.log("345", player)
|
||||
this.setPlayerAttributesHandler(player, {
|
||||
muteAudio: false
|
||||
})
|
||||
})
|
||||
// 远端用户取消推送音频
|
||||
this.TRTC.on(TRTC_EVENT.REMOTE_AUDIO_REMOVE, (event) => {
|
||||
console.log('* room REMOTE_AUDIO_REMOVE', event)
|
||||
const {
|
||||
player
|
||||
} = event.data
|
||||
this.setPlayerAttributesHandler(player, {
|
||||
muteAudio: true
|
||||
})
|
||||
})
|
||||
this.TRTC.on(TRTC_EVENT.REMOTE_AUDIO_VOLUME_UPDATE, (event) => {
|
||||
console.log('* room REMOTE_AUDIO_VOLUME_UPDATE', event)
|
||||
const {
|
||||
playerList
|
||||
} = event.data;
|
||||
this.playerList = this.sliceIntoChunks(playerList, 2);
|
||||
console.log("@@@@", this.playerList)
|
||||
})
|
||||
this.TRTC.on(TRTC_EVENT.LOCAL_AUDIO_VOLUME_UPDATE, (event) => {
|
||||
// console.log('* room LOCAL_AUDIO_VOLUME_UPDATE', event)
|
||||
const {
|
||||
pusher
|
||||
} = event.data
|
||||
this.pusher = pusher;
|
||||
})
|
||||
},
|
||||
|
||||
// 是否订阅某一个player Audio
|
||||
_mutePlayerAudio(player) {
|
||||
console.log('22222', player)
|
||||
//const player = event.currentTarget.dataset.value
|
||||
if (player.hasAudio && player.muteAudio) {
|
||||
this.setPlayerAttributesHandler(player, {
|
||||
muteAudio: false
|
||||
})
|
||||
return
|
||||
}
|
||||
if (player.hasAudio && !player.muteAudio) {
|
||||
this.setPlayerAttributesHandler(player, {
|
||||
muteAudio: true
|
||||
})
|
||||
return
|
||||
}
|
||||
},
|
||||
|
||||
// 订阅 / 取消订阅某一个player Audio
|
||||
_mutePlayerVideo(player) {
|
||||
console.log("1111")
|
||||
console.log(player)
|
||||
//const player = event.currentTarget.dataset.value
|
||||
if (player.hasVideo && player.muteVideo) {
|
||||
this.setPlayerAttributesHandler(player, {
|
||||
muteVideo: false
|
||||
})
|
||||
return
|
||||
}
|
||||
if (player.hasVideo && !player.muteVideo) {
|
||||
this.setPlayerAttributesHandler(player, {
|
||||
muteVideo: true
|
||||
})
|
||||
return
|
||||
}
|
||||
},
|
||||
|
||||
// 挂断退出房间
|
||||
_hangUp() {
|
||||
this.exitRoom()
|
||||
wx.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
},
|
||||
|
||||
// 设置美颜
|
||||
_setPusherBeautyHandle() {
|
||||
const beautyLevel = this.pusher.beautyLevel === 0 ? 9 : 0
|
||||
this.setPusherAttributesHandler({
|
||||
beautyLevel
|
||||
})
|
||||
},
|
||||
|
||||
// 订阅 / 取消订阅 Audio
|
||||
_pusherAudioHandler() {
|
||||
if (this.pusher.enableMic) {
|
||||
this.setPusherAttributesHandler({
|
||||
enableMic: false
|
||||
})
|
||||
} else {
|
||||
this.setPusherAttributesHandler({
|
||||
enableMic: true
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 订阅 / 取消订阅 Video
|
||||
_pusherVideoHandler() {
|
||||
if (this.pusher.enableCamera) {
|
||||
this.setPusherAttributesHandler({
|
||||
enableCamera: false
|
||||
})
|
||||
} else {
|
||||
this.setPusherAttributesHandler({
|
||||
enableCamera: true
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
_switchMemberListPanel() {
|
||||
if (this.playerList.length == 0) {
|
||||
uni.showToast({
|
||||
title: "暂无成员",
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
} else {
|
||||
this.show_memberList = !this.show_memberList;
|
||||
}
|
||||
},
|
||||
_handleClose() {
|
||||
this.show_memberList = false;
|
||||
// this.setData({
|
||||
// show_memberList: false
|
||||
// })
|
||||
},
|
||||
// 请保持跟 wxml 中绑定的事件名称一致
|
||||
_pusherStateChangeHandler(event) {
|
||||
this.TRTC.pusherEventHandler(event)
|
||||
},
|
||||
_pusherNetStatusHandler(event) {
|
||||
this.TRTC.pusherNetStatusHandler(event)
|
||||
},
|
||||
_pusherErrorHandler(event) {
|
||||
this.TRTC.pusherErrorHandler(event)
|
||||
},
|
||||
_pusherBGMStartHandler(event) {
|
||||
this.TRTC.pusherBGMStartHandler(event)
|
||||
},
|
||||
_pusherBGMProgressHandler(event) {
|
||||
this.TRTC.pusherBGMProgressHandler(event)
|
||||
},
|
||||
_pusherBGMCompleteHandler(event) {
|
||||
this.TRTC.pusherBGMCompleteHandler(event)
|
||||
},
|
||||
_pusherAudioVolumeNotify(event) {
|
||||
this.TRTC.pusherAudioVolumeNotify(event)
|
||||
},
|
||||
_playerStateChange(event) {
|
||||
this.TRTC.playerEventHandler(event)
|
||||
},
|
||||
_playerFullscreenChange(event) {
|
||||
this.TRTC.playerFullscreenChange(event)
|
||||
},
|
||||
_playerNetStatus(event) {
|
||||
this.TRTC.playerNetStatus(event)
|
||||
},
|
||||
_playerAudioVolumeNotify(event) {
|
||||
this.TRTC.playerAudioVolumeNotify(event)
|
||||
},
|
||||
|
||||
//数组重构
|
||||
sliceIntoChunks(arr, chunkSize) {
|
||||
const res = [];
|
||||
console.log(arr.length)
|
||||
for (let i = 0; i < arr.length; i += chunkSize) {
|
||||
const chunk = arr.slice(i, i + chunkSize);
|
||||
console.log(chunk)
|
||||
res.push(chunk);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
//切换为主频
|
||||
toggle(e, i, j) {
|
||||
// this.setPlayerAttributesHandler(e, { muteVideo: false })
|
||||
// console.log(this.shownum)
|
||||
// console.log('######',e,i,j)
|
||||
// console.log('zhu:',this.pusher,this.pusher.muteVideo,e.muteVideo)
|
||||
// if(e.userID == this.userInfo.userId){
|
||||
// this.setPlayerAttributesHandler(this.pushed, { muteVideo: false })
|
||||
// this.playerList[i].splice(j,1,this.pushed);
|
||||
// this.myshow = true;
|
||||
// this.shownum = true
|
||||
// }else{
|
||||
// this.myshow = false;
|
||||
// if(this.shownum){
|
||||
// this.playerList[i].splice(j,1,this.pusher);
|
||||
// this.pushed = e;
|
||||
// this.shownum = false
|
||||
// }else{
|
||||
// this.shownum = false
|
||||
// //this.setPlayerAttributesHandler(this.pushed, { muteVideo: false })
|
||||
// this.playerList[i].splice(j,1,this.pushed);
|
||||
// this.pushed = e;
|
||||
// }
|
||||
// }
|
||||
|
||||
// this.setPlayerAttributesHandler(e, { muteVideo: false })
|
||||
// console.log('######',e,i,j)
|
||||
// console.log('zhu:',this.pusher,this.pusher.muteVideo,e.muteVideo)
|
||||
if (e.userID == this.userInfo.userId) {
|
||||
this.playerList[i].splice(j, 1, this.pushed);
|
||||
this.myshow = true;
|
||||
} else {
|
||||
this.playerList[i].splice(j, 1, this.pusher);
|
||||
this.pushed = e;
|
||||
this.myshow = false;
|
||||
}
|
||||
// this.setPlayerAttributesHandler(this.playerList, { muteVideo: false })
|
||||
console.log('$$$$$$$$$$$', this.playerList)
|
||||
},
|
||||
/**
|
||||
* 切换前后摄像头
|
||||
*/
|
||||
switchCamera() {
|
||||
if (!this.cameraPosition) {
|
||||
// this.data.pusher.cameraPosition 是初始值,不支持动态设置
|
||||
this.cameraPosition = this.pusher.frontCamera;
|
||||
}
|
||||
|
||||
console.log(TAG_NAME, 'switchCamera', this.cameraPosition);
|
||||
this.cameraPosition = this.cameraPosition === 'front' ? 'back' : 'front';
|
||||
this.setData({
|
||||
cameraPosition: this.cameraPosition
|
||||
}, () => {
|
||||
console.log(TAG_NAME, 'switchCamera success', this.cameraPosition);
|
||||
}); // wx 7.0.9 不支持动态设置 pusher.frontCamera ,只支持调用 API switchCamer() 设置,这里修改 cameraPosition 是为了记录状态
|
||||
|
||||
this.pusher.getPusherContext().switchCamera();
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/* 9人 会议模版 */
|
||||
.template-grid {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: #F5F5F5;
|
||||
/* background-image: url(https://mc.qcloudimg.com/static/img/7da57e0050d308e2e1b1e31afbc42929/bg.png); */
|
||||
/* display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap; */
|
||||
}
|
||||
|
||||
.pusher {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.player {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
// max-height: calc(100vh - 170rpx);
|
||||
// min-height: calc(100vh - 170rpx);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/*flex: 1;*/
|
||||
}
|
||||
|
||||
|
||||
.view-container {
|
||||
position: relative;
|
||||
width: 100vh;
|
||||
}
|
||||
|
||||
.no-video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
font-size: 24rpx;
|
||||
border-radius: 16rpx;
|
||||
|
||||
.image {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.fullscreen {
|
||||
width: 100vw;
|
||||
height: calc(100vh - 196rpx);
|
||||
}
|
||||
|
||||
live-player {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
|
||||
.template-grid .btn-normal {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
margin: 0 6rpx;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.template-grid .btn-normal .btn-image {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
|
||||
.template-grid .btn-hangup {
|
||||
background: #f75c45;
|
||||
}
|
||||
|
||||
.template-grid .panel {
|
||||
position: absolute;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
width: 90vw;
|
||||
height: auto;
|
||||
z-index: 999;
|
||||
top: 50vh;
|
||||
left: 50vw;
|
||||
transform: translate(-50%, -50%);
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20rpx 0;
|
||||
border-radius: 10rpx;
|
||||
box-sizing: border-box;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.panel .close-btn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 10rpx 20rpx;
|
||||
}
|
||||
|
||||
.panel .panel-header {
|
||||
text-align: center;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.panel .panel-tips {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.panel .panel-body {
|
||||
flex: 1;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.panel .panel-body .scroll-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.memberlist-panel .panel-body {
|
||||
height: 30vh;
|
||||
|
||||
.audio-image {
|
||||
padding: 0 12rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.memberlist-panel .member-item {
|
||||
display: flex;
|
||||
/* border-bottom: 1px solid #999; */
|
||||
margin: 16rpx 16rpx 16rpx 32rpx;
|
||||
}
|
||||
|
||||
.memberlist-panel .member-id {
|
||||
width: 60%;
|
||||
font-size: 24rpx;
|
||||
line-height: 64rpx;
|
||||
}
|
||||
|
||||
.memberlist-panel .member-btns {
|
||||
width: 70%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.memberlist-panel .member-btns .btn-normal {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.memberlist-panel .member-btns .btn {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.sub-box {
|
||||
position: absolute;
|
||||
right: 10rpx;
|
||||
bottom: 24rpx;
|
||||
width: 80rpx;
|
||||
height: 172rpx;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
|
||||
.audio-image {
|
||||
padding: 0 14rpx;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.no-audio,
|
||||
.audio-volume {
|
||||
position: absolute;
|
||||
bottom: 20rpx;
|
||||
left: 20rpx;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
|
||||
.image {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
position: absolute;
|
||||
/*android 的bug ,image absolute后会向上漂移几个像素,如果要对其必须都设置absolute*/
|
||||
}
|
||||
}
|
||||
|
||||
.audio-active {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.audio-active .image {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.slide-up-tips {
|
||||
position: absolute;
|
||||
bottom: -100rpx;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
width: 200rpx;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-up-tips .image {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
|
||||
.player-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.player-placeholder .image {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
|
||||
.bottom-box {
|
||||
width: 100vw;
|
||||
height: 196rpx;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
|
||||
.bottom-btns {
|
||||
z-index: 3;
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
.btn-hangup {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
background: #f75c45;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-normal {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
background: white;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.btn-hangup .btn-image,
|
||||
.btn-normal .btn-image {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.swiper {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
width: 100vw;
|
||||
height: 48vh;
|
||||
padding: 0 12rpx;
|
||||
background-color: #F5F5F5;
|
||||
|
||||
.swiper-item {
|
||||
position: relative;
|
||||
background-color: #F5F5F5;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
// flex-wrap: wrap;
|
||||
height: 95% !important;
|
||||
|
||||
.player-container {
|
||||
border-radius: 16rpx;
|
||||
position: relative;
|
||||
margin: 24rpx 12rpx 12rpx 12rpx;
|
||||
width: 45%;
|
||||
height: 93%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
pagesmeeting/static/images/audio-active.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
pagesmeeting/static/images/audio-false.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
pagesmeeting/static/images/audio-true.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
pagesmeeting/static/images/avatar0_100.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
pagesmeeting/static/images/back.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
pagesmeeting/static/images/beauty-false.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
pagesmeeting/static/images/beauty-true.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
pagesmeeting/static/images/beauty.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
pagesmeeting/static/images/call.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
pagesmeeting/static/images/camera-false.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
pagesmeeting/static/images/camera-true.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
pagesmeeting/static/images/camera.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
pagesmeeting/static/images/close-white-big.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
pagesmeeting/static/images/close-white.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
pagesmeeting/static/images/display-pause-false.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
pagesmeeting/static/images/display-pause-true.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
pagesmeeting/static/images/display-pause-white.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
pagesmeeting/static/images/display-play-false.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
pagesmeeting/static/images/display-play-true.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
pagesmeeting/static/images/display-play-white.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
pagesmeeting/static/images/doubleroom.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
pagesmeeting/static/images/fullscreen-white.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
pagesmeeting/static/images/fullscreen.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
pagesmeeting/static/images/hangup-red.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
pagesmeeting/static/images/hangup.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
pagesmeeting/static/images/im-disable.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
pagesmeeting/static/images/im-white.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
pagesmeeting/static/images/im.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
pagesmeeting/static/images/list-white.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
pagesmeeting/static/images/list.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
pagesmeeting/static/images/loading.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
pagesmeeting/static/images/logo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
pagesmeeting/static/images/micro-open.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
pagesmeeting/static/images/more-disable.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
pagesmeeting/static/images/more-enable.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
pagesmeeting/static/images/more-white.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
pagesmeeting/static/images/multiroom.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
pagesmeeting/static/images/music-white.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
pagesmeeting/static/images/mute-camera-gray.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
pagesmeeting/static/images/mute-camera-white.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
pagesmeeting/static/images/mute-mic-gray.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
pagesmeeting/static/images/mute-mic-white.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
pagesmeeting/static/images/phone.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
pagesmeeting/static/images/setting-white.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
pagesmeeting/static/images/setting.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
pagesmeeting/static/images/slide-up.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
pagesmeeting/static/images/speaker-false.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
pagesmeeting/static/images/speaker-true.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
pagesmeeting/static/images/speaker-white.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
pagesmeeting/static/images/switch.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |