/* eslint-disable @typescript-eslint/no-explicit-any */ 'use client' import { useEffect, useRef, useState } from "react" import { Device } from 'mediasoup-client' import { Transport } from "mediasoup-client/types" const wsURL = 'ws://localhost:4000' let remoteStream export default function MediaSoupWidget() { const socket = useRef(undefined) const device = useRef(undefined) const videoRef = useRef(null) const videoBRef = useRef(null) const consumerTransport = useRef(null) const [connected, setConnected] = useState(false) const [states, setStates] = useState([]) const loadDevices = async (routerCapabilities: any) => { try { device.current = new Device() await device.current!.load({ routerRtpCapabilities: routerCapabilities }) // console.log(`Supported : `, device) setConnected(true) } catch (e) { if ((e as any).name === 'UnsupportedErro') { console.log(`Not Supported`) } console.error(`Faield to create device:`, e) } } const getUserMedia = async (transport: any, isWebCam: boolean) => { if (!device.current?.canProduce('video')) { console.error(`Cant stream video`) return } let stream try { stream = isWebCam ? await navigator.mediaDevices.getUserMedia({ video: true, audio: true }) : await navigator.mediaDevices.getDisplayMedia({ video: true }) } catch (e) { console.error(`Unable to get device to stream`, e) } return stream } const onProducerTransportCreated = async (producerDetails: any) => { // 5. Createe Send Transport setStates(s => [...s, 'createSendTransport']) const transport = device.current?.createSendTransport({ ...producerDetails }) transport?.on('connect', async ({ dtlsParameters }, callback, errback) => { // 6. connectProducerTransport setStates(s => [...s, 'createSendTransport']) socket.current?.send(JSON.stringify({ type: 'connectProducerTransport', dtlsParameters })) socket.current?.addEventListener('message', ev => { // 7. producerTransportConnected // console.log(`SEOM TO CHK `, ev.data) if (JSON.parse(ev.data).type === 'producerTransportConnected') { setStates(s => [...s, 'producerTransportConnected']) // console.log(`------->producerTransportConnected:onCB`) callback() } }) }) transport?.on('produce', async ({ kind, rtpParameters }, callback, errback) => { // 8. Produce setStates(s => [...s, 'produce']) socket.current?.send(JSON.stringify({ type: 'produce', transportId: transport.id, kind, rtpParameters })) socket.current?.addEventListener('message', ev => { if (ev.data.type === 'published') { callback(ev.data.id``) } }) }) transport?.on('connectionstatechange', async (state) => { console.log(`connectionstatechange`, state) switch (state) { case 'connecting': break; case 'closed': break; case 'connected': // Link stream here break; case 'disconnected': break; case 'failed': transport.close() break; case 'new': break; } }) transport?.on('icecandidateerror', async (error) => { console.log('icecandidateerror', error) }) transport?.on('icegatheringstatechange', async (error) => { console.log('icegatheringstatechange', error) }) transport?.on('producedata', async (error) => { console.log('producedata', error) }) let streamMedia try { streamMedia = await getUserMedia(transport, true) // is webcam const track = streamMedia!.getVideoTracks()[0] // const params - { track } // videoRef.current?.h = track console.log(`LocalStream`, streamMedia) // videoRef.current!.srcObject = streamMedia const producer = await transport?.produce({ track }) producer!.on("trackended", () => { console.log("track ended"); }); producer!.on("transportclose", () => { console.log("transport ended"); }); } catch (e) { console.log(`ERROR`, e) } } const onSubTransportCreated = (consumerDetails: any) => { consumerTransport.current = device.current!.createRecvTransport({ ...consumerDetails }) console.log(`onSubTransportCreated`, consumerDetails, consumerTransport.current.connectionState) consumerTransport.current.on('connect', ({ dtlsParameters }, callback, errback) => { //11 . Accept connect setStates(s => [...s, 'connectConsumerTransport']) socket.current?.send(JSON.stringify({ type: 'connectConsumerTransport', transportId: consumerTransport.current!.id, dtlsParameters })) socket.current?.addEventListener('message', ev => { if (JSON.parse(ev.data).type === 'subConnected') { console.log(`subConnected**`, JSON.parse(ev.data).type) callback() } }) }) consumerTransport.current?.on('connectionstatechange', async (state) => { console.log(`connectionstatechange:`, state) switch (state) { case 'connecting': break; case 'failed': console.log(`FILAED`); break; case 'connected': console.log(`remoteStream`, remoteStream) // videoBRef.current!.srcObject = remoteStream socket.current?.send(JSON.stringify({ type: 'resume', })) break; default: break; } }) consumerTransport.current?.on('icecandidateerror', () => { console.log(`icecandidateerror`) }) consumerTransport.current?.on('icegatheringstatechange', async (state) => { console.log(`icegatheringstatechange`, state) }) const stream = consumer(consumerTransport.current) } const onSubscribe = async (details: any) => { console.log('onSubscribe', details) const { producerId, id, kind, rtpParameters, type, producerPaused, } = details const codecOptions = {} const consumer = await consumerTransport.current!.consume({ producerId, id, kind, rtpParameters, // type, // producerPaused, // codecOptions }) const stream = new MediaStream() stream.addTrack(consumer.track) console.log(`TRYIN STREAM 1`,stream) videoBRef.current!.srcObject = stream } const consumer = async (transport: any) => { const rtpCapabilities = device.current?.recvRtpCapabilities socket.current?.send(JSON.stringify({ type: 'consume', rtpCapabilities })) } // const connectSendTransport = async () => { // const producer = await transport.produce(params); // console.log("Producer created:", producer.id, producer.kind); // producer.on("trackended", () => { // console.log("track ended"); // }); // producer.on("transportclose", () => { // console.log("transport ended"); // }); // }; const stream = () => { // 4.1 Start creating producers steam // 4. Producers stream setStates(s => [...s, 'createProducerTransport']) socket.current?.send(JSON.stringify({ type: 'createProducerTransport', forceTcp: false, rtpCapabilities: device.current?.sendRtpCapabilities })) } const joinStream = () => { setStates(s => [...s, 'createConsumerTransport']) socket.current?.send(JSON.stringify({ type: 'createConsumerTransport', forceTcp: false, // rtpCapabilities: device.current?.sendRtpCapabilities })) } // eslint-disable-next-line @typescript-eslint/no-explicit-any const parseWSMessage = (ev: any) => { const recv = JSON.parse(ev) // console.log(`-- parseWSMessage --`, recv.data) switch (recv.type) { case 'routerCapabilities': // 3. Received capabilities setStates(s => [...s, 'routerCapabilities']) loadDevices(recv.data); break; case 'producerTransportCreated': // 4.2 Received capabilities setStates(s => [...s, 'producerTransportCreated']) onProducerTransportCreated(recv.data); break; // case 'producerTransportConnected': // // 7. producerTransportConnected but not with callback // setStates(s => [...s, 'producerTransportCreated']) // // callback() // break; case 'newProducer': // 9 Found new Produce contents and send to all clients setStates(s => [...s, 'newProducer']) break; case 'subTransportCreated': // 10 Consumer joined setStates(s => [...s, 'subTransportCreated']) onSubTransportCreated(recv.data); break; case 'resumed': // 10 Consumer joined setStates(s => [...s, 'resumed']) console.log(`resumed`, recv.data) break; case 'subscribed': // 12 Consumer joined setStates(s => [...s, 'subscribed']) onSubscribe(recv.data) break; default: console.log(`Received Uknown`, recv.type) break; } // console.log(ev) } return (
WebRTCChat
Connected To Server
Establish connection to the WS Server
Checking Server Capabilities
getRouterRtpCapabilities
Has Server Capabilities
routerCapabilities
Create Producers Transport (Waiting for user to start stream)
createProducerTransport
Created Producers Transport
producerTransportCreated
Created Producers Transport
producerTransportCreated
Created Send Transport
createSendTransport
Producer Transport Connected
producerTransportConnected
Send Produced
produce
Found new Produce contents and send to all clients
newProducer
Cunsumber connected
subTransportCreated
Accept Consumer Connection
connectConsumerTransport
) }