Weird issue with Chrome/android and rtpcapabilities after device load

I have a strange issue I wonder if anyone has come across. On my web client when accessed using android and chrome, the initial call to device.load() provides only the opus audio codec in the device.rtpcapabilities. However after a refresh of the page, I get a full codec list in the device.rtpcapabilities. Because of this I can’t do any video until the page is refreshed, which is not an ideal solution, but the only one I can find that works.

if codec count < 2 then window.location.reload();

Since I can’t call device.load() a second time, I don’t know any other way to resolve it. I have tried to delay the call until the user presses a button on the page, but this did not resolve the issue.

I realize this is probably a chrome issue, but wondering if anyone else has seen this or there is a known workaround that anyone has come up with.

1 Like

Have you solved the issue? I have the same one with Firefox and it’s pretty strange. I get router rtp capabilities with 2 video codecs, then send it to device.load({routerRtpCapabilities})
Снимок экрана 2020-08-07 в 15.11.12
and then try to get device.rtpCapabilities, but actually receive headerExtensions with codecs array empty
Снимок экрана 2020-08-07 в 15.12.44

Well, that may happen if the given codecs in routerRtpCapabilities are not supported by Firefox, right? I mean: that’s exactly what mediasoup-client does, verify which codecs are supported and build a proper local rtpCapabilities.

Then you just complain because codecs array is empty. Well, yes it may be based on given input, but we don’t know the input. Try passing vp8 codec and it will work.

Yes, I know, you want H264. Then set a H264 profile supported by Firefox into router codecs.

Well, but it works for the second time, so I guess it doesn’t mean that routerRtpCapabilities or profile are not supported by Firefox, does it?

Probably. No idea why it does not work for the first time. It seems a Firefox issue.

I don’t think so. The author of this issue mentioned he has the same problems in Chrome.

So do you mean this is a bug in mediasoup-client?

He said android chrome, right? Well, maybe a big in Android chrome. Just try by creating a PeerConnection with receive audio/video and see how the SDP offer looks like. That’s what mediasoup-client does in order to retrieve capabilities.

Yes, my issue is with android and chrome. I have not tried firefox. I will. Good to know I am not the only one though. I will look into the idea of creating a peerconnection directly and see if that gives different result.

My solution at the moment is to detect when this occurs, set a cookie and do a page refresh. If it happens again (by checking cookie) I give up. But it always seems to get it the second time.

So here is what I found. Indeed the first call to RTCPeerConnection.createOffer is missing the H.264 codec. On the second call, magic… Its there…

So just calling this function before Device.Load() resolves the issue.
Way better than doing a refresh.

    async function checkCaps() {
        var pc = new (RTCPeerConnection)(
            {
                iceServers: [],
                iceTransportPolicy: 'all',
                bundlePolicy: 'max-bundle',
                rtcpMuxPolicy: 'require',
                sdpSemantics: 'unified-plan'
            });

        pc.addTransceiver('audio');
        pc.addTransceiver('video');

        var offer = await pc.createOffer();
        console.log("----------------------------------");
        console.log(offer.sdp);
        console.log("----------------------------------");
    }

Here is the sdp result on the fist call.

v=0
o=- 6787245077593504149 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=msid-semantic: WMS
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 9 0 8 105 13 110 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:wP3U
a=ice-pwd:WODSvU8fa2jmSOZyaDrrS6DV
a=ice-options:trickle
a=fingerprint:sha-256 1E:65:C8:E0:E4:18:B9:D0:F6:C3:E7:B5:47:26:AB:95:0A:0F:8B:8E:E3:2A:60:F2:D0:9C:E6:0E:B9:7D:0E:E8
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:- 36cd7a10-6498-467a-8f1d-ebffef097245
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:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:4269080135 cname:/fHPOuZpuOG5LlBn
a=ssrc:4269080135 msid:- 36cd7a10-6498-467a-8f1d-ebffef097245
a=ssrc:4269080135 mslabel:-
a=ssrc:4269080135 label:36cd7a10-6498-467a-8f1d-ebffef097245
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 102 127 104
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:wP3U
a=ice-pwd:WODSvU8fa2jmSOZyaDrrS6DV
a=ice-options:trickle
a=fingerprint:sha-256 1E:65:C8:E0:E4:18:B9:D0:F6:C3:E7:B5:47:26:AB:95:0A:0F:8B:8E:E3:2A:60:F2:D0:9C:E6:0E:B9:7D:0E:E8
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:11 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07
a=extmap:9 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:- e1cf4f6f-0f82-4d69-bf92-35d1bed683cd
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:102 red/90000
a=rtpmap:127 rtx/90000
a=fmtp:127 apt=102
a=rtpmap:104 ulpfec/90000
a=ssrc-group:FID 2622048700 3584009422
a=ssrc:2622048700 cname:/fHPOuZpuOG5LlBn
a=ssrc:2622048700 msid:- e1cf4f6f-0f82-4d69-bf92-35d1bed683cd
a=ssrc:2622048700 mslabel:-
a=ssrc:2622048700 label:e1cf4f6f-0f82-4d69-bf92-35d1bed683cd
a=ssrc:3584009422 cname:/fHPOuZpuOG5LlBn
a=ssrc:3584009422 msid:- e1cf4f6f-0f82-4d69-bf92-35d1bed683cd
a=ssrc:3584009422 mslabel:-
a=ssrc:3584009422 label:e1cf4f6f-0f82-4d69-bf92-35d1bed683cd

