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, 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, } })