Search code examples
webrtcvideo-streamingstreamingsimplewebrtc

WebRTC - multi-track streams issue on Firefox <> Chrome


I created a video-call + multiple screen sharing app that works well in every case except one.

The main flow is:

  • A user (P1) join an empty room, nothing happens
  • A second user (P2) join the room. P1 sends an offer to P2 which sends a response to P1.
  • Other users (PN) join the room. Every user already in the room send offers to the PN user. PN user send answers for every offer.
  • During the main app flow, every user can share their screen (multiple times). When this happens, the user send an offer to every already-connected peers and receive answers from them.

The webcam flow always works well, while the screen sharing breaks in a specific case:

  • P1 Is a Chrome user that joins the Room. Nothing happens.
  • P2 Is a Firefox user that joins the Room. P1 (Chrome) sends his offer to P2 Webcam connection works well as usual.
  • P2 (Firefox) share a screen. The Offer throws an error:
Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to parse SessionDescription. a=rtpmap:127 H264/90000 Duplicate  payload type with conflicting codec name or clock rate

Looking at the offer's SDP, i see that the payload for the line a=rtpmap:127 is duplicated:

a=rtpmap:127 H264/90000
...
a=rtpmap:127 rtx/90000

This error happens ONLY in the specified flow (Chrome sends an offer first, Firefox answers (and camera works well), Firefox share a screen and i get the SDP error). If the first offer is sent by a Firefox user, everything works well. If the first offer is sent by a Chrome, everything works well if the first screen sharing is started by a Chrome user.

If the first offer is sent by a Chrome user and then a Firefox user shares a screen, it breaks. Only in this case.

So, the problem is that the offer created by the Firefox user during the first screen sharing contains that payload conflict.

Why does it happen (only in that case) and how can i prevent this error?


Solution

  • I also run into this bug and i didn't found a solution other than modifying the SDP description by myself. Although this is a rough workaround, it fix the codec collision bug.

    1. You want to remove the duplicate codec number form the video track (127 here):

      m=video 9 UDP/TLS/RTP/SAVPF 100 101 127 127 ...

    2. then remove the payload of the RTX codec.

      a=rtpmap:127 RTX/90000

      ...

    Keep in mind that RTX is used to resend corrupted packages

    RTX stands for retransmission. RTX

    RTP retransmission is an effective packet loss recovery technique for real-time applications with relaxed delay bounds rfc4588

    So removing it from payload may void this capability.

    Here my JS (typescript) function

    removeDuplicateCodecs(sdp: string): string {
        // split sdp for each video track
        const lines = sdp.split(/^(?=m=video)/gm);
    
        for (let i = 0, videosLength = lines.length; i < videosLength; i++) {
            if (lines[i].startsWith("m=video")) {
    
                // split each line
                let rows = lines[i].split(/\r\n/gm);
                const codecDuplicated: string[]= [];
    
                if (rows.length) {
                    // take first row and get all codecs
                    const duplicates = rows[0].match(/(\b\d+\b)(?=.*\b\1\b)/g);
    
                    if (duplicates?.length) {
                        duplicates.forEach(duplicate => {
    
                            // remove duplicates from row
                            rows[0] = rows[0].replace(` ${duplicate}`, '')
                            codecDuplicated.push(duplicate);
                        });
                    }
                }
    
                // join back all rows
                lines[i] = rows.join('\r\n');
    
                // split by rtpmap
                rows = lines[i].split(/^(?=a=rtpmap:)/gm);
                if (rows) {
                    codecDuplicated.forEach(duplicate => {
                        
                    // remove duplicate codec definitions rows
                        rows = rows.filter(row => !row.startsWith(`a=rtpmap:${duplicate} rtx`));
                    });
                    lines[i] = rows.join('');
                }
            }
        }
        return lines.join('');
    }