xinelu-applet-ui/pagesmeeting/room/room.vue
2023-11-08 15:33:37 +08:00

898 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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 class="panel memberlist-panel" v-if="show_memberList">
<view @click="_handleClose" class='close-btn'>X</view>
<view class="panel-header">成员列表</view>
<view class="panel-body">
<view class="panel-tips" v-if="playerList.length == 0">暂无成员</view>
<scroll-view class="scroll-container" scroll-y="true">
<view v-for="(items, index) in playerList" :key="index">
<view class="member-item" v-for="(item, streamID) in items" :key="streamID">
<view class="member-id">{{ item.userID }}</view>
<view class="member-btns">
<view class="btn">
<image class="audio-image" @click="_mutePlayerAudio(item)"
:src="item.muteAudio? require('../static/images/mute-mic-white.png') : require('../static/images/micro-open.png')">
</image>
</view>
<view class="btn">
<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>
</view>
</view>
</scroll-view>
</view>
</view> -->
</view>
</template>
<script>
import TRTC from '../static/trtc-wx';
import {
mapState
} from 'vuex';
export default {
data() {
return {
RtcConfig: {
sdkAppID: '', // 必要参数 开通实时音视频服务创建应用后分配的 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)
// 将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) {
console.log("options", options)
// pusher 初始化参数
const pusherConfig = {
beautyLevel: 9,
}
const pusher = this.TRTC.createPusher(pusherConfig)
console.log("pusher", pusher)
console.log('userID', this.RtcConfig)
this.RtcConfig.userID = options.userID;
this.RtcConfig.sdkAppID = '1600006944';
this.RtcConfig.userSig = options.userSig;
this.pusher = pusher.pusherAttributes;
console.log(this.localAudio, this.localVideo)
console.log("000000000000000")
},
enterRoom(options) {
const roomID = options.roomID
const config = Object.assign(this.RtcConfig, {
roomID
})
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('* room LOCAL_JOIN', event)
if (this.localVideo) {
this.setPusherAttributesHandler({
enableCamera: true
})
}
if (this.localAudio) {
this.setPusherAttributesHandler({
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>