chad/client/app/composables/use-mediasoup.ts
Никита Круглицкий 8e19f55dc0
All checks were successful
Deploy / deploy (push) Successful in 3m59s
№4
2025-10-02 23:39:59 +06:00

176 lines
4.4 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.l.google.com:19302' },
{ urls: 'stun:stun.l.google.com:5349' },
{ urls: 'stun:stun1.l.google.com:3478' },
{ urls: 'stun:stun1.l.google.com:5349' },
{ urls: 'stun:stun2.l.google.com:19302' },
{ urls: 'stun:stun2.l.google.com:5349' },
{ urls: 'stun:stun3.l.google.com:3478' },
{ urls: 'stun:stun3.l.google.com:5349' },
{ urls: 'stun:stun4.l.google.com:19302' },
{ urls: 'stun:stun4.l.google.com:5349' },
]
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('producers', async (producers) => {
watch(connected, async () => {
if (!connected.value)
return
for (const producer of producers) {
await consume(producer.producerId)
}
}, { immediate: true })
})
socket.on('newProducer', async ({ producerId }) => {
await consume(producerId)
})
async function consume(producerId: number) {
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 { producerId } = await socket.emitWithAck('produce', {
transportId: sendTransport.id,
kind,
rtpParameters,
})
// callback({ producerId })
}
catch (err) {
errback(err)
}
})
}
async function publishMic() {
const devices = await navigator.mediaDevices.enumerateDevices()
console.log(devices)
const stream = await navigator.mediaDevices.getUserMedia({
// audio: true,
audio: {
autoGainControl: false,
noiseSuppression: true,
echoCancellation: false,
latency: 0,
},
})
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,
}
})