Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/livekit sdk init #4449

Merged
merged 4 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions src/components/Main/MainView/QallView/AudioTrack.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
<script setup lang="ts">
import type { LocalAudioTrack, RemoteAudioTrack } from 'livekit-client'
import { onMounted, onUnmounted, useTemplateRef } from 'vue'
import { onMounted, onUnmounted, ref, useTemplateRef, watchEffect } from 'vue'
import type { TrackInfo } from '/@/composables/qall/useLiveKitSDK'

const props = defineProps<{
track: LocalAudioTrack | RemoteAudioTrack
const { trackInfo } = defineProps<{
trackInfo: TrackInfo
}>()
const audioElement = useTemplateRef<HTMLMediaElement>('audioElement')
const volume = ref(1)

watchEffect(() => {
if (audioElement.value) {
audioElement.value.volume = volume.value
}
})

onMounted(() => {
if (audioElement.value) {
props.track.attach(audioElement.value)
trackInfo.trackPublication?.track?.attach(audioElement.value)
}
})

onUnmounted(() => {
props.track.detach()
trackInfo.trackPublication?.track?.detach()
})
</script>

<template>
<audio :id="track.sid" ref="audioElement"></audio>
<audio :id="trackInfo.trackPublication?.trackSid" ref="audioElement"></audio>
<input v-model="volume" type="range" min="0" max="1" step="0.01" />
</template>
20 changes: 15 additions & 5 deletions src/components/Main/MainView/QallView/QallView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,44 @@ 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()
</script>

<template>
<div :class="$style.Block">
<h1 :class="$style.Header">Qall View</h1>
<div>
<button @click="addScreenShareTrack">Add Screen Share Track</button>
<div :class="$style.TrackContainer">
<template
v-for="track of tracksMap.values()"
:key="track.trackPublication?.trackSid"
>
<VideoComponent
v-if="track.trackPublication?.kind === 'video'"
:track="track.trackPublication.videoTrack!"
:track-info="track"
:participant-identity="track.participantIdentity"
:class="$style.video"
/>
<AudioComponent
v-else-if="track.trackPublication?.kind === 'audio'"
:track="track.trackPublication.audioTrack!"
v-else-if="track.trackPublication?.kind === 'audio' && track.isRemote"
:track-info="track"
/>
</template>
</div>
</div>
</template>

<style lang="scss" module>
.TrackContainer {
height: fit-content;
}
.video {
width: 50%;
height: 50%;
}
.Block {
color: green;
overflow: scroll;
}

.Header {
Expand Down
42 changes: 35 additions & 7 deletions src/components/Main/MainView/QallView/VideoTrack.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
<script setup lang="ts">
import type { VideoTrack } from 'livekit-client'
import { onMounted, onUnmounted, useTemplateRef } from 'vue'
import { onMounted, onUnmounted, ref, useTemplateRef, watchEffect } from 'vue'
import { useQall } from '/@/composables/qall/useQall'
import type { TrackInfo } from '/@/composables/qall/useLiveKitSDK'

const { track, participantIdentity } = defineProps<{
track: VideoTrack
const { trackInfo, participantIdentity } = defineProps<{
trackInfo: TrackInfo
participantIdentity: string
}>()

const { removeScreenShareTrack } = useQall()

const videoElement = useTemplateRef<HTMLVideoElement>('videoElement')
const volume = ref(1)
watchEffect(() => {
if (videoElement.value) {
videoElement.value.volume = volume.value
}
})

onMounted(() => {
if (videoElement.value) {
track.attach(videoElement.value)
trackInfo.trackPublication?.track?.attach(videoElement.value)
}
})

onUnmounted(() => {
track.detach()
trackInfo.trackPublication?.track?.detach()
})
</script>

Expand All @@ -25,6 +34,25 @@ onUnmounted(() => {
<div>
<p>{{ participantIdentity }}</p>
</div>
<video :id="track.sid" ref="videoElement"></video>
<video
v-if="trackInfo.trackPublication"
:id="trackInfo.trackPublication.trackSid"
ref="videoElement"
:class="$style.video"
></video>
<input v-model="volume" type="range" min="0" max="1" step="0.01" />
<button
v-if="!trackInfo.isRemote && trackInfo.trackPublication"
@click="removeScreenShareTrack(trackInfo.trackPublication)"
>
Remove Screen Share
</button>
</div>
</template>

<style lang="scss" module>
.video {
width: 100%;
height: 100%;
}
</style>
130 changes: 116 additions & 14 deletions src/composables/qall/useLiveKitSDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,24 @@
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
}

Expand All @@ -36,6 +45,7 @@
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
})
Expand Down Expand Up @@ -64,8 +74,9 @@
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
})
Expand All @@ -77,20 +88,34 @@
}

function handleDisconnect() {
console.log('disconnected from room')

Check warning on line 91 in src/composables/qall/useLiveKitSDK.ts

View workflow job for this annotation

GitHub Actions / run lint

Unexpected console statement
}

const joinRoom = async (roomName: string, userName: string) => {
try {
const traQtoken = (await apis.getMyQRCode(true)).data
console.log(traQtoken)

Check warning on line 97 in src/composables/qall/useLiveKitSDK.ts

View workflow job for this annotation

GitHub Actions / run lint

Unexpected console statement
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)

Check warning on line 118 in src/composables/qall/useLiveKitSDK.ts

View workflow job for this annotation

GitHub Actions / run lint

Unexpected console statement

// set up event listeners
room.value
Expand All @@ -102,8 +127,8 @@
.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)

Check warning on line 131 in src/composables/qall/useLiveKitSDK.ts

View workflow job for this annotation

GitHub Actions / run lint

Unexpected console statement

// publish local camera and mic tracks
await room.value.localParticipant.setMicrophoneEnabled(
Expand All @@ -121,9 +146,7 @@
dtx: false
}
)
await room.value.localParticipant.setScreenShareEnabled(true, {
audio: true
})
await room.value.localParticipant.setAttributes({})
} catch {
addErrorToast('Qallの接続に失敗しました')
await leaveRoom()
Expand All @@ -143,26 +166,105 @@
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)

Check warning on line 215 in src/composables/qall/useLiveKitSDK.ts

View workflow job for this annotation

GitHub Actions / run lint

Unexpected console statement
await room.value.localParticipant.unpublishTrack(
localpublication.track,
true
)
console.log(audioSid)

Check warning on line 220 in src/composables/qall/useLiveKitSDK.ts

View workflow job for this annotation

GitHub Actions / run lint

Unexpected console statement
room.value.localParticipant.setAttributes(newAttributes)
Attributes.value = newAttributes
if (!audioSid) {
return
}

const audioTrack = tracksMap.value.get(audioSid)
console.log(audioTrack)

Check warning on line 228 in src/composables/qall/useLiveKitSDK.ts

View workflow job for this annotation

GitHub Actions / run lint

Unexpected console statement
console.log(tracksMap.value)

Check warning on line 229 in src/composables/qall/useLiveKitSDK.ts

View workflow job for this annotation

GitHub Actions / run lint

Unexpected console statement
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
}
}
20 changes: 19 additions & 1 deletion src/composables/qall/useQall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -23,6 +39,8 @@ export const useQall = () => {
return {
isCalling,
toggleCalling,
addScreenShareTrack,
removeScreenShareTrack,
tracksMap
}
}
Loading