video.nvue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. <template>
  2. <view class="video_box" :style="'height:' + phoneHeight + 'px'">
  3. <!-- 本地预览视图 -->
  4. <view class="video_me" v-show="allUserViewObjectList.length > 0">
  5. <!-- <view class="video_me" v-if="videoShow"> -->
  6. <zego-local-view :viewMode="publisherViewModeIndex"
  7. style="width: 150px; height: 200px; flex: 1; position: fixed; right: 0; top: 65px;">
  8. </zego-local-view>
  9. </view>
  10. <!-- 服务器传来的预览图 -->
  11. <view class="video_you" v-for="(item, index) in allUserViewObjectList" :key="item.streamID">
  12. <!-- <view class="video_you"> -->
  13. <zego-remote-view v-if="item.streamID" :streamID="item.streamID" :viewMode="item.viewMode"
  14. style="width:600px;height:600px;">
  15. </zego-remote-view>
  16. </view>
  17. <view class="video_btn">
  18. <view class="video_imgList">
  19. <view class="video_imgList1">
  20. <view class="video_view" @click="turn">
  21. <image class="video_view_img" src="../../static/image/9.png" mode=""></image>
  22. </view>
  23. <text class="video_view_txt">翻转镜头</text>
  24. </view>
  25. <!-- <view class="video_imgList2" v-if="CameraShow">
  26. <view class="video_view" @click="closeCamera">
  27. <image class="video_view_img" src="../../static/image/11.png" mode=""></image>
  28. </view>
  29. <text class="video_view_txt" style="margin: 0 0 0 30rpx;">静音</text>
  30. </view>
  31. <view class="video_imgList2" v-else>
  32. <view class="video_view" @click="openCamera">
  33. <image class="video_view_img" src="../../static/image/12.png" mode=""></image>
  34. </view>
  35. <text class="video_view_txt" style="margin: 0 0 0 30rpx;">开启</text>
  36. </view> -->
  37. <image @click="offVideo(false,true)" style="width: 50px; height: 50px; margin: 0 0 0 330rpx;"
  38. src="../../static/image/10.png" mode=""></image>
  39. </view>
  40. <!-- <image @click="offVideo()" style="width: 50px; height: 50px; margin: 0 0 0 330rpx;"
  41. src="../../static/image/10.png" mode=""></image> -->
  42. </view>
  43. <!-- 消息提醒 -->
  44. <u-toast ref="uToast"></u-toast>
  45. </view>
  46. </template>
  47. <script>
  48. import {
  49. mapState,
  50. mapMutations
  51. } from 'vuex'
  52. import store from '@/store/index.js'; //需要引入store
  53. let App = getApp();
  54. // var API = App.globalData.socketTask;
  55. var API = App.globalData;
  56. import permision from "@/zego-express-video-uniapp/permission.js";
  57. import ZegoExpressEngine from '@/zego-express-video-uniapp/lib/ZegoExpressEngine';
  58. import {
  59. ZegoScenario,
  60. ZegoRoomState,
  61. ZegoUpdateType,
  62. // ZegoViewMode,
  63. ZegoVideoCodecID,
  64. ZegoRemoteDeviceState
  65. } from '@/zego-express-video-uniapp/lib/ZegoExpressDefines'
  66. import {
  67. AppID,
  68. AppSign
  69. } from '@/zego-express-video-uniapp/KeyCenter.js'
  70. import ZegoLocalView from '@/zego-express-video-uniapp/zego-view/ZegoLocalView';
  71. import ZegoRemoteView from '@/zego-express-video-uniapp/zego-view/ZegoRemoteView';
  72. import {
  73. assign,
  74. forEach
  75. } from 'lodash-es';
  76. export default {
  77. data() {
  78. return {
  79. phoneHeight: '', // 获取当前的屏幕高度
  80. // 即构
  81. publisherViewModeIndex: 0, // 本地预览图
  82. viewModeA: 0, // 服务器预览图
  83. serveViewModeIndex: "", // 服务器拉流预览图
  84. engine: undefined,
  85. videoObj: null,
  86. userid: "Uni" + Math.floor(Math.random() * 1000000).toString(),
  87. isPublishingStream: false,
  88. shotShow: false, // 翻转镜头
  89. videoShow: false, // 切换视频显示
  90. allStreamList: [],
  91. allUserViewObjectList: [],
  92. CameraShow: true, // 镜头开启关闭
  93. playerStreamID: 0,
  94. isPendingExit: false
  95. }
  96. },
  97. computed: {
  98. ...mapState({
  99. isVideoCallRefused: state => state.isVideoCallRefused,
  100. currentReceiveUserID: state => state.currentReceiveUserID
  101. })
  102. },
  103. components: {
  104. ZegoLocalView: ZegoLocalView,
  105. ZegoRemoteView: ZegoRemoteView
  106. },
  107. watch: {
  108. // allUserViewObjectList(val) {
  109. // var that = this
  110. // if (val.length > 0) {
  111. // val.forEach((item) => {
  112. // if (item.streamID) {
  113. // this.tensile(item)
  114. // return true
  115. // }
  116. // })
  117. // }
  118. // }
  119. isVideoCallRefused(val) {
  120. console.warn(val, 'isVideoCallRefused video nvue')
  121. if (val) {
  122. this.offVideo(true)
  123. }
  124. }
  125. },
  126. methods: {
  127. ...mapMutations(['updateIsRefusedCall', 'updateTalkingStatus', 'updateOccupyedStatus']),
  128. // 即构 - 视频通话 初始化
  129. async setup() {
  130. var that = this
  131. // 创建引擎
  132. let profile = {
  133. appID: AppID,
  134. appSign: AppSign,
  135. scenario: ZegoScenario.General
  136. }
  137. this.engine = await ZegoExpressEngine.createEngineWithProfile(profile);
  138. this.engine.useFrontCamera(this.shotShow); // 设置前后摄像头
  139. // console.log(this.engine)
  140. if (this.isPendingExit) {
  141. await this.offVideo(true)
  142. }
  143. this.addListeners();
  144. // console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' + that.videoObj.room_id)
  145. const userID = (that.userid = that.videoObj.room_id + that.videoObj.user_id)
  146. console.log(userID, that.userid)
  147. await this.engine.loginRoom(that.videoObj.room_id, {
  148. userID,
  149. userName: that.videoObj.user_id + '-' +
  150. that.videoObj.real_name
  151. }, {
  152. isUserStatusNotify: true
  153. }).then(res => {
  154. console.log(res, 'login room -------------')
  155. }).catch(err => {
  156. console.error(err, 'login room')
  157. });
  158. },
  159. // 即构 - 翻转镜头
  160. turn() {
  161. this.shotShow = !this.shotShow
  162. this.engine.useFrontCamera(this.shotShow)
  163. },
  164. // 即构 - 关闭镜头 - 停止拉流 、推流
  165. closeCamera() {
  166. this.allStreamList.forEach((item) => {
  167. // this.notTensile(item.streamID); // 停止拉流
  168. item.muteAudio = !item.muteAudio;
  169. this.engine.mutePlayStreamAudio(item.streamID, item.muteAudio);
  170. })
  171. // // 停止推流
  172. this.CameraShow = false
  173. },
  174. // 即构 - 开启镜头 - 开启拉流、推流
  175. openCamera() {
  176. console.log('开启摄像头')
  177. this.allStreamList.forEach((item) => {
  178. // this.engine.startPlayingStream(item.streamID); // 开始拉流
  179. item.muteAudio = !item.muteAudio;
  180. this.engine.mutePlayStreamAudio(item.streamID, item.muteAudio);
  181. })
  182. // // 开始推流
  183. // this.engine.startPreview();
  184. // this.engine.startPublishingStream(this.videoObj.room_id);
  185. this.CameraShow = true
  186. },
  187. // 切换视频显示
  188. changeShow() {
  189. this.videoShow = !this.videoShow
  190. // this.onPublish()
  191. this.engine.startPreview();
  192. this.engine.startPublishingStream(this.userid);
  193. this.publishBtnName = "Stop Publishing";
  194. console.log(this.videoShow)
  195. },
  196. // 即构 - 视频通话 监听房间
  197. addListeners() {
  198. console.log('开始监听房间了啊')
  199. var that = this
  200. // console.log(this.allStreamList, '------------------')
  201. // 房间状态变化通知
  202. this.engine.on("roomStateUpdate", (roomID, state, errorCode, extendedData) => {
  203. console.log('房间状态变化', state)
  204. if (state == ZegoRoomState.Connected) {
  205. // 与房间连接成功,只有当房间状态是连接成功时,才能进行推流、拉流等操作。
  206. // 接下来的“预览并推流”的代码写在这里
  207. console.log("房间连接成功");
  208. }
  209. if (state == ZegoRoomState.DisConnected) {
  210. // 与房间断开了连接
  211. console.log("与房间断开连接");
  212. }
  213. if (state == ZegoRoomState.Connecting) {
  214. // 与房间尝试连接中
  215. console.log("与房间尝试连接中");
  216. }
  217. });
  218. // 房间用户变化通知
  219. this.engine.on("roomUserUpdate", (roomID, updateType, userList) => {
  220. console.log(updateType, ZegoUpdateType.Delete, 'roomUserUpdate')
  221. console.log(userList)
  222. console.log("有其他用户进出房间");
  223. if (updateType == ZegoUpdateType.Add) {
  224. console.log('进入了啊AAAAAAAAAA')
  225. for (let user of userList) {
  226. let payload = assign({
  227. streamID: ''
  228. }, user)
  229. for (let stream of this.allStreamList) {
  230. if (user.userID == stream.user.userID) {
  231. payload.streamID = stream.streamID
  232. if (!this.playerStreamID) {
  233. this.playerStreamID = stream.streamID;
  234. this.tensile(payload)
  235. }
  236. }
  237. }
  238. const index = this.allUserViewObjectList.findIndex(item => item.userID === payload
  239. .userID)
  240. console.log('-----------------------all user view object list ', index, payload)
  241. if (index === -1) {
  242. this.allUserViewObjectList.push(payload);
  243. }
  244. }
  245. } else if (updateType == ZegoUpdateType.Delete) {
  246. this.allUserViewObjectList = this.allUserViewObjectList.filter((object) => {
  247. for (let user of userList) {
  248. if (user.userID == object.userID) {
  249. return false;
  250. }
  251. }
  252. return true;
  253. });
  254. }
  255. if (updateType == "1") {
  256. // 有其他用户退出房间
  257. this.offVideo()
  258. }
  259. });
  260. // 房间内其他用户推的流变化通知
  261. this.engine.on("roomStreamUpdate", (roomID, updateType, streamList) => {
  262. // if (updateType == "0") {
  263. // // 流新增,开始拉流
  264. // this.tensile(streamList[0].streamID)
  265. // } else if (updateType == "1") {
  266. // // 流删除,停止拉流
  267. // this.notTensile(streamList[0].streamID);
  268. // }
  269. console.log('进入了啊BBBBBBBBBBBBBBBBBBBBBBB')
  270. console.log('---------updateType-----------', updateType)
  271. console.log('---------streamList-----------', streamList)
  272. console.log('---------ZegoUpdateType-----------', ZegoUpdateType)
  273. console.log('---------ZegoUpdateType.Add-----------', ZegoUpdateType.Add)
  274. console.log('---------ZegoUpdateType.Delete-----------', ZegoUpdateType.Delete)
  275. console.log('---------allUserViewObjectList-----------', this.allUserViewObjectList)
  276. if (updateType == ZegoUpdateType.Add) {
  277. for (let object of this.allUserViewObjectList) {
  278. for (let stream of streamList) {
  279. if (object.userID == stream.user.userID) {
  280. object.streamID = stream.streamID;
  281. if (!this.playerStreamID) {
  282. this.playerStreamID = stream.streamID
  283. this.tensile(object)
  284. }
  285. }
  286. }
  287. }
  288. this.allStreamList = this.allStreamList.concat(streamList);
  289. console.log(this.allStreamList, '-------------')
  290. } else if (updateType == ZegoUpdateType.Delete) {
  291. for (let object of this.allUserViewObjectList) {
  292. for (let stream of streamList) {
  293. if (object.streamID == stream.streamID) {
  294. object.streamID = undefined;
  295. }
  296. }
  297. }
  298. this.allStreamList = this.allStreamList.filter((object) => {
  299. for (let stream of streamList) {
  300. if (object.streamID == stream.streamID) {
  301. return false;
  302. }
  303. }
  304. return true;
  305. });
  306. }
  307. });
  308. // 拉流质量回调
  309. this.engine.on("playerQualityUpdate", (streamID, quality) => {
  310. console.log('拉流质量回调');
  311. console.log(streamID, quality);
  312. // if (this.videoShow == false) {
  313. // setTimeout(() => {
  314. // that.videoShow = true
  315. // console.log(that.videoShow, '要打开推流的视频窗口了啊')
  316. // }, 3500)
  317. // }
  318. });
  319. // 拉流后的事件回调
  320. this.engine.on("playerStateUpdate", (streamID, state, errorCode, extendedData) => {
  321. console.warn('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' + '拉流后的事件触发了start')
  322. console.warn(streamID, state, errorCode, extendedData, this.playerStreamID)
  323. console.warn('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' + '拉流后的事件触发end')
  324. })
  325. this.engine.on('playerRecvVideoFirstFrame', (streamID) => {
  326. console.warn('playerRecvVideoFirstFrame- 拉流端视频接收首帧回调', streamID)
  327. })
  328. this.engine.on('playerRenderVideoFirstFrame', (streamID) => {
  329. console.warn('playerRenderVideoFirstFrame- 拉流端渲染完视频首帧回调', streamID)
  330. })
  331. this.engine.on('publisherStateUpdate', (streamID, state, errorCode, extendedData) => {
  332. console.warn('publisherStateUpdate 推流状态回调', streamID, state, errorCode, extendedData)
  333. })
  334. this.engine.on('publisherCapturedVideoFirstFrame', (channel) => {
  335. console.warn('publisherCapturedVideoFirstFrame 推流端视频采集首帧回调', channel)
  336. })
  337. this.engine.on('remoteCameraStateUpdate', (streamID, state) => {
  338. console.warn('remoteCameraStateUpdate 远端摄像头设备状态通知', streamID, state)
  339. })
  340. },
  341. tensile(item) {
  342. console.warn('---------------------------- 拉流start -----------------------')
  343. console.log(this.allStreamList)
  344. console.log(item, item.streamID)
  345. console.log(this.allUserViewObjectList)
  346. // 拉流点击快于推流时,需要等流更新后重新拉
  347. // startPlayingStream 在 play.js文件
  348. // this.startPlayingStream(this.playStreamID);
  349. this.engine.startPlayingStream(item.streamID)
  350. console.warn('---------------------------- 拉流start1 -----------------------', this.playerStreamID)
  351. console.warn('---------------------------- 拉流start end -----------------------')
  352. },
  353. // 停止拉流
  354. notTensile(roomId) {
  355. this.serveViewModeIndex = roomId
  356. this.engine.stopPlayingStream(roomId)
  357. },
  358. // 推流 - 展示视图 - 本地视频流
  359. onPublish() {
  360. if (this.isPublishingStream) {
  361. // 停止推流
  362. this.engine.stopPreview();
  363. this.engine.stopPublishingStream();
  364. } else {
  365. console.log('开始推流了啊', this.isPublishingStream)
  366. // 开始推流
  367. this.engine.startPreview();
  368. // 设置编码格式
  369. // let videoConfig = {};
  370. // videoConfig.codecID = ZegoVideoCodecID.VP8;
  371. // this.engine.setVideoConfig(videoConfig);
  372. // 设置编码格式
  373. // this.engine.startPublishingStream(this.videoObj.room_id);
  374. console.log(this.userid)
  375. this.engine.startPublishingStream(this.userid).then(res => {
  376. console.log(res, 'start publish stream ------------------1')
  377. }).catch(err => {
  378. console.error(err)
  379. });;
  380. // this.videoShow = true
  381. }
  382. this.isPublishingStream = !this.isPublishingStream;
  383. },
  384. // 挂断退出
  385. async offVideo(isRefused = false, isNotice = true) {
  386. console.warn('进入了啊offVideo', this.engine)
  387. this.updateIsRefusedCall(false);
  388. this.updateTalkingStatus(false);
  389. this.updateOccupyedStatus(false);
  390. if (!this.engine) {
  391. console.warn('挂断退出 ------- 12131312')
  392. this.isPendingExit = true;
  393. } else {
  394. this.isPendingExit = false;
  395. }
  396. var that = this
  397. if (that.videoObj && !that.videoObj.room_id || !that.videoObj) {
  398. return
  399. }
  400. this.engine.logoutRoom(that.videoObj.room_id); // 退出房间
  401. const execExit = () => {
  402. console.log('---------------- exec exit')
  403. setTimeout(() => {
  404. that.videoShow = true
  405. that.allStreamList = [];
  406. that.allUserViewObjectList = [];
  407. var obj = {}
  408. // obj = {
  409. // 'action': 'read', // 动作标识,必填
  410. // 'send_user_id': that.videoObj.user_id, // 接收人用户id, 非必填
  411. // 'data': {}
  412. // }
  413. console.warn('-----------off video 接收人用户id-------------', that.videoObj
  414. .user_id, API)
  415. obj = {
  416. 'action': 'list', // 动作标识,必填
  417. 'type': '当前为挂断',
  418. 'recv_user_id': that.videoObj.user_id, // 接收人用户id, 非必填
  419. 'data': {}
  420. }
  421. if (!isRefused) {
  422. API.socketTask.send({
  423. data: JSON.stringify(obj),
  424. async success(res) {
  425. console.log("消息发送成功");
  426. },
  427. });
  428. }
  429. if (isNotice) {
  430. console.log(this.videoObj, 'is notice')
  431. if (!that.currentReceiveUserID) {
  432. console.warn('当前连接的用户不存在,不是本人发起的通话.')
  433. } else {
  434. const socketData = {
  435. action: 'reject_video', // 动作标识,必填
  436. recv_user_id: that
  437. .currentReceiveUserID, // 接收人用户id, 非必填
  438. data: {},
  439. }
  440. API.socketTask.send({
  441. data: JSON.stringify(socketData),
  442. async success(res) {
  443. console.log(
  444. 'reject_video 消息发送成功 reject_video')
  445. },
  446. })
  447. }
  448. }
  449. this.handleBack()
  450. }, 1500)
  451. }
  452. this.$refs.uToast.show({
  453. type: 'default',
  454. message: isRefused ? '对方正忙' : '结束视频通话!',
  455. })
  456. console.log('------------------------------------------ exec exit exec before')
  457. execExit()
  458. // if (isRefused) {
  459. // uni.showModal({
  460. // title: '提示',
  461. // content: isRefused ? '对方正忙' : '结束视频通话!',
  462. // showCancel: false,
  463. // confirmText: '好',
  464. // success: (res) => {
  465. // execExit()
  466. // },
  467. // fail: () => {
  468. // this.handleBack()
  469. // }
  470. // });
  471. // } else {
  472. // this.$refs.uToast.show({
  473. // type: 'default',
  474. // message: isRefused ? '对方正忙' : '结束视频通话!',
  475. // })
  476. // console.log('------------------------------------------ exec exit exec before')
  477. // execExit()
  478. // }
  479. // uni.redirectTo({
  480. // url: "/pages/response/index"
  481. // })
  482. },
  483. // 删除this.engine对象, 删除即构
  484. async destroyEngine() {
  485. this.engine = undefined;
  486. ZegoExpressEngine.destroyEngine();
  487. },
  488. handleBack() {
  489. const pages = getCurrentPages();
  490. if (pages.length < 3) {
  491. uni.switchTab({
  492. url: '/pages/response/index',
  493. success(res) {
  494. console.warn(res, '返回上一页成功,当前页面:视频通话页面 ')
  495. },
  496. fail(err) {
  497. console.error(err)
  498. },
  499. complete() {
  500. console.warn('返回上一页执行完成,当前页面:视频通话页面 ')
  501. }
  502. })
  503. } else {
  504. uni.navigateBack({
  505. delta: 1
  506. })
  507. }
  508. }
  509. },
  510. async onLoad(optinos) {
  511. console.log('onload -------------- 1213', this.isVideoCallRefused)
  512. if (this.isVideoCallRefused) {
  513. this.$refs.uToast.show({
  514. type: 'default',
  515. message: '对方正忙',
  516. })
  517. setTimeout(() => {
  518. this.handleBack();
  519. }, 1500)
  520. // uni.showModal({
  521. // title: '提示',
  522. // content: '对方正忙',
  523. // showCancel: false,
  524. // confirmText: '好',
  525. // success: (res) => {
  526. // setTimeout(() => {
  527. // this.handleBack();
  528. // }, 1500)
  529. // },
  530. // fail: () => {
  531. // this.handleBack()
  532. // }
  533. // });
  534. return
  535. }
  536. var that = this
  537. that.videoObj = JSON.parse(optinos.videoObj)
  538. // // console.log(that.videoObj)
  539. // // 即构 - 视频通话
  540. // // 获取系统信息同步接口
  541. if (uni.getSystemInfoSync().platform === 'android') {
  542. await permision.requestAndroidPermission('android.permission.RECORD_AUDIO');
  543. await permision.requestAndroidPermission('android.permission.CAMERA');
  544. }
  545. await this.setup()
  546. this.onPublish(); // 推流
  547. uni.getSystemInfo({ //异步获取。
  548. success(res) {
  549. // that.phoneHeight = res.windowHeight - 13; //窗口高度
  550. that.phoneHeight = res.windowHeight; //窗口高度
  551. }
  552. });
  553. },
  554. async onShow() {
  555. await this.setup();
  556. },
  557. // 监听页面返回
  558. onBackPress() {
  559. console.log('返回了!')
  560. this.offVideo(); // 挂断退出
  561. this.destroyEngine(); // 删除this.engine对象,删除即构
  562. },
  563. // 页面卸载 - 生命周期
  564. onUnload() {
  565. this.updateTalkingStatus(false);
  566. this.destroyEngine();
  567. console.log('onUnload');
  568. },
  569. }
  570. </script>
  571. <style lang="scss">
  572. .video_box {
  573. background: #000;
  574. width: 1200rpx;
  575. position: relative;
  576. z-index: 0;
  577. // .video_you {
  578. .video_me {
  579. background: #000;
  580. width: 150px;
  581. height: 200px;
  582. position: fixed;
  583. right: 0;
  584. top: 65px;
  585. z-index: 2000;
  586. }
  587. .video_you {
  588. background: #000;
  589. width: 1200rpx;
  590. height: 600rpx;
  591. position: absolute;
  592. right: 0;
  593. bottom: 0;
  594. z-index: 1;
  595. }
  596. .video_btn {
  597. position: fixed;
  598. bottom: 35rpx;
  599. left: 0;
  600. width: 750rpx;
  601. .video_imgList {
  602. display: flex;
  603. flex-direction: row;
  604. justify-content: space-around;
  605. // 翻转
  606. .video_imgList1 {
  607. .video_view {
  608. background: rgba(0, 0, 0, .5);
  609. border-radius: 50px;
  610. width: 50px;
  611. height: 50px;
  612. margin: 0 auto;
  613. .video_view_img {
  614. height: 15px;
  615. width: 20px;
  616. margin: 36rpx 0 0 33rpx;
  617. }
  618. }
  619. .video_view_txt {
  620. color: #fff;
  621. font-size: 14px;
  622. line-height: 30px;
  623. text-align: center;
  624. }
  625. }
  626. // 语音通话
  627. .video_imgList2 {
  628. .video_view {
  629. background: rgba(0, 0, 0, .5);
  630. border-radius: 50px;
  631. width: 50px;
  632. height: 50px;
  633. margin: 0 0 0 30rpx;
  634. .video_view_img {
  635. height: 25px;
  636. width: 25px;
  637. margin: 24rpx 0 0 25rpx;
  638. }
  639. }
  640. .video_view_txt {
  641. color: #fff;
  642. font-size: 14px;
  643. line-height: 30px;
  644. text-align: center;
  645. }
  646. }
  647. }
  648. }
  649. }
  650. </style>