chad/client/app/composables/use-mediasoup.ts
Никита Круглицкий 8d987c00a8
All checks were successful
Deploy / deploy (push) Successful in 30s
#3 update
2025-10-01 23:15:05 +06:00

162 lines
4.0 KiB
TypeScript

import type { Socket } from 'socket.io-client'
import { createGlobalState } from '@vueuse/core'
import * as mediasoupClient from 'mediasoup-client'
import { io } from 'socket.io-client'
const ICE_SERVERS: RTCIceServer[] = [
{
urls: 'stun:stun.relay.metered.ca:80',
},
{
urls: 'turn:global.relay.metered.ca:80',
username: '4cad09c0111f423f5283814c',
credential: 'B1dO8AGehex4o3pt',
},
{
urls: 'turn:global.relay.metered.ca:80?transport=tcp',
username: '4cad09c0111f423f5283814c',
credential: 'B1dO8AGehex4o3pt',
},
{
urls: 'turn:global.relay.metered.ca:443',
username: '4cad09c0111f423f5283814c',
credential: 'B1dO8AGehex4o3pt',
},
{
urls: 'turns:global.relay.metered.ca:443?transport=tcp',
username: '4cad09c0111f423f5283814c',
credential: 'B1dO8AGehex4o3pt',
},
]
export const useMediasoup = createGlobalState(() => {
const socket: Socket = io('https://api.koptilnya.xyz', {
path: '/chad/ws/',
transports: ['websocket'],
})
const initializing = ref(false)
const connected = ref(false)
const streams = shallowRef<MediaStream[]>([])
let device: mediasoupClient.Device
let sendTransport: mediasoupClient.types.Transport
let recvTransport: mediasoupClient.types.Transport
socket.on('newProducer', async ({ producerId }) => {
const params = await socket.emitWithAck('consume', {
producerId,
transportId: recvTransport.id,
rtpCapabilities: device.rtpCapabilities,
})
if (params?.error) {
console.error('consume error:', params.error)
return
}
const consumer = await recvTransport.consume(params)
const stream = new MediaStream([consumer.track])
streams.value.push(stream)
triggerRef(streams)
})
async function loadDevice() {
device = new mediasoupClient.Device()
const rtpCapabilities = await socket.emitWithAck('getRtpCapabilities')
await device.load({ routerRtpCapabilities: rtpCapabilities })
}
async function createSendTransport() {
const params = await socket.emitWithAck('createTransport')
sendTransport = device.createSendTransport({
...params,
iceServers: [
...ICE_SERVERS,
...(params.iceServers ?? []),
],
})
sendTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
try {
await socket.emitWithAck('connectTransport', {
transportId: sendTransport.id,
dtlsParameters,
})
callback()
}
catch (err) {
errback(err)
}
})
sendTransport.on('produce', async ({ kind, rtpParameters }, callback, errback) => {
try {
const { id } = await socket.emitWithAck('produce', {
transportId: sendTransport.id,
kind,
rtpParameters,
})
callback({ id })
}
catch (err) {
errback(err)
}
})
}
async function publishMic() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
const track = stream.getAudioTracks()[0]
await sendTransport.produce({ track })
}
async function createRecvTransport() {
const params = await socket.emitWithAck('createTransport')
recvTransport = device.createRecvTransport({
...params,
iceServers: [
...ICE_SERVERS,
...(params.iceServers ?? []),
],
})
recvTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
try {
await socket.emitWithAck('connectTransport', {
transportId: recvTransport.id,
dtlsParameters,
})
callback()
}
catch (err) {
errback(err)
}
})
}
(async () => {
if (initializing.value || connected.value)
return
initializing.value = true
connected.value = false
await loadDevice()
await createSendTransport()
await createRecvTransport()
await publishMic()
initializing.value = false
connected.value = true
})()
return {
connected,
streams,
}
})