And on the second…

v=0
o=- 4640706164128443506 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=msid-semantic: WMS
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 9 0 8 105 13 110 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:YHPe
a=ice-pwd:Em/03PUbxL6hYbpGIsOG7DQw
a=ice-options:trickle
a=fingerprint:sha-256 5E:9F:0C:91:45:D3:5E:D6:92:50:29:3D:BE:85:F6:EC:01:7E:AA:78:4B:CF:E2:F8:9C:23:61:5A:0A:DD:DF:F6
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:- 6cd784b0-3cbf-4d32-9042-2ddd9cfe512c
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:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:3551797276 cname:LCj8HKMHJQoZ15pZ
a=ssrc:3551797276 msid:- 6cd784b0-3cbf-4d32-9042-2ddd9cfe512c
a=ssrc:3551797276 mslabel:-
a=ssrc:3551797276 label:6cd784b0-3cbf-4d32-9042-2ddd9cfe512c
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 104
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:YHPe
a=ice-pwd:Em/03PUbxL6hYbpGIsOG7DQw
a=ice-options:trickle
a=fingerprint:sha-256 5E:9F:0C:91:45:D3:5E:D6:92:50:29:3D:BE:85:F6:EC:01:7E:AA:78:4B:CF:E2:F8:9C:23:61:5A:0A:DD:DF:F6
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:11 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07
a=extmap:9 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:- 33cbe64d-e7d0-4ed4-940a-5d9b126e0224
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 H264/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:102 red/90000
a=rtpmap:123 rtx/90000
a=fmtp:123 apt=102
a=rtpmap:104 ulpfec/90000
a=ssrc-group:FID 2425285139 887086633
a=ssrc:2425285139 cname:LCj8HKMHJQoZ15pZ
a=ssrc:2425285139 msid:- 33cbe64d-e7d0-4ed4-940a-5d9b126e0224
a=ssrc:2425285139 mslabel:-
a=ssrc:2425285139 label:33cbe64d-e7d0-4ed4-940a-5d9b126e0224
a=ssrc:887086633 cname:LCj8HKMHJQoZ15pZ
a=ssrc:887086633 msid:- 33cbe64d-e7d0-4ed4-940a-5d9b126e0224
a=ssrc:887086633 mslabel:-
a=ssrc:887086633 label:33cbe64d-e7d0-4ed4-940a-5d9b126e0224
1 Like

This is a good catch, but don’t assume that’s the “solution”. It’s an obvious bug in Chrome for Android that should be reported to Chrome.

Agreed, more of a ‘workaround’ than a proper solution. I am going to verify the issue still exists in the latest dev branch. If it does I will report it. Thanks for all the help.

Thank you Mike, great trick!

Some additional context here:

  • This problem occurs typically with Android WebViews, which are based on Chromium and not Chrome.
  • Chromium, contrary to Chrome, is built without the proprietary codecs enabled, notably without H264 (see rtc_use_h264 flag).
  • However on Android the “MediaCodec” and “OMX” libs are able to support H264 software decoding, as long as it’s a baseline profile => using profile-level-id: '42e01f' is the safest bet.

I’ve noticed that due to this external lib kicking-in, there is an async problem that explains why it does not work the first time.

I’ve also seen cases where RTCPeerConnection + mediasoup device loading is not enough.

The strategy I ended up using is:

  1. Create and load mediasoup Device
  2. If msDevice.rtpCapabilities.codecs.find() does not find the codec, create the fake RTCPeerConnection. Otherwise we’re good.
  3. Create and load another mediasoup Device from scratch.

Please note for the fake RTCPeerConnection that you can strip most of the config options, writing simply:

const peerCo = new RTCPeerConnection();
peerCo.addTransceiver('video');
const offer = await peerCo.createOffer();

Thanks again for the great trick, and to the MediaSoup team for the great lib!