When producing getting error "connected" even though seemingly "connect" never fired before

Hello everyone, I’m trying to implement the client side logic and when I call the transport.produce() method it fails with error saying “connected” and so does the transport.on(“connect”) event. Even though I never connected any transports. Following is my code

Client-SIde

async function createSendTransport() {
    let data;
    socket.emit("create-transport", async (transportInfo) => {
        console.log(transportInfo.iceCandidates);
        const { id, iceParameters, iceCandidates, dtlsParameters } = transportInfo;

        const transport = device.createSendTransport({
            id,
            iceParameters,
            iceCandidates,
            dtlsParameters,
        });
        console.log("Producer transport initialized:", transport);

        transport.on("connect", async ({ dtlsParameters }, callback, errback) => {
            console.log("Transport connect event triggered.", dtlsParameters);
            socket.emit("connect-transport", { dtlsParameters }, (error) => {
                if (error) {
                    console.error("Error connecting transport:", error);
                    return errback(error);
                }
                console.log("Transport connected successfully");
                callback();
            });
        });

        transport.on("connectionstatechange", async (state) => {
            console.log("Transport connection state:", state);

            if (state === "connected") {
                console.log("Transport is connected. Ready to produce.");
            } else if (state === "connecting") {
                console.log("Connecting transport");
            } else if (state === "failed") {
                console.error("Transport connection failed.");
            }
        });

        transport.on("produce", async ({ kind, rtpParameters }, callback, errback) => {
            console.log(`Produce event triggered for ${kind}`);
            const trackId = videoTrack.id;
            if (!trackId) {
                const errorMsg = `No track available for kind ${kind}`;
                console.error(errorMsg);
                return errback(errorMsg);
            }
            console.log(`Producing ${kind} with rtpParameters:`, rtpParameters);

            socket.emit(
                "produce",
                { kind, trackId, rtpParameters, transportId: transport.id },
                (response) => {
                    if (response.error) {
                        console.error("Error in producing media:", response.error);
                        return errback(response.error);
                    }
                    console.log("Produced track ID:", response.id);
                    callback({ id: response.id });
                }
            );
        });

        try {
            videoTrack = localStream.getVideoTracks()[0];
            console.log("Video track selected:", videoTrack);

            const encodings = [
                { maxBitrate: 100000 },
                { maxBitrate: 300000 },
                { maxBitrate: 900000 },
            ];
            const codecOptions = {
                videoGoogleStartBitrate: 1000,
            };
            console.log("Transport state:", transport.connectionState);
            producer = await transport.produce({ track: videoTrack, encodings, codecOptions });
        } catch (error) {
            console.log("Error producing track:", error);
        }
    });
}

Hey @magnimuslaertius

Can you paste the actual Mediasoup client side error (or log) from the browser console highlighting this error.

Hey @BlueMagnificent thanks for trying to help first of all. And I fixed this issue but now I have a different issue. I am doing receiving everything correctly now but when I bind the remote tracks to a new video tag, the video doesn’t play. When I logged the consumer.tracks, I got the following.

  1. MediaStreamTrack

  2. contentHint: “”

  3. enabled: true

  4. id: “7aec0cb8-637f-413c-acdd-8c17b5c51b7d”

  5. kind: “video”

  6. label: “7aec0cb8-637f-413c-acdd-8c17b5c51b7d”

  7. muted: true

  8. oncapturehandlechange: null

  9. onended: null

  10. onmute: null

  11. onunmute: null

  12. readyState: “live”

  13. stats: null

Thanks for replying again, I’ve been trying to make this thing work for over a week. I’ll be closely watching this thread so if you want to check something in my code I’d more than happy to provide you with.

Okay I’ve fixed all the bugs in my code however it only works for 2 peers as soon as the 3rd peer joins the server crashes. Following are the logs from all my server-side console.log statements

