Files
chad/new-client/src/shared/composables/use-producers.ts
2026-05-22 05:08:41 +06:00

165 lines
4.0 KiB
TypeScript

import type { MediaKind, ProducerOptions } from 'mediasoup-client/types'
import { createGlobalState } from '@vueuse/core'
import { markRaw } from 'vue'
import { useMediasoup } from './use-mediasoup'
import { useSignaling } from './use-signaling'
export const useProducers = createGlobalState(() => {
const { socket } = useSignaling()
const { device, sendTransport, producers } = useMediasoup()
async function createProducer(options: ProducerOptions) {
if (!sendTransport.value || !device.value || !options.track)
return
if (!device.value.canProduce(options.track.kind as MediaKind))
return
const producer = await sendTransport.value.produce({ disableTrackOnPause: true, ...options })
producer.observer.on('trackended', () => disableProducer(producer.id))
await producers.add({
id: producer.id,
paused: producer.paused,
kind: producer.kind,
appData: producer.appData as Record<string, unknown>,
ref: markRaw(producer),
})
}
async function disableProducer(producerId: string) {
const snap = producers.get(producerId)
if (!snap)
return
try {
snap.ref.close()
await socket.value?.emitWithAck('close-producer', { producerId })
}
catch {}
}
async function pauseProducer(producerId: string) {
const snap = producers.get(producerId)
if (!snap || snap.paused)
return
try {
snap.ref.pause()
await socket.value?.emitWithAck('pause-producer', { producerId })
}
catch {
snap.ref.resume()
}
}
async function resumeProducer(producerId: string) {
const snap = producers.get(producerId)
if (!snap || !snap.paused)
return
try {
snap.ref.resume()
await socket.value?.emitWithAck('resume-producer', { producerId })
}
catch {
snap.ref.pause()
}
}
async function enableMic() {
for (const snap of producers.store.value.values()) {
if (snap.kind === 'audio')
return
}
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
const track = stream.getAudioTracks()[0]
if (!track)
return
await createProducer({
track,
streamId: 'mic-video',
codecOptions: { opusStereo: true, opusDtx: true },
appData: { source: 'mic' },
})
}
async function disableMic() {
for (const snap of producers.store.value.values()) {
if (snap.kind === 'audio')
await disableProducer(snap.id)
}
}
async function enableVideo() {
for (const snap of producers.store.value.values()) {
if (snap.kind === 'video' && snap.appData.source !== 'share')
return
}
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: { ideal: 1920 }, height: { ideal: 1080 }, frameRate: { ideal: 60 } },
})
const track = stream.getVideoTracks()[0]
if (!track)
return
await createProducer({
track,
streamId: 'mic-video',
appData: { source: 'camera' },
})
}
async function disableVideo() {
for (const snap of producers.store.value.values()) {
if (snap.kind === 'video' && snap.appData.source !== 'share')
await disableProducer(snap.id)
}
}
async function enableShare() {
for (const snap of producers.store.value.values()) {
if (snap.appData.source === 'share')
return
}
const stream = await navigator.mediaDevices.getDisplayMedia({
audio: false,
video: { displaySurface: 'monitor' },
})
const track = stream.getVideoTracks()[0]
if (!track)
return
await createProducer({
track,
streamId: 'share',
zeroRtpOnPause: true,
appData: { source: 'share' },
})
}
async function disableShare() {
for (const snap of producers.store.value.values()) {
if (snap.appData.source === 'share')
await disableProducer(snap.id)
}
}
return {
enableMic,
disableMic,
enableVideo,
disableVideo,
enableShare,
disableShare,
pauseProducer,
resumeProducer,
disableProducer,
}
})