diff --git a/src/components/Main/MainView/QallView/AudioTrack.vue b/src/components/Main/MainView/QallView/AudioTrack.vue index 6dd392a3f..3c99214b5 100644 --- a/src/components/Main/MainView/QallView/AudioTrack.vue +++ b/src/components/Main/MainView/QallView/AudioTrack.vue @@ -1,23 +1,31 @@ diff --git a/src/components/Main/MainView/QallView/QallView.vue b/src/components/Main/MainView/QallView/QallView.vue index ffa1a6b19..e6ebe6e3a 100644 --- a/src/components/Main/MainView/QallView/QallView.vue +++ b/src/components/Main/MainView/QallView/QallView.vue @@ -3,25 +3,27 @@ import { useQall } from '/@/composables/qall/useQall' import VideoComponent from '/@/components/Main/MainView/QallView/VideoTrack.vue' import AudioComponent from '/@/components/Main/MainView/QallView/AudioTrack.vue' -const { tracksMap } = useQall() +const { tracksMap, addScreenShareTrack } = useQall() diff --git a/src/composables/qall/useLiveKitSDK.ts b/src/composables/qall/useLiveKitSDK.ts index 288be36ce..6e5d33a49 100644 --- a/src/composables/qall/useLiveKitSDK.ts +++ b/src/composables/qall/useLiveKitSDK.ts @@ -12,15 +12,24 @@ import type { LocalTrackPublication, LocalParticipant, Participant, - TrackPublication + LocalTrack } from 'livekit-client' import { ref, type Ref } from 'vue' import { useToastStore } from '/@/store/ui/toast' +import apis from '/@/lib/apis' const { addErrorToast } = useToastStore() -type TrackInfo = { - trackPublication: TrackPublication | undefined +export type TrackInfo = ( + | { + isRemote: true + trackPublication: RemoteTrackPublication + } + | { + isRemote: false + trackPublication: LocalTrackPublication | undefined + } +) & { participantIdentity: string } @@ -36,6 +45,7 @@ function handleTrackSubscribed( if (track.kind === Track.Kind.Video || track.kind === Track.Kind.Audio) { // attach it to a new HTMLVideoElement or HTMLAudioElement tracksMap.value.set(publication.trackSid, { + isRemote: true, trackPublication: publication, participantIdentity: participant.identity }) @@ -64,8 +74,9 @@ function handleLocalTrackPublished( participant: LocalParticipant ) { // when local tracks are ended, update UI to remove them from rendering - if (!publication.track || publication.track.kind === Track.Kind.Audio) return + if (!publication.track) return tracksMap.value.set(publication.trackSid, { + isRemote: false, trackPublication: publication, participantIdentity: participant.identity }) @@ -82,14 +93,28 @@ function handleDisconnect() { const joinRoom = async (roomName: string, userName: string) => { try { + const traQtoken = (await apis.getMyQRCode(true)).data + console.log(traQtoken) const res = await fetch( - `http://localhost:3000/getToken?roomName=${roomName}&participantName=${userName}` + `https://easy-livekit-token-publisher.trap.show/token`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${traQtoken}`, + 'Content-Type': 'application/json' + } + } ) - const token = await res.text() + const json = await res.json() + const token = json.token + // pre-warm connection, this can be called as early as your page is loaded //room.prepareConnection("https://livekit-test.trap.show:39357", token); room.value = new Room() - await room.value.prepareConnection('ws://localhost:7880', token) + await room.value.prepareConnection( + 'wss://livekit.qall-dev.trapti.tech', + token + ) console.log(token) // set up event listeners @@ -102,7 +127,7 @@ const joinRoom = async (roomName: string, userName: string) => { .on(RoomEvent.LocalTrackPublished, handleLocalTrackPublished) // connect to room - await room.value.connect('ws://localhost:7880', token) + await room.value.connect('wss://livekit.qall-dev.trapti.tech', token) console.log('connected to room', room.value.name) // publish local camera and mic tracks @@ -121,9 +146,7 @@ const joinRoom = async (roomName: string, userName: string) => { dtx: false } ) - await room.value.localParticipant.setScreenShareEnabled(true, { - audio: true - }) + await room.value.localParticipant.setAttributes({}) } catch { addErrorToast('Qallの接続に失敗しました') await leaveRoom() @@ -143,26 +166,105 @@ async function leaveRoom() { window.removeEventListener('beforeunload', leaveRoom) } +const Attributes = ref<{ [key: string]: string }>({}) + const addScreenShareTrack = async () => { try { if (!room.value) { addErrorToast('ルームが存在しません') return } - const localTrack = await createLocalScreenTracks({}) - localTrack.map(async t => { - await room.value?.localParticipant.publishTrack(t) + const localTracks = await createLocalScreenTracks({ + audio: true }) + + await Promise.all( + localTracks.map(async t => { + await room.value?.localParticipant.publishTrack(t) + }) + ) + const videoSid = localTracks.find(t => t.kind === Track.Kind.Video)?.sid + const audioSid = localTracks.find(t => t.kind === Track.Kind.Audio)?.sid + if (audioSid && videoSid) { + Attributes.value = { + ...room.value.localParticipant.attributes, + [videoSid]: audioSid + } + await room.value.localParticipant.setAttributes({ + ...room.value.localParticipant.attributes, + [videoSid]: audioSid + }) + } } catch { addErrorToast('スクリーン共有に失敗しました') } } +const removeScreenShareTrack = async ( + localpublication: LocalTrackPublication +) => { + if (localpublication.track) { + if (!room.value) { + addErrorToast('ルームが存在しません') + return + } + + const { [localpublication.trackSid]: audioSid, ...newAttributes } = + Attributes.value + //room.value.localParticipant.attributes + console.log(audioSid) + await room.value.localParticipant.unpublishTrack( + localpublication.track, + true + ) + console.log(audioSid) + room.value.localParticipant.setAttributes(newAttributes) + Attributes.value = newAttributes + if (!audioSid) { + return + } + + const audioTrack = tracksMap.value.get(audioSid) + console.log(audioTrack) + console.log(tracksMap.value) + if ( + !audioTrack || + audioTrack.isRemote || + !audioTrack.trackPublication?.track + ) { + return + } + + await room.value.localParticipant.unpublishTrack( + audioTrack.trackPublication.track, + true + ) + } +} + +const setLocalTrackMute = async (track: LocalTrack, muted: boolean) => { + if (muted) { + await track.mute() + } else { + await track.unmute() + } +} + +const setTrackEnabled = ( + publication: RemoteTrackPublication, + muted: boolean +) => { + publication.setEnabled(!muted) +} + export const useLiveKitSDK = () => { return { joinRoom, leaveRoom, addScreenShareTrack, + removeScreenShareTrack, + setTrackEnabled, + setLocalTrackMute, tracksMap } } diff --git a/src/composables/qall/useQall.ts b/src/composables/qall/useQall.ts index ba7152909..8577104b4 100644 --- a/src/composables/qall/useQall.ts +++ b/src/composables/qall/useQall.ts @@ -2,11 +2,27 @@ import { ref } from 'vue' import { useLiveKitSDK } from '/@/composables/qall/useLiveKitSDK' import { useMeStore } from '/@/store/domain/me' import { useToastStore } from '/@/store/ui/toast' +import type { LocalAudioTrack, LocalVideoTrack } from 'livekit-client' const isCalling = ref(false) -const { joinRoom, leaveRoom, addScreenShareTrack, tracksMap } = useLiveKitSDK() +const { + joinRoom, + leaveRoom, + addScreenShareTrack, + removeScreenShareTrack, + setLocalTrackMute, + tracksMap +} = useLiveKitSDK() const { myId } = useMeStore() const { addErrorToast } = useToastStore() + +const setSpeakerMute = (track: LocalAudioTrack, muted: boolean) => { + setLocalTrackMute(track, muted) +} + +const setVideoMute = (track: LocalVideoTrack, muted: boolean) => { + setLocalTrackMute(track, muted) +} export const useQall = () => { const toggleCalling = (channelName: string) => { if (isCalling.value) { @@ -23,6 +39,8 @@ export const useQall = () => { return { isCalling, toggleCalling, + addScreenShareTrack, + removeScreenShareTrack, tracksMap } }