Compare commits

..

28 Commits

Author SHA1 Message Date
zhangtao 7874ebd377 change axios 2022-12-08 11:37:03 +08:00
zhangtao 285c149943 change mirror 2022-11-29 09:40:25 +08:00
zhangtao bc2b7dfb1e change mirror 2022-11-28 17:21:59 +08:00
zhangtao d8546abbaf change device 2022-10-28 10:21:55 +08:00
zhangtao ac94990547 change device 2022-10-28 10:16:26 +08:00
zhangtao c3d3e737b7 change device 2022-10-28 10:10:28 +08:00
zhangtao ca616cfb08 change device 2022-10-28 10:04:15 +08:00
zhangtao 69356dd714 change device 2022-10-28 10:00:26 +08:00
zhangtao 95bfae8356 change api 2022-10-26 18:20:52 +08:00
zhangtao 7825e02ef5 change heart 2022-10-26 15:44:38 +08:00
zhangtao f2b4cc7091 taskid 2022-10-26 10:49:41 +08:00
zhangtao c3346a2f1f change domain 2022-10-24 11:09:50 +08:00
zhangtao 8682fa26db add log 2022-10-24 09:56:30 +08:00
zhangtao b18105f02d add log 2022-10-24 09:45:53 +08:00
zhangtao a91424d995 string userid 2022-10-24 09:39:42 +08:00
zhangtao 71cb7da4d4 string userid 2022-10-24 09:37:35 +08:00
zhangtao fd4cc250f9 string userid 2022-10-24 09:14:55 +08:00
zhangtao 26ccf348e5 userSig 2022-10-23 14:21:10 +08:00
zhangtao 51af040686 userSig 2022-10-23 11:57:31 +08:00
zhangtao 10ad7cdeb2 add mini redirect 2022-10-22 14:56:00 +08:00
zhangtao 8bcb39045c add mini redirect 2022-10-22 14:53:07 +08:00
zhangtao 83a9ed380e add mini redirect 2022-10-22 14:49:46 +08:00
zhangtao 29e1ccad73 change camera 2022-10-22 14:17:25 +08:00
zhangtao b696b192ed change camera 2022-10-22 14:13:10 +08:00
zhangtao d911a20419 add log 2022-10-22 11:59:12 +08:00
zhangtao 377c3e7856 view camera 2022-10-22 10:38:28 +08:00
zhangtao 2698ba7007 view camera 2022-10-22 10:22:57 +08:00
zhangtao 54f189b4c4 public 2022-10-21 18:32:18 +08:00
9 changed files with 304 additions and 71 deletions

2
.env
View File

@ -1,5 +1,5 @@
# title # title
VITE_APP_TITLE = 'h5' VITE_APP_TITLE = '视频会议'
# 端口号 # 端口号
VITE_PORT = 3000 VITE_PORT = 3000

View File

@ -10,6 +10,8 @@
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<title><%= title %></title> <title><%= title %></title>
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

1
public/DYFRqZwcf2.txt Normal file
View File

@ -0,0 +1 @@
db957f82a208d53cdaeff928dd554022

View File