App is running
Worker pid: 16028
Router created
Client connected: Kbcr-GZ_FDHxou1mAAAB
Peer b43b77c6-f9e6-446e-b44c-54c9e7561e5c is attempting to join room 52ed7f82-adab-45a1-a45d-54ffde488a67
Producer transport for peer b43b77c6-f9e6-446e-b44c-54c9e7561e5c created with id:115252a7-eae6-4790-9756-3783308d8004
Client producer transport attempting to connect
DTLS PARAMS...After Producer Transport connects to the server  { dtlsParameters: { role: 'client', fingerprints: [ [Object] ] } }
Server recieved track fe2e7bcc-71a1-4436-b69f-f98a0c7b7d1b
video producer created with ID: d865f0ed-9788-459a-a714-d6f9101eb50f
Client connected: 3pkaKAlBRwuoujTGAAAD
Peer 50a01815-5efc-4d84-bce3-66ae657263a2 is attempting to join room 52ed7f82-adab-45a1-a45d-54ffde488a67
Producer transport for peer 50a01815-5efc-4d84-bce3-66ae657263a2 created with id:5e8c9812-7fca-4679-b1d3-b76ebee6959a
Consumer transport for peer 50a01815-5efc-4d84-bce3-66ae657263a2 created with id:af622976-4968-4495-a3f3-86abdc0eaa53
Client producer transport attempting to connect
DTLS PARAMS...After Producer Transport connects to the server  { dtlsParameters: { role: 'client', fingerprints: [ [Object] ] } }
Consumer created for producer ID: d865f0ed-9788-459a-a714-d6f9101eb50f
Server recieved track b97bf4b4-4b18-4e17-9ce2-210b6f206e9f
Client consumer transport attempting to connect
DTLS PARAMS...After Consumer Transport connects to the server  { dtlsParameters: { role: 'client', fingerprints: [ [Object] ] } }
video producer created with ID: b8458330-6864-42c3-96bd-2d5b8d4af0dd
Consumer transport for peer b43b77c6-f9e6-446e-b44c-54c9e7561e5c created with id:810430ea-c0f3-417a-96cd-70e7ae987d9e
consumer resume
Consumer created for producer ID: b8458330-6864-42c3-96bd-2d5b8d4af0dd
Client consumer transport attempting to connect
DTLS PARAMS...After Consumer Transport connects to the server  { dtlsParameters: { role: 'client', fingerprints: [ [Object] ] } }
consumer resume
Client connected: Xhw8r964eKuKaMrCAAAF
Peer 6b9e4f68-451d-47b2-81a0-58abf01e0846 is attempting to join room 52ed7f82-adab-45a1-a45d-54ffde488a67
Producer transport for peer 6b9e4f68-451d-47b2-81a0-58abf01e0846 created with id:3cea2ae8-5adf-40ec-9fc8-fc2af8f3b60a
Consumer transport for peer 6b9e4f68-451d-47b2-81a0-58abf01e0846 created with id:9354a723-5768-46b2-b4e4-c1da75a390f3
Consumer transport for peer 6b9e4f68-451d-47b2-81a0-58abf01e0846 created with id:1cbbb6af-5262-4171-ac52-1b2ac09b3e9f
Client producer transport attempting to connect
DTLS PARAMS...After Producer Transport connects to the server  { dtlsParameters: { role: 'client', fingerprints: [ [Object] ] } }
Consumer created for producer ID: d865f0ed-9788-459a-a714-d6f9101eb50f
Consumer created for producer ID: b8458330-6864-42c3-96bd-2d5b8d4af0dd
Server recieved track 417a98d5-dfd1-4e09-bfd6-c2254e76f673
Client consumer transport attempting to connect
DTLS PARAMS...After Consumer Transport connects to the server  { dtlsParameters: { role: 'client', fingerprints: [ [Object] ] } }
Client consumer transport attempting to connect
DTLS PARAMS...After Consumer Transport connects to the server  { dtlsParameters: { role: 'client', fingerprints: [ [Object] ] } }
video producer created with ID: e2fc4b3c-07dd-45a9-9071-371572fd17f6
D:\WebDev\_WebRTC\kurento-live-room\group-conference\node_modules\mediasoup\node\lib\Channel.js:260
                    sent.reject(new Error(response.reason()));
                                ^

