2
votes

I've created a very basic WebRTC video chat interface (Chrome only test). I'm signaling the SDP over websockets, and using Twilio service for the STUN / TURN configuration.

The "offer answer dance" seems to be working fine and I get the remote stream (identified with the id) and the blob url is added to the video tag:

<video controls="true" autoplay="autoplay" width="640" height="480" src="blob:http%3A//localhost%3A3000/76ea7cb1-3ded-4c8d-b486-ea8856fe259e"></video>

However the view is empty.


Here is how I answer an incoming offer:

navigator.getUserMedia({video: true, audio: true}, function (stream) {
                        console.log("Stream Created:", stream.id);
                        pc.addStream(stream);
                        pc.setRemoteDescription(new RTCSessionDescription({type: "offer", sdp: sdp}), function () {
                            console.log("Remote description set");
                            pc.createAnswer(function (answer) {
                                pc.setLocalDescription(answer, function () {
                                    // send the answer to a server to be forwarded back to the caller 
                                    ws.send(JSON.stringify({action: "answer", to: callerID, from:myId, message: answer.sdp}));
                                }, error);
                            }, error);
                        }, error);
                    });

And here is the onaddstream function:

pc.onaddstream = function(e){
        console.log("Stream Added:", e.stream.id);
        $("video")[0].src = URL.createObjectURL(e.stream);
        $("video")[0].play();
    };

EDIT added SDP:

Offer SDP v=0
    o=- 69925770258960282 2 IN IP4 127.0.0.1
    s=-
        t=0 0
    a=group:BUNDLE audio video
    a=msid-semantic: WMS sY6R8GGwtIDPcK4uDv3W66jBseIXZvAq2Nbp
    m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126
    c=IN IP4 0.0.0.0
    a=rtcp:9 IN IP4 0.0.0.0
    a=ice-ufrag:ADj3FEdod3ks08kE
    a=ice-pwd:3W5QU38oyiB2Sj7EZql8p8Gn
    a=fingerprint:sha-256 ED:D2:A4:8C:E0:5F:38:25:D3:D9:86:FC:BD:7C:1E:5C:DE:DE:6D:01:75:DA:3A:8F:F5:3E:27:83:D2:FD:DF:FD
    a=setup:actpass
    a=mid:audio
    a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
    a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
        a=sendrecv
    a=rtcp-mux
    a=rtpmap:111 opus/48000/2
    a=rtcp-fb:111 transport-cc
    a=fmtp:111 minptime=10; useinbandfec=1
    a=rtpmap:103 ISAC/16000
    a=rtpmap:104 ISAC/32000
    a=rtpmap:9 G722/8000
    a=rtpmap:0 PCMU/8000
    a=rtpmap:8 PCMA/8000
    a=rtpmap:106 CN/32000
    a=rtpmap:105 CN/16000
    a=rtpmap:13 CN/8000
    a=rtpmap:126 telephone-event/8000
    a=maxptime:60
    a=ssrc:349223883 cname:07HrC4w/vFw0loFv
    a=ssrc:349223883 msid:sY6R8GGwtIDPcK4uDv3W66jBseIXZvAq2Nbp 282288d1-5b9a-4912-9ced-f6188b33139e
    a=ssrc:349223883 mslabel:sY6R8GGwtIDPcK4uDv3W66jBseIXZvAq2Nbp
    a=ssrc:349223883 label:282288d1-5b9a-4912-9ced-f6188b33139e
    m=video 9 UDP/TLS/RTP/SAVPF 100 101 116 117 96 97 98
    c=IN IP4 0.0.0.0
    a=rtcp:9 IN IP4 0.0.0.0
    a=ice-ufrag:ADj3FEdod3ks08kE
    a=ice-pwd:3W5QU38oyiB2Sj7EZql8p8Gn
    a=fingerprint:sha-256 ED:D2:A4:8C:E0:5F:38:25:D3:D9:86:FC:BD:7C:1E:5C:DE:DE:6D:01:75:DA:3A:8F:F5:3E:27:83:D2:FD:DF:FD
    a=setup:actpass
    a=mid:video
    a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
    a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
        a=extmap:4 urn:3gpp:video-orientation
    a=sendrecv
    a=rtcp-mux
    a=rtpmap:100 VP8/90000
    a=rtcp-fb:100 ccm fir
    a=rtcp-fb:100 nack
    a=rtcp-fb:100 nack pli
    a=rtcp-fb:100 goog-remb
    a=rtcp-fb:100 transport-cc
    a=rtpmap:101 VP9/90000
    a=rtcp-fb:101 ccm fir
    a=rtcp-fb:101 nack
    a=rtcp-fb:101 nack pli
    a=rtcp-fb:101 goog-remb
    a=rtcp-fb:101 transport-cc
    a=rtpmap:116 red/90000
    a=rtpmap:117 ulpfec/90000
    a=rtpmap:96 rtx/90000
    a=fmtp:96 apt=100
    a=rtpmap:97 rtx/90000
    a=fmtp:97 apt=101
    a=rtpmap:98 rtx/90000
    a=fmtp:98 apt=116
    a=ssrc-group:FID 2207775072 661034986
    a=ssrc:2207775072 cname:07HrC4w/vFw0loFv
    a=ssrc:2207775072 msid:sY6R8GGwtIDPcK4uDv3W66jBseIXZvAq2Nbp 4b45d0b6-877f-400e-8e3d-42cefcd8808e
    a=ssrc:2207775072 mslabel:sY6R8GGwtIDPcK4uDv3W66jBseIXZvAq2Nbp
    a=ssrc:2207775072 label:4b45d0b6-877f-400e-8e3d-42cefcd8808e
    a=ssrc:661034986 cname:07HrC4w/vFw0loFv
    a=ssrc:661034986 msid:sY6R8GGwtIDPcK4uDv3W66jBseIXZvAq2Nbp 4b45d0b6-877f-400e-8e3d-42cefcd8808e
    a=ssrc:661034986 mslabel:sY6R8GGwtIDPcK4uDv3W66jBseIXZvAq2Nbp
    a=ssrc:661034986 label:4b45d0b6-877f-400e-8e3d-42cefcd8808e

What am I missing?

1
Any error messages? - Might not help, but I recommend calling setRemoteDescription right away, before getUserMedia, otherwise that peer might miss out on ice candidates. You're doing pc.onicecandidate right?jib
Thanks for the reply, no error messages. My mistake was sending an offer without the candidatesShlomi Schwartz

1 Answers

3
votes

The SDP wont initially have any ICE candidates in it, and you seem to be missing code to - and make no mention of - trickle ICE candidates over your signaling channel as they become available:

pc.onicecandidate = e => yourSignaling.send({ ice: e.candidate });

and on the other end:

var incoming = msg => pc.addIceCandidate(msg.ice).catch(log);

See Sending ICE candidates on MDN.

Also, on receiving an offer, call setRemoteDescription ASAP, before getUserMedia, otherwise that peer is not ready to receive ICE candidates, which may be arriving immediately after the offer.