diff --git a/src/views/home/index.vue b/src/views/home/index.vue index 005a322..b100500 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -3,6 +3,17 @@ + +
+
+
+ +
@@ -23,7 +34,9 @@ - +
+
+ 退出会议
@@ -34,9 +47,10 @@ import TRTC from 'trtc-js-sdk' import { useRoute } from 'vue-router' 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 { AudioOutlined, AudioMutedOutlined } from '@vicons/antd' +import { isUndef } from '@/utils/is.js' // import { Screen, ScreenOff } from '@vicons/carbon' export default { name: 'HomePage', @@ -55,14 +69,11 @@ export default { sdkSecret: '9b5fc557f286d7e4d6eafd8023026da59f0674000f319754aa1ec4beefddcdd6', userId: '', roomId: null, - isJoining: false, - isJoined: false, - isPublishing: false, - isPublished: false, secret: { 'haoran': 'eJwtzEELgjAYxvHvsqsh29ymCB28hIIElZR1G2y1ty2VJSVE3z1Tj8-vgf8HVeUhfGmPUkRDjFbTBqWbHq4wsZGtl83yPJWVXQcKpYRhHHMqGJkfPXTg9eicc4oxnrWHx9*EEFHECGVLBW5jWMn6HCRBW7njJd*WNgNrNrnbFXcZG356V94Vg7W12ydr9P0BbGAyOQ__', 'wanghaoran': 'eJwtzEELgjAcBfDvsnO4ubnFhA6ihwIhUqEOXhZO*6dNUalF9N0z9fh*7-E*KItT56l75CPqELSZMxTajFDCzC9lqptqe2XWdihq1XVQIN-1CNlyKjx3abTtoNeTc84pIWTRER5-E0IwVwq5bgeopnPPRDluaVPGTb1P3nd7CcPMHgJ2zbFJ5HCKrDkHxzLHMmX1Dn1-G3A0Zg__' - } + }, + remoteStreamList: [] }) const settings = reactive({ @@ -74,6 +85,15 @@ export default { isMutedAudio: false // 是否关闭音频 }) + const status = reactive({ + isJoining: false, + isJoined: false, + isPublishing: false, + isUnPublishing: false, + isPublished: false, + isLeaving: false + }) + const createMeetingRoom = async() => { data.client = TRTC.createClient({ sdkAppId: data.sdkAppId, // 填写您申请的 sdkAppId @@ -85,18 +105,18 @@ export default { } const joinMeetingRoom = async() => { - if (data.isJoining || data.isJoined) { + if (status.isJoining || status.isJoined) { return } - data.isJoining = true + status.isJoining = true !data.client && await createMeetingRoom() try { await data.client.join({ roomId: data.roomId }) - data.isJoining = false - data.isJoined = true + status.isJoining = false + status.isJoined = true startGetAudioLevel() } catch (error) { - data.isJoining = false + status.isJoining = false console.error('join room failed', error) throw error } @@ -140,15 +160,14 @@ export default { console.log(`peer-leave ${userId}`, event) }) /* 远端添加流 */ - data.client.on('stream-added', (event) => { + data.client.on('stream-added', async(event) => { const { stream: remoteStream } = event const remoteUserId = remoteStream.getUserId() - if (remoteUserId === `share_${this.userId}`) { - this.unSubscribe(remoteStream) + if (remoteUserId === `share_${data.userId}`) { + await unsubscribeStream(remoteStream) } else { console.log(`remote stream added: [${remoteUserId}] type: ${remoteStream.getType()}`) - this.subscribe(remoteStream) - this.addSuccessLog(`RemoteStream added: [${remoteUserId}].`) + await subscribeStream(remoteStream) } }) /* 远端流订阅成功 */ @@ -156,19 +175,18 @@ export default { const { stream: remoteStream } = event const remoteUserId = remoteStream.getUserId() console.log('stream-subscribed userId: ', remoteUserId) - this.addSuccessLog(`RemoteStream subscribed: [${remoteUserId}].`) - this.remoteStreamList.push(remoteStream) - this.$nextTick(() => { - this.playRemoteStream(remoteStream, remoteUserId) + data.remoteStreamList.push(remoteStream) + nextTick(() => { + playRemoteStream(remoteStream, remoteUserId) }) }) /* 远端流移除 */ data.client.on('stream-removed', (event) => { const { stream: remoteStream } = event remoteStream.stop() - const index = this.remoteStreamList.indexOf(remoteStream) + const index = data.remoteStreamList.indexOf(remoteStream) if (index >= 0) { - this.remoteStreamList.splice(index, 1) + data.remoteStreamList.splice(index, 1) } console.log(`stream-removed userId: ${remoteStream.getUserId()} type: ${remoteStream.getType()}`) }) @@ -176,7 +194,6 @@ export default { data.client.on('stream-updated', (event) => { const { stream: remoteStream } = event 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) => { @@ -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: 初始化本地流 * @return {*} @@ -249,21 +307,41 @@ export default { * @return {*} */ const publishStream = async() => { - if (!data.isJoined || data.isPublishing || data.isPublished) { + if (!status.isJoined || status.isPublishing || status.isPublished) { return } - data.isPublishing = true + status.isPublishing = true try { await data.client.publish(settings.localStream) - data.isPublishing = false - data.isPublished = true + status.isPublishing = false + status.isPublished = true } catch (error) { - data.isPublishing = false + status.isPublishing = false console.error('publish localStream failed', 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: 打开摄像头 * @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() => { - await data.client.leave() + if (!status.isJoined || status.isLeaving) { + return + } + status.isLeaving = true + stopGetAudioLevel() + status.isPublished && await unPublishStream() + settings.localStream && destroyLocalStream() + + try { + await data.client.leave() + status.isLeaving = false + status.isJoined = false + } catch (error) { + status.isLeaving = false + console.error('leave room error', error) + throw error + } } onMounted(() => { const { userId, roomId } = route.query data.userId = userId data.roomId = Number(roomId) - // nextTick(async() => { - // await createMeetingRoom() - // await initLocalStream() - // }) }) watch(() => [settings.cameraId, settings.microphoneId], async([cameraId, microphoneId]) => { if (cameraId && microphoneId) { @@ -334,10 +447,12 @@ export default { return { ...toRefs(data), ...toRefs(settings), + ...toRefs(status), handleVideoMute, handleVideoUnMute, handleAudioMute, - handleAudioUnMute + handleAudioUnMute, + leaveMeetingRoom } } } @@ -347,10 +462,12 @@ export default { .room{ width: 100vw; height: 100vh; + position: relative; .local-stream-container{ width: 100%; height: 100%; - position: relative; + position: absolute; + top: 0; .local-stream-content { width: 100%; height: 100%; @@ -358,9 +475,9 @@ export default { .local-stream-control { width: 100%; height: 100%; - height: 40px; + height: 50px; position: absolute; - bottom: 5px; + bottom: 0; z-index: 99; display: flex; 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; + } + } }