Error: connect() already called [method:webRtcTransport.connect]

It looks like the transport connect method of an already existing participants is called again when a new participant joins. This could be in your procedure that handles new participants joining. That being the case, only that new participant’s transport connect method should be called and not that of the existing participants since they are already connected.

This my receiver transport connecting method

socket.on("connect-recv-transport", async ({ dtlsParameters }, callback) => {
            console.log("Client consumer transport attempting to connect");
            const transport = rooms[socket.roomId].recvTransports[socket.peerId];

            if (!transport) {
                return callback({ error: "Consumer Transport not found" });
            }
            console.log("DTLS PARAMS...After Consumer Transport connects to the server ", {
                dtlsParameters,
            });
            await transport.connect({ dtlsParameters });
            callback("Consumer transport successfully connected");
        });

And the send transport connection method is implemented in a similar way as well. Is there a way to check before hand if the transport I’m trying to connect is already connected and hence it doesn’t proceed to try and connect it or will I just have to go through the whole client server setup and find why the transport of already connected peers is being called?

In simple setups, transport creation and connection for a participant is usually part of the initializing process when the participants joins for the first time. This is also where you device a way to map a transport to it’s participant. Based on your code snippet that looks just like what you have done. My suspicion, then, would be on how you notify other participants of a new peer and how they consume from it. Can you review how it is handled both on the client side and on the server side, if possible, paste the code from the both sides.

Thanks a lot for trying to help friend.
So when a new peer joins they emit “join-room” and then the server maps the new peer with their id and stuff and also sends them the list of producers of existing members in the meeting (or so I hoped) along with router’s rtpCapabilities.

socket.on("join-room", async ({ roomId }) => {
            if (!roomId) {
                roomId = getRoomID();
            }
            const peerId = getPeerID();
            const router = await getRouter();

            console.log(`Peer ${peerId} is attempting to join room ${roomId}`);
            joinRoom(roomId, peerId);
            addPeer(peerId, socket.id);

            socket.join(roomId);
            socket.roomId = roomId;
            socket.peerId = peerId;

            const existingProducers = rooms[roomId].producers || {};
            const producers = [];
            for (const producerId in existingProducers) {
                const producer = existingProducers[producerId];
                producers.push({ producerId: producer.id, kind: producer.kind });
            }
            socket.emit("room-joined", {
                routerRtpCapabilities: router.rtpCapabilities,
                producers: producers,
                roomId: socket.roomId,
                peerId: socket.peerId,
            });
        });

On the client side when it receives the “room-joined” event it creates a medisoup device, loads the device, gets the localstream creates a send transport which also further on requests the server to create a new producer as well (we’ll come back to this) but most importantly, it consumes the list of producers it received from the server.

async function joinRoom(roomId) {
    socket.emit("join-room", { roomId });

    socket.on("room-joined", async ({ routerRtpCapabilities, producers, roomId, peerId }) => {
        console.log("Room joined. Router RTP capabilities:", routerRtpCapabilities);
        console.log("Room ID:", roomId, "Peer ID:", peerId);

        document.getElementById("roomID").innerText = roomId;

        device = new mediasoup.Device();
        await device.load({ routerRtpCapabilities });

        localStream = await navigator.mediaDevices.getUserMedia({ video: true });
        document.getElementById("local-video").srcObject = localStream;

        createSendTransport();

        console.log("Initial Producers:", producers);
        producers.forEach(async (producer) => await consumeMedia(producer.producerId));
    });

    socket.on("new-producer", async ({ producerId }) => {
        console.log("New producer available:", producerId);
        await consumeMedia(producerId);
    });
}

When the createSendTransport() method creates a transport on the server and connects it, it also creates a new producer on the server and the server emits “new producer” and the client consumes that producer. That is how I hoped the existing peers are notified of the new peer and consume their media.

Please help me out, or would you like to see my full code, I’ll be happy to show you