user19434921
user19434921

Reputation:

sdp: The order of m-lines in subsequent offer doesn't match order from previous offer/answer

**i am using React Native and getting these two errors in my console log :

  1. Failed to set local offer sdp: The order of m-lines in subsequent offer doesn't match order from previous offer/answer.
  2. Failed to set remote answer sdp: Called in wrong state: stable

Here is my console log below**

LOG  rn-webrtc:pc:DEBUG 0 addTrack +3s
 LOG  rn-webrtc:pc:DEBUG 0 createOffer +102ms
 LOG  rn-webrtc:pc:DEBUG 0 createOffer +25ms
 LOG  rn-webrtc:pc:DEBUG 0 createOffer OK +12ms
 LOG  rn-webrtc:pc:DEBUG 0 setLocalDescription +4ms
 LOG  rn-webrtc:pc:DEBUG 0 createOffer OK +14ms
 LOG  rn-webrtc:pc:DEBUG 0 setLocalDescription +2ms
 LOG  rn-webrtc:pc:DEBUG 0 setLocalDescription OK +78ms

 DEBUG  socket.io-parser encoded {"type":2,"data":["student-offer",{"to":"-iRPss5JKBlxXQMbAAAB",
"offer":{"type":"offer","sdp":"v=0\r\no=- 2287403258896374185 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS bfb7f5b2-32a4-4635-bd0f-c1c3d50b7075\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:jtR8\r\na=ice-pwd:FhO9CIH5eZXks7sqSr87kc3W\r\na=ice-options:trickle renomination\r\na=fingerprint:sha-256 80:37:6C:E6:78:2E:35:3C:FB:55:A5:F2:72:E9:46:02:B5:3E:DF:4E:70:87:06:A0:81:6E:0F:73:41:5B:AE:6B\r\na=setup:actpass\r\na=mid:0\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=sendrecv\r\na=msid:bfb7f5b2-32a4-4635-bd0f-c1c3d50b7075 fec6b43a-c2bc-406f-b488-df44eb1f4be1\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:63 red/48000/2\r\na=fmtp:63 111/111\r\na=rtpmap:9 G722/8000\r\na=rtpmap:102 ILBC/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:3159182572 cname:PCDAKJXlWTgrCQw9\r\na=ssrc:3159182572 msid:bfb7f5b2-32a4-4635-bd0f-c1c3d50b7075 fec6b43a-c2bc-406f-b488-df44eb1f4be1\r\n"}}],"options":{"compress":true},"nsp":"/"} as 2["student-offer",{"to":"-iRPss5JKBlxXQMbAAAB","offer":{"type":"offer","sdp":"v=0\r\no=- 2287403258896374185 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS bfb7f5b2-32a4-4635-bd0f-c1c3d50b7075\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:jtR8\r\na=ice-pwd:FhO9CIH5eZXks7sqSr87kc3W\r\na=ice-options:trickle renomination\r\na=fingerprint:sha-256 80:37:6C:E6:78:2E:35:3C:FB:55:A5:F2:72:E9:46:02:B5:3E:DF:4E:70:87:06:A0:81:6E:0F:73:41:5B:AE:6B\r\na=setup:actpass\r\na=mid:0\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=sendrecv\r\na=msid:bfb7f5b2-32a4-4635-bd0f-c1c3d50b7075 fec6b43a-c2bc-406f-b488-df44eb1f4be1\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:63 red/48000/2\r\na=fmtp:63 111/111\r\na=rtpmap:9 G722/8000\r\na=rtpmap:102 ILBC/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:3159182572 cname:PCDAKJXlWTgrCQw9\r\na=ssrc:3159182572 msid:bfb7f5b2-32a4-4635-bd0f-c1c3d50b7075 fec6b43a-c2bc-406f-b488-df44eb1f4be1\r\n"}}] +10ms

 DEBUG  socket.io-client:socket emitting event ["offer-accepted",{"from":"-iRPss5JKBlxXQMbAAAB",
"ans":{"type":"answer","sdp":"v=0\r\no=- 8253650245603910748 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS e0e92938-b3e3-4fca-bfd4-3eda5eeb5325\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:3pBx\r\na=ice-pwd:dFH3SZ6F2bc9QoG8FBshIduU\r\na=ice-options:trickle renomination\r\na=fingerprint:sha-256 BC:51:3B:D6:3B:8B:E8:E6:55:CA:2D:B2:A5:5A:B1:42:3C:1D:98:F1:8E:F1:74:C7:70:49:D3:46:D2:33:2F:71\r\na=setup:active\r\na=mid:0\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=sendrecv\r\na=msid:e0e92938-b3e3-4fca-bfd4-3eda5eeb5325 9bf1d093-64a1-4c5e-abef-10bc4163363c\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:63 red/48000/2\r\na=fmtp:63 111/111\r\na=rtpmap:9 G722/8000\r\na=rtpmap:102 ILBC/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:2611272474 cname:lZEiOr+ny1FHM0pn\r\n"}}] +4s
 LOG  rn-webrtc:pc:DEBUG 0 setRemoteDescription +1s
 LOG  rn-webrtc:pc:DEBUG 0 setRemoteDescription +3ms
 LOG  rn-webrtc:pc:DEBUG 0 ontrack +271ms
 LOG  rn-webrtc:pc:DEBUG 0 setRemoteDescription OK +48ms
 WARN  Possible Unhandled Promise Rejection (id: 0):

Error: Failed to set local offer sdp: The order of m-lines in subsequent offer doesn't match order from previous offer/answer.
Error: Failed to set remote answer sdp: Called in wrong state: stable

