Browse Source

添加摄像头状态

wolyshaw 3 năm trước cách đây
mục cha
commit
e7089f42f2

+ 2 - 0
src/base.css

@@ -7,4 +7,6 @@ body{
   --live-main-color: #32363B;
   --message-color: #575B61;
   --live-light-color: #01A79E;
+  --tips-backound-color: #32363B;
+  --tips-color: #A4A6A9;
 }

+ 3 - 0
src/components/live-broadcast/event.ts

@@ -5,6 +5,9 @@ export const LIVE_EVENT_MESSAGE = {
   'RC:TxtMsg': 'Text',
   'RC:Chatroom:Barrage': 'Barrage',
   'RC:Chatroom:Like': 'Like',
+  'RC:Chatroom:SeatsCtrl': 'SeatsCtrl',
+  'RC:Chatroom:ChatBan': 'ChatBan',
+  'RC:Chatroom:SeatApply': 'SeatApply',
 }
 
 export default mitt()

+ 34 - 3
src/components/live-broadcast/header.tsx

@@ -1,18 +1,49 @@
 import { defineComponent } from 'vue'
-import { ElButton } from 'element-plus'
+import { ElButton, ElMessageBox } from 'element-plus'
 import runtime, * as RuntimeUtils from './runtime'
+import { state } from '/src/state'
+import request from '/src/helpers/request'
 import styles from './header.module.less'
 
 export default defineComponent({
   name: 'LiveBroadcastHeader',
+  methods: {
+    async startLive() {
+      try {
+        await ElMessageBox.confirm('是否确认开始直播?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+        await RuntimeUtils.startLive()
+      } catch (error) {}
+    },
+    async closeLive() {
+      try {
+        await ElMessageBox.confirm('是否确认结束直播?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+        await request.post('/api-im/user/statusImUser', {
+          data: {
+            os: 'IOS',
+            status: '2',
+            userid: state.user?.id
+          }
+        })
+        await RuntimeUtils.closeLive()
+      } catch (error) {}
+    }
+  },
   render() {
     return (
       <div class={styles.header}>
         <h3 class={styles.title}>直播内容:如何在校管乐团中锻炼孩子的自我管理及团队协作意识</h3>
         {runtime.videoStatus === 'liveing' ? (
-          <ElButton type="danger" color="#EA4132" onClick={RuntimeUtils.closeLive}>关闭直播</ElButton>
+          <ElButton type="danger" color="#EA4132" onClick={this.closeLive}>关闭直播</ElButton>
         ) : (
-          <ElButton type="primary" color="#01A79E" onClick={RuntimeUtils.startLive}>开始直播</ElButton>
+          <ElButton type="primary" color="#01A79E" onClick={this.startLive}>开始直播</ElButton>
         )}
       </div>
     )

+ 5 - 0
src/components/live-broadcast/index.module.less

@@ -12,5 +12,10 @@
     max-height: calc(100vh - var(--header-bar-height) - var(--header-height) - var(--action-bar-height));
     flex: 1;
     background-color: var(--video-backound-color);
+    position: relative;
+    video{
+      width: 100%;
+      height: 100%;
+    }
   }
 }

+ 10 - 5
src/components/live-broadcast/index.tsx

@@ -2,6 +2,7 @@ import { defineComponent, ref } from 'vue'
 import * as RTC from '@rongcloud/plugin-rtc'
 import Header from './header'
 import ActionBar from './action-bar'
+import VideoStatus from './video-status'
 import { state } from '/src/state'
 import runtime, * as RuntimeUtils from './runtime'
 import { removeMedia } from './helpers'
@@ -28,15 +29,16 @@ export default defineComponent({
         runtime.videoRef = videoRef.value
         await RuntimeUtils.getMicrophones()
         await RuntimeUtils.getCameras()
+        runtime.mediaStreams = mediaStreams
         RuntimeUtils.setSelectCamera(runtime.cameras[0])
         RuntimeUtils.setSelectMicrophone(runtime.microphones[0])
-        microphoneAudioTrack = await RuntimeUtils.getTrack('microphone')
         cameraVideoTrack = await RuntimeUtils.getTrack('camera')
-        runtime.videoStatus = 'stream'
-        runtime.mediaStreams = mediaStreams
-        // runtime.mediaStreamTrack = mediaStreams.getTracks()
         RuntimeUtils.setVideoSrcObject(runtime.videoRef, mediaStreams)
         await RuntimeUtils.setTrack([cameraVideoTrack], 'camera')
+        microphoneAudioTrack = await RuntimeUtils.getTrack('microphone')
+        console.log(runtime.deviceStatus)
+        runtime.videoStatus = 'stream'
+        // runtime.mediaStreamTrack = mediaStreams.getTracks()
         await RuntimeUtils.setTrack([microphoneAudioTrack], 'microphone')
         const join = await RuntimeUtils.joinRoom('w_3fi4PXQcooe5_VUseReE', RTC.RCLivingType.VIDEO, {
           onMessageReceive(name, content) {
@@ -63,7 +65,10 @@ export default defineComponent({
     return (
       <div class={styles.main}>
         <Header/>
-        <video class={styles.video} ref={videoRef}></video>
+        <div class={styles.video}>
+          <video ref={videoRef}></video>
+          <VideoStatus/>
+        </div>
         <ActionBar/>
         {/* <div>video: {runtime.videoStatus}, imStatus: {runtime.imConnectStatus}</div> */}
       </div>

+ 18 - 3
src/components/live-broadcast/runtime.ts

@@ -17,6 +17,10 @@ type ActiveTracks = {
   [key in TrackType]: RTC.RCLocalTrack | null
 }
 
+type DeviceStatus = {
+  [key in TrackType]: 'init' | 'granted' | 'denied'
+}
+
 const runtime = reactive({
   /** IM连接状态 */
   imConnectStatus: 'connecting' as imConnectStatus,
@@ -50,6 +54,12 @@ const runtime = reactive({
   activeTracks: {} as ActiveTracks,
   /** 是否允许连麦 */
   allowSeatsCtrl: true,
+  /** 当前设备获取状态 */
+  deviceStatus: {
+    microphone: 'init',
+    camera: 'init',
+    screen: 'init'
+  } as DeviceStatus
 })
 
 export default runtime
@@ -70,7 +80,7 @@ const MessageSeatsCtrl = RongIMLib.registerMessageType('RC:Chatroom:SeatsCtrl',
 const MessageChatBan = RongIMLib.registerMessageType('RC:Chatroom:ChatBan', true, true)
 
 type MessageProps = {
-  messageType: 'RC:Chatroom:Welcome' | 'RC:TxtMsg' | 'RC:Chatroom:Barrage' | 'RC:Chatroom:Like',
+  messageType: 'RC:Chatroom:Welcome' | 'RC:TxtMsg' | 'RC:Chatroom:Barrage' | 'RC:Chatroom:Like' | 'RC:Chatroom:SeatsCtrl' | 'RC:Chatroom:ChatBan' | 'RC:Chatroom:SeatApply',
   content: any,
 }
 
@@ -237,9 +247,14 @@ export const getTrack = async (trackType: TrackType): Promise<RTC.RCLocalTrack>
   }
 
   Track = res?.track as RTC.RCLocalTrack
-  if (res.code !== RTC.RCRTCCode.SUCCESS || !Track) {
-    throw new Error('获取数据流失败')
+  if (res.code === RTC.RCRTCCode.PERMISSION_DENIED) {
+    runtime.deviceStatus[trackType] = 'denied'
+  } else {
+    runtime.deviceStatus[trackType] = 'granted'
   }
+  // if (res.code !== RTC.RCRTCCode.SUCCESS || !Track) {
+  //   throw new Error('获取数据流失败')
+  // }
   return Track
 }
 

+ 29 - 0
src/components/live-broadcast/video-status.module.less

@@ -0,0 +1,29 @@
+.container{
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  display: flex;
+  background-color: var(--tips-backound-color);
+  color: var(--live-color);
+  border-radius: 8px;
+  transform: translate(-50%, -50%);
+  >div{
+    padding: 32px 100px;
+    text-align: center;
+    width: 360px;
+    box-sizing: border-box;
+    svg{
+      width: 74px;
+      height: 68px;
+    }
+    .title{
+      font-size: 20px;
+      margin-top: 15px;
+    }
+    .tips{
+      font-size: 14px;
+      color: var(--tips-color);
+      margin-top: 5px;
+    }
+  }
+}

+ 27 - 0
src/components/live-broadcast/video-status.tsx

@@ -0,0 +1,27 @@
+import { defineComponent } from 'vue'
+import runtime from './runtime'
+import styles from './video-status.module.less'
+
+export default defineComponent({
+  name: 'VideoStatus',
+  render() {
+    return (
+      <div class={styles.container}>
+        {runtime.deviceStatus.camera === 'denied' ? (
+          <div class={styles.denied}>
+            <SvgIcon name="camera-status" class={styles.icon}/>
+            <p class={styles.title}>摄像头被禁用</p>
+            <p class={styles.tips}>请授权允许访问摄像头</p>
+          </div>
+        ) : null}
+        {runtime.deviceStatus.camera === 'init' ? (
+          <div class={styles.init}>
+            <SvgIcon name="camera-status" class={styles.icon}/>
+            <p class={styles.title}>请完成摄像头授权</p>
+            <p class={styles.tips}>请授权允许访问摄像头</p>
+          </div>
+        ) : null}
+      </div>
+    )
+  }
+})

+ 2 - 1
src/components/live-message/item-list.tsx

@@ -90,6 +90,7 @@ export default defineComponent({
   },
   computed: {
     loading() {
+      return false
       switch (this.type) {
         case 'message':
           return this.messageList.length === 0
@@ -127,4 +128,4 @@ export default defineComponent({
       </div>
     )
   }
-})
+})

+ 34 - 0
src/icons/camera-status.svg

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="74px" height="68px" viewBox="0 0 74 68" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>摄像头检测</title>
+    <g id="后台直播界面" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="网页直播(镜头空态)" transform="translate(-730.000000, -426.000000)">
+            <g id="编组-20" transform="translate(585.000000, 394.000000)">
+                <g id="摄像头检测" transform="translate(145.000000, 32.000000)">
+                    <rect id="矩形" x="0" y="0" width="74" height="68"></rect>
+                    <g id="编组-19" transform="translate(0.000000, 5.000000)" fill-rule="nonzero">
+                        <g id="编组-16" transform="translate(12.000000, 46.000000)">
+                            <path d="M23,0 C13.0320832,0 4.37477418,5.67226061 0,14 L46,14 C41.6242122,5.67226061 32.9669032,0 23,0 Z" id="路径" fill="#DCE1EB"></path>
+                            <path d="M24.0162186,0 L24,0 L24,14 L47,14 C42.6280741,5.67226061 33.9774791,0 24.0172322,0 L24.0162186,0 Z" id="路径" fill="#CDD2E1"></path>
+                        </g>
+                        <g id="编组-14" transform="translate(9.000000, 0.000000)">
+                            <path d="M0,26.5 C0,41.1355459 11.8644541,53 26.5,53 C41.1355459,53 53,41.1355459 53,26.5 C53,11.8644541 41.1355459,0 26.5,0 C11.8644541,0 0,11.8644541 0,26.5 Z" id="路径" fill="#FFFFFF"></path>
+                            <path d="M26.5,53 C41.1355459,53 53,41.1355459 53,26.5 C53,11.8644541 41.1355459,0 26.5,0 L26.5,53 Z" id="路径备份" fill="#E9EDF5"></path>
+                        </g>
+                        <g id="编组-2" transform="translate(23.000000, 14.000000)">
+                            <path d="M0,13 C0,20.1797017 5.82029825,26 13,26 C20.1797017,26 26,20.1797017 26,13 C26,5.82029825 20.1797017,0 13,0 C5.82029825,0 0,5.82029825 0,13 Z" id="路径备份-2" fill="#9196AA"></path>
+                            <path d="M13,26 C20.1797017,26 26,20.1797017 26,13 C26,5.82029825 20.1797017,0 13,0 L13,26 Z" id="路径备份-3" fill="#7E8596"></path>
+                        </g>
+                        <g id="编组-2备份" transform="translate(27.000000, 18.000000)">
+                            <path d="M0,8.5 C0,13.1944204 3.80557963,17 8.5,17 C13.1944204,17 17,13.1944204 17,8.5 C17,3.80557963 13.1944204,0 8.5,0 C3.80557963,0 0,3.80557963 0,8.5 Z" id="路径备份-2" fill="#555A66"></path>
+                            <path d="M8.5,17 C13.1944204,17 17,13.1944204 17,8.5 C17,3.80557963 13.1944204,0 8.5,0 L8.5,17 Z" id="路径备份-3" fill="#333940"></path>
+                        </g>
+                        <path d="M30,29 C30,29.7145312 30.3811978,30.3747852 31,30.7320508 C31.6188021,31.0893164 32.3811979,31.0893164 33,30.7320508 C33.6188022,30.3747852 34,29.7145312 34,29 C34,28.2854688 33.6188022,27.6252148 33,27.2679492 C32.3811979,26.9106836 31.6188021,26.9106836 31,27.2679492 C30.3811978,27.6252148 30,28.2854688 30,29 L30,29 Z" id="路径" fill="#DCE1EB"></path>
+                        <path d="M4.38143035,7.09796596 L0.180465597,6.63605315 C0.0907959527,6.62583763 0.0185683275,6.55777952 0.00304529407,6.46887487 C-0.0124777393,6.37997022 0.0324130614,6.29146287 0.113315284,6.25146496 L3.90222125,4.38143029 L4.36413406,0.180465595 C4.37434958,0.0907959515 4.44240769,0.0185683272 4.53131234,0.00304529403 C4.620217,-0.0124777392 4.70872435,0.032413061 4.74872225,0.113315282 L6.6187569,3.9022212 L10.8187042,4.36413401 C10.9085302,4.37399409 10.9810926,4.44191224 10.996864,4.53089085 C11.0126353,4.61986947 10.9678368,4.70858967 10.886872,4.74872219 L7.09796605,6.61875681 L6.63605324,10.8187041 C6.62619321,10.9085301 6.55827506,10.9810926 6.4692964,10.996864 C6.38031774,11.0126353 6.29159749,10.9678368 6.25146499,10.8868719 L4.38143035,7.09796596 Z" id="路径" fill="#E9EDF5"></path>
+                        <path d="M62.2860972,47.0659509 L60.0762938,45.4440243 C60.0094697,45.3951185 59.9829413,45.3079521 60.0111845,45.2300898 C60.0394278,45.1522275 60.1156607,45.1023663 60.1982846,45.1077144 L62.9333005,45.2856512 L64.5554992,43.0749494 C64.6046547,43.0090075 64.6912187,42.9831164 64.7684876,43.0112451 C64.8457565,43.0393738 64.8954436,43.1148653 64.8907412,43.1969898 L64.713808,45.9340492 L66.9236114,47.5559758 C66.9906376,47.6049646 67.0171658,47.6924154 66.9886594,47.7704077 C66.9601531,47.8484 66.8834941,47.8981049 66.8006893,47.8922857 L64.0656734,47.7143488 L62.4434747,49.9250506 C62.3943193,49.9909925 62.3077552,50.0168836 62.2304863,49.9887549 C62.1532174,49.9606262 62.1035304,49.8851347 62.1082327,49.8030102 L62.285166,47.0659509 L62.2860972,47.0659509 Z" id="路径" fill="#FEFEFF"></path>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>