@ -37,8 +37,6 @@ body {
margin: 0; margin: 0;
display: flex; display: flex;
place-items: center; place-items: center;
min-width: 320px;
min-height: 100vh;
} }
h1 { h1 {

View File

@ -3,10 +3,11 @@ html {
} }
html, html,
body { body,#app{
width: 100%; width: 100%;
min-width: 1440px;
height: 100%; height: 100%;
margin: 0;
padding: 0;
overflow: hidden; overflow: hidden;
background-color: #f2f2f2; background-color: #f2f2f2;
font-family: 'Encode Sans Condensed', sans-serif; font-family: 'Encode Sans Condensed', sans-serif;

View File

@ -1,75 +1,71 @@
<template> <template>
<div class="select-container"> <div class="select-container" />
<n-select
v-model:value="activeDeviceId"
class="select"
:placeholder="deviceType"
:options="deviceList"
value-field="deviceId"
@update:value="handleChange"
/>
</div>
</template> </template>
<script> <script>
import TRTC from 'trtc-js-sdk' import TRTC from 'trtc-js-sdk'
import { reactive, toRefs, onMounted, defineComponent } from 'vue' import { defineComponent, reactive, toRefs, onMounted, onBeforeUnmount } from 'vue'
export default defineComponent({ export default defineComponent({
name: 'DeviceSelect', name: 'DeviceSelect',
props: { props: {
deviceType: {
type: String,
default: () => {}
}, },
value: { emits: ['init', 'switch'],
type: String,
default: () => {}
}
},
emits: ['update:value'],
setup(props, { emit }) { setup(props, { emit }) {
const data = reactive({ const data = reactive({
deviceList: [], videoList: [],
activeDeviceId: '' audioList: [],
videoDeviceId: '',
audioDeviceId: ''
}) })
const getDeviceList = async() => { const getDeviceList = async() => {
switch (props.deviceType) { data.videoList = await TRTC.getCameras()
case 'camera': data.audioList = await TRTC.getMicrophones()
data.deviceList = await TRTC.getCameras() data.videoDeviceId = data.videoList[0]?.deviceId || ''
break data.audioDeviceId = data.audioList[0]?.deviceId || ''
case 'microphone': emit('init', {
data.deviceList = await TRTC.getMicrophones() cameraId: data.videoDeviceId,
break microphoneId: data.audioDeviceId
case 'speaker': })
data.deviceList = await TRTC.getSpeakers()
break
default:
break
}
data.activeDeviceId = data.deviceList[0].deviceId
emit('update:value', data.activeDeviceId)
} }
const handleChange = (value) => { const handleCameraReverse = () => {
data.activeDeviceId = value const index = data.videoList.findIndex((item) => item.deviceId === data.videoDeviceId)
emit('update:value', data.activeDeviceId) const len = data.videoList.length
if (len <= 1) {
return
} else if (index === 0) {
data.videoDeviceId = data.videoList[len - 1].deviceId
emit('init', {
cameraId: data.videoDeviceId,
microphoneId: data.audioDeviceId
})
emit('switch', { type: 'video', cameraId: data.videoDeviceId, isMirror: false })
} else {
data.videoDeviceId = data.videoList[0].deviceId
emit('init', {
cameraId: data.videoDeviceId,
microphoneId: data.audioDeviceId
})
emit('switch', { type: 'video', cameraId: data.videoDeviceId, isMirror: true })
}
} }
onMounted(() => { onMounted(() => {
navigator.mediaDevices.getUserMedia({ audio: true, video: true }) navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(() => { .then((res) => {
getDeviceList() getDeviceList()
}) })
// navigator.mediaDevices.addEventListener('devicechange', this.getDeviceList) navigator.mediaDevices.addEventListener('devicechange', getDeviceList)
})
onBeforeUnmount(() => {
navigator.mediaDevices.removeEventListener('devicechange', getDeviceList)
}) })
// beforeUnmount() {
// navigator.mediaDevices.removeEventListener('devicechange', this.getDeviceList)
// }
return { return {
...toRefs(data), ...toRefs(data),
handleChange handleCameraReverse
} }
} }

View File

@ -0,0 +1,127 @@
<template>
<div class="select-container">
<n-select
v-model:value="activeDeviceId"
class="select"
:placeholder="deviceType"
:options="deviceList"
value-field="deviceId"
@update:value="handleChange"
/>
</div>
</template>
<script>
import TRTC from 'trtc-js-sdk'
import { defineComponent, reactive, toRefs, onMounted, onBeforeUnmount } from 'vue'
export default defineComponent({
name: 'DeviceSelect',
props: {
deviceType: {
type: String,
default: () => {}
},
value: {
type: String,
default: () => {}
}
},
emits: ['update:value', 'switch'],
setup(props, { emit }) {
const data = reactive({
deviceList: [],
activeDeviceId: ''
})
const getDeviceList = async() => {
switch (props.deviceType) {
case 'video':
data.deviceList = await TRTC.getCameras()
break
case 'audio':
data.deviceList = await TRTC.getMicrophones()
break
case 'speaker':
data.deviceList = await TRTC.getSpeakers()
break
default:
break
}
data.activeDeviceId = data.deviceList[0].deviceId
emit('update:value', data.activeDeviceId)
}
const handleChange = (value) => {
data.activeDeviceId = value
emit('update:value', data.activeDeviceId)
emit('switch', { type: props.deviceType, deviceId: data.activeDeviceId })
}
const handleReverse = () => {
const index = data.deviceList.findIndex((item) => item.deviceId === data.activeDeviceId)
if (data.deviceList.length === 1) {
return
} else if (index === 0) {
data.activeDeviceId = data.deviceList[1].deviceId
emit('update:value', data.activeDeviceId)
emit('switch', { type: props.deviceType, deviceId: data.activeDeviceId })
} else {
data.activeDeviceId = data.deviceList[0].deviceId
emit('update:value', data.activeDeviceId)
emit('switch', { type: props.deviceType, deviceId: data.activeDeviceId })
}
}
onMounted(() => {
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then((res) => {
console.log(res)
getDeviceList()
})
navigator.mediaDevices.addEventListener('devicechange', getDeviceList)
})
onBeforeUnmount(() => {
navigator.mediaDevices.removeEventListener('devicechange', getDeviceList)
})
return {
...toRefs(data),
handleChange,
handleReverse
}
}
})
</script>
<style lang="scss" scoped>
.select-container {
display: flex;
.label {
display: inline-block;
padding: 0 20px;
width: 120px;
height: 40px;
text-align: left;
line-height: 40px;
border-top: 1px solid #DCDFE6;
border-left: 1px solid #DCDFE6;
border-bottom: 1px solid #DCDFE6;
border-radius: 4px 0 0 4px;
color: #909399;
background-color: #F5F7FA;
font-weight: bold;
}
.select {
flex-grow: 1;
}
}
</style>
<style lang="scss">
.select {
input {
border-radius: 0 4px 4px 0 !important;
}
}
</style>

View File

@ -1,7 +1,8 @@
<template> <template>
<div class="room"> <div class="room">
<DeviceSelect v-show="false" v-model:value="cameraId" device-type="camera" /> <!-- <DeviceSelect v-show="false" ref="cameraRef" v-model:value="cameraId" device-type="video" @switch="handleCamareSwitch" /> -->
<DeviceSelect v-show="false" v-model:value="microphoneId" device-type="microphone" /> <!-- <DeviceSelect v-show="false" v-model:value="microphoneId" device-type="audio" /> -->
<Device ref="cameraRef" @init="deviceInit" @switch="handleCamareSwitch" />
<!-- 远端 --> <!-- 远端 -->
<div class="remote-container"> <div class="remote-container">
@ -19,6 +20,11 @@
<div id="localStream" class="local-stream-content" /> <div id="localStream" class="local-stream-content" />
<!-- 本地流操作栏 --> <!-- 本地流操作栏 -->
<div v-if="isPlayingLocalStream" class="local-stream-control"> <div v-if="isPlayingLocalStream" class="local-stream-control">
<div class="video-control control">
<n-icon size="40" @click="handelCameraReverse">
<CameraReverseOutline />
</n-icon>
</div>
<div class="video-control control"> <div class="video-control control">
<n-icon v-if="!isMutedVideo" size="40" @click="handleVideoMute"> <n-icon v-if="!isMutedVideo" size="40" @click="handleVideoMute">
<VideocamOutline /> <VideocamOutline />
@ -46,16 +52,21 @@
<script> <script>
import TRTC from 'trtc-js-sdk' import TRTC from 'trtc-js-sdk'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import DeviceSelect from './components/Device.vue' import Device from './components/Device.vue'
import { reactive, toRefs, onMounted, watch, nextTick } from 'vue' // import DeviceSelect from './components/DeviceSelect.vue'
import { VideocamOutline, VideocamOffOutline } from '@vicons/ionicons5' import { ref, reactive, toRefs, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import { CameraReverseOutline, VideocamOutline, VideocamOffOutline } from '@vicons/ionicons5'
import { AudioOutlined, AudioMutedOutlined } from '@vicons/antd' import { AudioOutlined, AudioMutedOutlined } from '@vicons/antd'
import { isUndef } from '@/utils/is.js' import { isUndef } from '@/utils/is.js'
import { formatDateTime } from '@/utils/index.js'
import axios from 'axios'
// import { Screen, ScreenOff } from '@vicons/carbon' // import { Screen, ScreenOff } from '@vicons/carbon'
export default { export default {
name: 'HomePage', name: 'HomePage',
components: { components: {
DeviceSelect, Device,
// DeviceSelect,
CameraReverseOutline,
VideocamOutline, VideocamOutline,
VideocamOffOutline, VideocamOffOutline,
AudioOutlined, AudioOutlined,
@ -63,17 +74,19 @@ export default {
}, },
setup() { setup() {
const route = useRoute() const route = useRoute()
const cameraRef = ref()
const data = reactive({ const data = reactive({
hasInit: false,
client: null, client: null,
timer: null,
sdkAppId: 1400752641, sdkAppId: 1400752641,
sdkSecret: '9b5fc557f286d7e4d6eafd8023026da59f0674000f319754aa1ec4beefddcdd6', sdkSecret: '9b5fc557f286d7e4d6eafd8023026da59f0674000f319754aa1ec4beefddcdd6',
userId: '', userId: null,
userSig: null,
roomId: null, roomId: null,
secret: { taskId: null,
'haoran': 'eJwtzEELgjAYxvHvsqsh29ymCB28hIIElZR1G2y1ty2VJSVE3z1Tj8-vgf8HVeUhfGmPUkRDjFbTBqWbHq4wsZGtl83yPJWVXQcKpYRhHHMqGJkfPXTg9eicc4oxnrWHx9*EEFHECGVLBW5jWMn6HCRBW7njJd*WNgNrNrnbFXcZG356V94Vg7W12ydr9P0BbGAyOQ__', remoteStreamList: [],
'wanghaoran': 'eJwtzEELgjAcBfDvsnO4ubnFhA6ihwIhUqEOXhZO*6dNUalF9N0z9fh*7-E*KItT56l75CPqELSZMxTajFDCzC9lqptqe2XWdihq1XVQIN-1CNlyKjx3abTtoNeTc84pIWTRER5-E0IwVwq5bgeopnPPRDluaVPGTb1P3nd7CcPMHgJ2zbFJ5HCKrDkHxzLHMmX1Dn1-G3A0Zg__' isMirror: true
},
remoteStreamList: []
}) })
const settings = reactive({ const settings = reactive({
@ -98,7 +111,7 @@ export default {
data.client = TRTC.createClient({ data.client = TRTC.createClient({
sdkAppId: data.sdkAppId, // sdkAppId sdkAppId: data.sdkAppId, // sdkAppId
userId: data.userId, // userId userId: data.userId, // userId
userSig: data.secret[data.userId], // userSig userSig: data.userSig, // userSig
mode: 'rtc' mode: 'rtc'
}) })
handleClientEvents() handleClientEvents()
@ -115,6 +128,10 @@ export default {
status.isJoining = false status.isJoining = false
status.isJoined = true status.isJoined = true
startGetAudioLevel() startGetAudioLevel()
updateUsers(2)
data.timer = setInterval(async() => {
await heartbeat()
}, 5000)
} catch (error) { } catch (error) {
status.isJoining = false status.isJoining = false
console.error('join room failed', error) console.error('join room failed', error)
@ -293,7 +310,10 @@ export default {
* @return {*} * @return {*}
*/ */
const playLocalStream = async() => { const playLocalStream = async() => {
settings.localStream.play('localStream') if (settings.localStream && settings.isPlayingLocalStream) {
settings.isPlayingLocalStream = false
}
settings.localStream.play('localStream', { mirror: data.isMirror })
.then(() => { .then(() => {
settings.isPlayingLocalStream = true settings.isPlayingLocalStream = true
}) })
@ -405,6 +425,24 @@ export default {
data.client && data.client.enableAudioVolumeEvaluation(-1) data.client && data.client.enableAudioVolumeEvaluation(-1)
} }
/**
* @description: 切换音频
* @param {*} type
* @param {*} cameraId
* @return {*}
*/
const handleCamareSwitch = ({ type, cameraId, isMirror = true }) => {
try {
if (settings.localStream) {
data.isMirror = isMirror
settings.localStream.switchDevice(type, cameraId)
playLocalStream()
}
} catch (error) {
console.error('switchDevice failed', error)
}
}
/** /**
* @description: 退出直播间 * @description: 退出直播间
* @return {*} * @return {*}
@ -422,6 +460,7 @@ export default {
await data.client.leave() await data.client.leave()
status.isLeaving = false status.isLeaving = false
status.isJoined = false status.isJoined = false
wx.miniProgram.switchTab({ url: '/pages/tool/tool' })
} catch (error) { } catch (error) {
status.isLeaving = false status.isLeaving = false
console.error('leave room error', error) console.error('leave room error', error)
@ -429,13 +468,78 @@ export default {
} }
} }
onMounted(() => { /**
const { userId, roomId } = route.query * @description: 用户签名
data.userId = userId * @return {*}
*/
const handleSig = async() => {
const res = await axios.get(`/hhz/api/tencentCloudRtc/genUserSig/${data.userId}`)
if (res.data.code === 0) {
data.userSig = res.data.data.userSig
}
}
const updateUsers = async(status) => {
const params = {
roomUserId: data.taskId,
status
}
await axios.put(`/hhz/api/meeting/updatePeopleStatus`, params)
}
/**
* @description: 心跳定时调用
* @param {*} status
* @return {*}
*/
const heartbeat = async() => {
const time = new Date().getTime()
const params = {
roomUserId: data.taskId,
heartbeatTime: formatDateTime(time)
}
await axios.put(`/hhz/api/meeting/heartbeatTime`, params)
}
/**
* @description: 翻转摄像头
* @return {*}
*/
const handelCameraReverse = () => {
if (!status.isMutedVideo) {
cameraRef.value.handleCameraReverse()
}
}
onMounted(async() => {
const { userId, roomId, taskId } = route.query
data.userId = String(userId)
data.roomId = Number(roomId) data.roomId = Number(roomId)
data.taskId = Number(taskId)
await handleSig()
}) })
watch(() => [settings.cameraId, settings.microphoneId], async([cameraId, microphoneId]) => {
if (cameraId && microphoneId) { onBeforeUnmount(() => {
updateUsers(3)
clearInterval(data.timer)
data.timer = null
})
// watch(() => [settings.cameraId, settings.microphoneId], async([cameraId, microphoneId]) => {
// console.log('cameraId:' + cameraId, 'microphoneId:' + microphoneId)
// if (cameraId && microphoneId) {
// data.hasInit = true
// }
// })
const deviceInit = (device) => {
const { cameraId, microphoneId } = device
settings.cameraId = cameraId
settings.microphoneId = microphoneId
data.hasInit = true
}
watch(() => [data.hasInit, data.userSig], async([init, sig]) => {
if (init && sig) {
await createMeetingRoom() await createMeetingRoom()
await joinMeetingRoom() await joinMeetingRoom()
await initLocalStream() await initLocalStream()
@ -448,10 +552,14 @@ export default {
...toRefs(data), ...toRefs(data),
...toRefs(settings), ...toRefs(settings),
...toRefs(status), ...toRefs(status),
cameraRef,
deviceInit,
handleVideoMute, handleVideoMute,
handleVideoUnMute, handleVideoUnMute,
handleAudioMute, handleAudioMute,
handleAudioUnMute, handleAudioUnMute,
handelCameraReverse,
handleCamareSwitch,
leaveMeetingRoom leaveMeetingRoom
} }
} }

View File

@ -23,7 +23,7 @@ export default {
const data = reactive({ const data = reactive({
form: { form: {
userId: 'wanghaoran', userId: 'wanghaoran',
roomId: 111 roomId: '111'
} }
}) })