This is the code I am using for creating offer and ans :-

    const getAnswer = async (offer) => {
        if (peer) {
            const offerDescription = new RTCSessionDescription( offer );
            await peer.setRemoteDescription( offerDescription );
        
            const ans = await peer.createAnswer();
            await peer.setLocalDescription( ans );

            return ans;
        }
    }

    const setLocalDescription = async (ans) => {
        if (peer) {
            await peer.setRemoteDescription(new RTCSessionDescription(ans));
        }
    }

    const getOffer = async () => {
        if (peer) {
            const offer = await peer.createOffer(sessionConstraints);
            var res = await peer.setLocalDescription(new RTCSessionDescription(offer));
            return offer;
        }
    }

I am working on two different apps, one app only sends offer and waits for answer and the other only receives the offer and send answer. There is no error in my seconds app but I am getting this error on my first app and below is the code for handling the call in first app

const { ready, socketId, socket, peer, getAnswer, setLocalDescription, getOffer } = webrtc;

  useEffect(() => {
    handleCallUser()
  }, [])

  /////////////////// SEND OFFER //////////////////
  const handleCallUser = useCallback(async () => {
    const stream = await mediaDevices.getUserMedia({
      audio: true,
      video: true,
    });
 
    setMyStream(stream);
    for (const track of stream.getTracks()) {
      peer.addTrack(track, stream);
    }

    notifee.cancelAllNotifications();
    startCall() 

    const offer = await getOffer();

    socket.emit("student-offer", { to: socketId.mentor, offer });
  }, [socketId, socket]);

  /////////// HANDLE CALL ACCEPTED ////////////////////
  const handleCallAccepted = useCallback(
    ({ from, ans }: any) => {
      setLocalDescription(ans);
    },
    []
  );

  /////////// HANDLE NEGOCIATION ////////////////////
  const handleNegoNeeded = useCallback(async () => {
    const offer = await getOffer();
    socket.emit("peer:nego:needed", { offer, to: socketId.mentor });
  }, [socketId, socket]);

  const handleNegoNeedIncomming = useCallback(
    async ({ from, offer }: any) => {
      const ans = await getAnswer(offer);
      socket.emit("peer:nego:done", { to: from, ans });
    },
    [socket]);

  const handleNegoNeedFinal = useCallback(async ({ ans }: any) => {
    await setLocalDescription(ans);
  }, []);

  ////////////////////// GET TRACKS ////////////////////////
  const handleTracks = async (ev: any) => {
    const remoteStream = ev.streams;
    setRemoteStream(remoteStream[0]);
  }

  ////////////////////// ICE CANDIDATE /////////////////////
  const handleIceCandidate = useCallback(async (event: any) => {
    socket.emit('ice-candidate', { to: socketId.mentor, candidate: event.candidate })
  }, []);
  const setIceCandidate = useCallback(async ({ candidate }: any) => {
    peer.addIceCandidate(new RTCIceCandidate(candidate))
  }, []);

  /////////// HANDLE PEER ////////////////////
  useEffect(() => {
    peer.addEventListener("negotiationneeded", handleNegoNeeded);
    peer.addEventListener("track", handleTracks);
    peer.addEventListener("onicecandidate", handleIceCandidate);
    return () => {
      peer.removeEventListener("negotiationneeded", handleNegoNeeded);
      peer.removeEventListener("track", handleTracks);
      peer.removeEventListener("onicecandidate", handleIceCandidate);
    };
  }, [handleNegoNeeded, handleTracks, handleIceCandidate]);


  /////////// HANDLE SOCKETS ////////////////////
  useEffect(() => {
    socket.on("offer-accepted", handleCallAccepted);
    socket.on("ice-candidate", setIceCandidate);
    socket.on("peer:nego:needed", handleNegoNeedIncomming);
    socket.on("peer:nego:final", handleNegoNeedFinal);

    return () => {
      socket.off("call:accepted", handleCallAccepted);
      socket.off("ice-candidate", setIceCandidate);
      socket.off("peer:nego:needed", handleNegoNeedIncomming);
      socket.off("peer:nego:final", handleNegoNeedFinal);
    };
  }, [socket, handleCallAccepted, handleNegoNeedIncomming, handleNegoNeedFinal]);

Upvotes: 5

Views: 1894

Answers (1)

Andriy Baranskyy
Andriy Baranskyy

Reputation: 116

I am curious how you are using const setLocalDescription = async (ans) Clearly, this is a method to set local description, but you are setting remote description inside there... Can you elaborate on what calls it?

At LiveSwitch, this is how we implement it: imagine you have peer A and peer B. On peer A, call createOffer() and setLocalDescription(), then send this description to B, where you'd call setRemoteDescriotion() and createAnswer(), then take the answer and send it back to peer A, which calls setRemoteDescription() with that answer.

Now, imagine you need to change something on a live session, then, you'd create a new offer, set it, then set remote description on the remote side, create answer there and the set remote description on the local peer. You'd do this over and over. After performing this set of actions, your system will be in a stable set. If you are calling any of these methods in the stable set, then you're 'renegotiating'.

When I am looking at your logs, it looks like you have already negotiated a connection and then are trying to renegotiate it because: a) the first error indicates that you are trying to change the order of streams of the previous offer/answer, b) the second error indicate that you are trying to create an answer without setting an offer.

To conclude: it seems to me that you have completed offer-answer exchange and then are immediately trying to complete another one. My suggestion, log out which peer (A vs B) calls which method and in what sequence AND then compare the sequence to the algorithm I spelled out above. this will very likely point out the issue.

Upvotes: 3

Related Questions