add leave
This commit is contained in:
parent
db0909dfd3
commit
c00578bae7
|
|
@ -3,6 +3,17 @@
|
||||||
<DeviceSelect v-show="false" v-model:value="cameraId" device-type="camera" />
|
<DeviceSelect v-show="false" v-model:value="cameraId" device-type="camera" />
|
||||||
<DeviceSelect v-show="false" v-model:value="microphoneId" device-type="microphone" />
|
<DeviceSelect v-show="false" v-model:value="microphoneId" device-type="microphone" />
|
||||||
|
|
||||||
|
<!-- 远端 -->
|
||||||
|
<div class="remote-container">
|
||||||
|
<div
|
||||||
|
v-for="(item) in remoteStreamList"
|
||||||
|
:id="item.getUserId()"
|
||||||
|
:key="item.getUserId()"
|
||||||
|
class="remote-stream-container"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 本地 -->
|
||||||
<div v-if="localStream" class="local-stream-container">
|
<div v-if="localStream" class="local-stream-container">
|
||||||
<!-- 本地流播放区域 -->
|
<!-- 本地流播放区域 -->
|
||||||
<div id="localStream" class="local-stream-content" />
|
<div id="localStream" class="local-stream-content" />
|
||||||
|
|
@ -23,7 +34,9 @@
|
||||||
<n-icon v-if="isMutedAudio" size="40" @click="handleAudioUnMute">
|
<n-icon v-if="isMutedAudio" size="40" @click="handleAudioUnMute">
|
||||||
<AudioMutedOutlined />
|
<AudioMutedOutlined />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
<div class="audio-control control">
|
||||||
|
<n-button type="error" ghost @click="leaveMeetingRoom">退出会议</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -34,9 +47,10 @@
|
||||||
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 DeviceSelect from './components/Device.vue'
|
||||||
import { reactive, toRefs, onMounted, watch } from 'vue'
|
import { reactive, toRefs, onMounted, watch, nextTick } from 'vue'
|
||||||
import { VideocamOutline, VideocamOffOutline } from '@vicons/ionicons5'
|
import { VideocamOutline, VideocamOffOutline } from '@vicons/ionicons5'
|
||||||
import { AudioOutlined, AudioMutedOutlined } from '@vicons/antd'
|
import { AudioOutlined, AudioMutedOutlined } from '@vicons/antd'
|
||||||
|
import { isUndef } from '@/utils/is.js'
|
||||||
// import { Screen, ScreenOff } from '@vicons/carbon'
|
// import { Screen, ScreenOff } from '@vicons/carbon'
|
||||||
export default {
|
export default {
|
||||||
name: 'HomePage',
|
name: 'HomePage',
|
||||||
|
|
@ -55,14 +69,11 @@ export default {
|
||||||
sdkSecret: '9b5fc557f286d7e4d6eafd8023026da59f0674000f319754aa1ec4beefddcdd6',
|
sdkSecret: '9b5fc557f286d7e4d6eafd8023026da59f0674000f319754aa1ec4beefddcdd6',
|
||||||
userId: '',
|
userId: '',
|
||||||
roomId: null,
|
roomId: null,
|
||||||
isJoining: false,
|
|
||||||
isJoined: false,
|
|
||||||
isPublishing: false,
|
|
||||||
isPublished: false,
|
|
||||||
secret: {
|
secret: {
|
||||||
'haoran': 'eJwtzEELgjAYxvHvsqsh29ymCB28hIIElZR1G2y1ty2VJSVE3z1Tj8-vgf8HVeUhfGmPUkRDjFbTBqWbHq4wsZGtl83yPJWVXQcKpYRhHHMqGJkfPXTg9eicc4oxnrWHx9*EEFHECGVLBW5jWMn6HCRBW7njJd*WNgNrNrnbFXcZG356V94Vg7W12ydr9P0BbGAyOQ__',
|
'haoran': 'eJwtzEELgjAYxvHvsqsh29ymCB28hIIElZR1G2y1ty2VJSVE3z1Tj8-vgf8HVeUhfGmPUkRDjFbTBqWbHq4wsZGtl83yPJWVXQcKpYRhHHMqGJkfPXTg9eicc4oxnrWHx9*EEFHECGVLBW5jWMn6HCRBW7njJd*WNgNrNrnbFXcZG356V94Vg7W12ydr9P0BbGAyOQ__',
|
||||||
'wanghaoran': 'eJwtzEELgjAcBfDvsnO4ubnFhA6ihwIhUqEOXhZO*6dNUalF9N0z9fh*7-E*KItT56l75CPqELSZMxTajFDCzC9lqptqe2XWdihq1XVQIN-1CNlyKjx3abTtoNeTc84pIWTRER5-E0IwVwq5bgeopnPPRDluaVPGTb1P3nd7CcPMHgJ2zbFJ5HCKrDkHxzLHMmX1Dn1-G3A0Zg__'
|
'wanghaoran': 'eJwtzEELgjAcBfDvsnO4ubnFhA6ihwIhUqEOXhZO*6dNUalF9N0z9fh*7-E*KItT56l75CPqELSZMxTajFDCzC9lqptqe2XWdihq1XVQIN-1CNlyKjx3abTtoNeTc84pIWTRER5-E0IwVwq5bgeopnPPRDluaVPGTb1P3nd7CcPMHgJ2zbFJ5HCKrDkHxzLHMmX1Dn1-G3A0Zg__'
|
||||||
}
|
},
|
||||||
|
remoteStreamList: []
|
||||||
})
|
})
|
||||||
|
|
||||||
const settings = reactive({
|
const settings = reactive({
|
||||||
|
|
@ -74,6 +85,15 @@ export default {
|
||||||
isMutedAudio: false // 是否关闭音频
|
isMutedAudio: false // 是否关闭音频
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const status = reactive({
|
||||||
|
isJoining: false,
|
||||||
|
isJoined: false,
|
||||||
|
isPublishing: false,
|
||||||
|
isUnPublishing: false,
|
||||||
|
isPublished: false,
|
||||||
|
isLeaving: false
|
||||||
|
})
|
||||||
|
|
||||||
const createMeetingRoom = async() => {
|
const createMeetingRoom = async() => {
|
||||||
data.client = TRTC.createClient({
|
data.client = TRTC.createClient({
|
||||||
sdkAppId: data.sdkAppId, // 填写您申请的 sdkAppId
|
sdkAppId: data.sdkAppId, // 填写您申请的 sdkAppId
|
||||||
|
|
@ -85,18 +105,18 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
const joinMeetingRoom = async() => {
|
const joinMeetingRoom = async() => {
|
||||||
if (data.isJoining || data.isJoined) {
|
if (status.isJoining || status.isJoined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data.isJoining = true
|
status.isJoining = true
|
||||||
!data.client && await createMeetingRoom()
|
!data.client && await createMeetingRoom()
|
||||||
try {
|
try {
|
||||||
await data.client.join({ roomId: data.roomId })
|
await data.client.join({ roomId: data.roomId })
|
||||||
data.isJoining = false
|
status.isJoining = false
|
||||||
data.isJoined = true
|
status.isJoined = true
|
||||||
startGetAudioLevel()
|
startGetAudioLevel()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
data.isJoining = false
|
status.isJoining = false
|
||||||
console.error('join room failed', error)
|
console.error('join room failed', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
@ -140,15 +160,14 @@ export default {
|
||||||
console.log(`peer-leave ${userId}`, event)
|
console.log(`peer-leave ${userId}`, event)
|
||||||
})
|
})
|
||||||
/* 远端添加流 */
|
/* 远端添加流 */
|
||||||
data.client.on('stream-added', (event) => {
|
data.client.on('stream-added', async(event) => {
|
||||||
const { stream: remoteStream } = event
|
const { stream: remoteStream } = event
|
||||||
const remoteUserId = remoteStream.getUserId()
|
const remoteUserId = remoteStream.getUserId()
|
||||||
if (remoteUserId === `share_${this.userId}`) {
|
if (remoteUserId === `share_${data.userId}`) {
|
||||||
this.unSubscribe(remoteStream)
|
await unsubscribeStream(remoteStream)
|
||||||
} else {
|
} else {
|
||||||
console.log(`remote stream added: [${remoteUserId}] type: ${remoteStream.getType()}`)
|
console.log(`remote stream added: [${remoteUserId}] type: ${remoteStream.getType()}`)
|
||||||
this.subscribe(remoteStream)
|
await subscribeStream(remoteStream)
|
||||||
this.addSuccessLog(`RemoteStream added: [${remoteUserId}].`)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
/* 远端流订阅成功 */
|
/* 远端流订阅成功 */
|
||||||
|
|
@ -156,19 +175,18 @@ export default {
|
||||||
const { stream: remoteStream } = event
|
const { stream: remoteStream } = event
|
||||||
const remoteUserId = remoteStream.getUserId()
|
const remoteUserId = remoteStream.getUserId()
|
||||||
console.log('stream-subscribed userId: ', remoteUserId)
|
console.log('stream-subscribed userId: ', remoteUserId)
|
||||||
this.addSuccessLog(`RemoteStream subscribed: [${remoteUserId}].`)
|
data.remoteStreamList.push(remoteStream)
|
||||||
this.remoteStreamList.push(remoteStream)
|
nextTick(() => {
|
||||||
this.$nextTick(() => {
|
playRemoteStream(remoteStream, remoteUserId)
|
||||||
this.playRemoteStream(remoteStream, remoteUserId)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
/* 远端流移除 */
|
/* 远端流移除 */
|
||||||
data.client.on('stream-removed', (event) => {
|
data.client.on('stream-removed', (event) => {
|
||||||
const { stream: remoteStream } = event
|
const { stream: remoteStream } = event
|
||||||
remoteStream.stop()
|
remoteStream.stop()
|
||||||
const index = this.remoteStreamList.indexOf(remoteStream)
|
const index = data.remoteStreamList.indexOf(remoteStream)
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
this.remoteStreamList.splice(index, 1)
|
data.remoteStreamList.splice(index, 1)
|
||||||
}
|
}
|
||||||
console.log(`stream-removed userId: ${remoteStream.getUserId()} type: ${remoteStream.getType()}`)
|
console.log(`stream-removed userId: ${remoteStream.getUserId()} type: ${remoteStream.getType()}`)
|
||||||
})
|
})
|
||||||
|
|
@ -176,7 +194,6 @@ export default {
|
||||||
data.client.on('stream-updated', (event) => {
|
data.client.on('stream-updated', (event) => {
|
||||||
const { stream: remoteStream } = event
|
const { stream: remoteStream } = event
|
||||||
console.log(`type: ${remoteStream.getType()} stream-updated hasAudio: ${remoteStream.hasAudio()} hasVideo: ${remoteStream.hasVideo()}`)
|
console.log(`type: ${remoteStream.getType()} stream-updated hasAudio: ${remoteStream.hasAudio()} hasVideo: ${remoteStream.hasVideo()}`)
|
||||||
this.addSuccessLog(`RemoteStream updated: [${remoteStream.getUserId()}] audio:${remoteStream.hasAudio()}, video:${remoteStream.hasVideo()}.`)
|
|
||||||
})
|
})
|
||||||
/* 远端流禁用音频 */
|
/* 远端流禁用音频 */
|
||||||
data.client.on('mute-audio', (event) => {
|
data.client.on('mute-audio', (event) => {
|
||||||
|
|
@ -211,6 +228,47 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 订阅远端流
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const subscribeStream = async(remoteStream, config = { audio: true, video: true }) => {
|
||||||
|
try {
|
||||||
|
await data.client.subscribe(remoteStream, {
|
||||||
|
audio: isUndef(config.audio) ? true : config.audio,
|
||||||
|
video: isUndef(config.video) ? true : config.video
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`subscribe ${remoteStream.getUserId()} with audio: ${config.audio} video: ${config.video} error`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 取消订阅远端流
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const unsubscribeStream = async(remoteStream) => {
|
||||||
|
try {
|
||||||
|
await data.client.unsubscribe(remoteStream)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`unsubscribe ${remoteStream.getUserId()} error`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 播放远端流
|
||||||
|
* @param {*} remoteStream
|
||||||
|
* @param {*} element
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const playRemoteStream = (remoteStream, element) => {
|
||||||
|
if (remoteStream.getType() === 'main' && remoteStream.getUserId().indexOf('share') >= 0) {
|
||||||
|
remoteStream.play(element, { objectFit: 'contain' }).catch()
|
||||||
|
} else {
|
||||||
|
remoteStream.play(element).catch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 初始化本地流
|
* @description: 初始化本地流
|
||||||
* @return {*}
|
* @return {*}
|
||||||
|
|
@ -249,21 +307,41 @@ export default {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
const publishStream = async() => {
|
const publishStream = async() => {
|
||||||
if (!data.isJoined || data.isPublishing || data.isPublished) {
|
if (!status.isJoined || status.isPublishing || status.isPublished) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data.isPublishing = true
|
status.isPublishing = true
|
||||||
try {
|
try {
|
||||||
await data.client.publish(settings.localStream)
|
await data.client.publish(settings.localStream)
|
||||||
data.isPublishing = false
|
status.isPublishing = false
|
||||||
data.isPublished = true
|
status.isPublished = true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
data.isPublishing = false
|
status.isPublishing = false
|
||||||
console.error('publish localStream failed', error)
|
console.error('publish localStream failed', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 终止推流
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const unPublishStream = async() => {
|
||||||
|
if (!status.isPublished || status.isUnPublishing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.isUnPublishing = true
|
||||||
|
try {
|
||||||
|
await data.client.unpublish(settings.localStream)
|
||||||
|
status.isUnPublishing = false
|
||||||
|
status.isPublished = false
|
||||||
|
} catch (error) {
|
||||||
|
status.isUnPublishing = false
|
||||||
|
console.error('unpublish localStream failed', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 打开摄像头
|
* @description: 打开摄像头
|
||||||
* @return {*}
|
* @return {*}
|
||||||
|
|
@ -308,18 +386,53 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 销毁房间
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const destroyLocalStream = () => {
|
||||||
|
settings.localStream && settings.localStream.stop()
|
||||||
|
settings.localStream && settings.localStream.close()
|
||||||
|
settings.localStream = null
|
||||||
|
settings.isPlayingLocalStream = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 禁用音频
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const stopGetAudioLevel = () => {
|
||||||
|
data.client && data.client.enableAudioVolumeEvaluation(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 退出直播间
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
const leaveMeetingRoom = async() => {
|
const leaveMeetingRoom = async() => {
|
||||||
|
if (!status.isJoined || status.isLeaving) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.isLeaving = true
|
||||||
|
stopGetAudioLevel()
|
||||||
|
status.isPublished && await unPublishStream()
|
||||||
|
settings.localStream && destroyLocalStream()
|
||||||
|
|
||||||
|
try {
|
||||||
await data.client.leave()
|
await data.client.leave()
|
||||||
|
status.isLeaving = false
|
||||||
|
status.isJoined = false
|
||||||
|
} catch (error) {
|
||||||
|
status.isLeaving = false
|
||||||
|
console.error('leave room error', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const { userId, roomId } = route.query
|
const { userId, roomId } = route.query
|
||||||
data.userId = userId
|
data.userId = userId
|
||||||
data.roomId = Number(roomId)
|
data.roomId = Number(roomId)
|
||||||
// nextTick(async() => {
|
|
||||||
// await createMeetingRoom()
|
|
||||||
// await initLocalStream()
|
|
||||||
// })
|
|
||||||
})
|
})
|
||||||
watch(() => [settings.cameraId, settings.microphoneId], async([cameraId, microphoneId]) => {
|
watch(() => [settings.cameraId, settings.microphoneId], async([cameraId, microphoneId]) => {
|
||||||
if (cameraId && microphoneId) {
|
if (cameraId && microphoneId) {
|
||||||
|
|
@ -334,10 +447,12 @@ export default {
|
||||||
return {
|
return {
|
||||||
...toRefs(data),
|
...toRefs(data),
|
||||||
...toRefs(settings),
|
...toRefs(settings),
|
||||||
|
...toRefs(status),
|
||||||
handleVideoMute,
|
handleVideoMute,
|
||||||
handleVideoUnMute,
|
handleVideoUnMute,
|
||||||
handleAudioMute,
|
handleAudioMute,
|
||||||
handleAudioUnMute
|
handleAudioUnMute,
|
||||||
|
leaveMeetingRoom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -347,10 +462,12 @@ export default {
|
||||||
.room{
|
.room{
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
position: relative;
|
||||||
.local-stream-container{
|
.local-stream-container{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
.local-stream-content {
|
.local-stream-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -358,9 +475,9 @@ export default {
|
||||||
.local-stream-control {
|
.local-stream-control {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
height: 40px;
|
height: 50px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 5px;
|
bottom: 0;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
@ -372,5 +489,20 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.remote-container{
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
width: 150px;
|
||||||
|
max-height: calc(100% - 100px);
|
||||||
|
overflow-y: scroll;
|
||||||
|
z-index: 999;
|
||||||
|
right: 10px;
|
||||||
|
.remote-stream-container{
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
background: red;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue