Working around WebRTC bugs
WebRTC bugs… my favorite topic as you may have just seen at ClueCon. One of the questions I got was whether I keep knowledge of those bugs to myself. Well… I try not to. In the spirit of full disclosure here is how we at appear.in work around two of the more annoying Chrome bugs right now.
Failure to acquire the microphone on OSX
On MacOSX (and probably also on Windows), Chrome sometimes can not acquire data from the microphone. The getUserMedia call will succeed but the microphone doesn’t capture any data. This issue has been around for a while. It typically results in “the other side can’t hear me” style issues.
It was hoped to be fixed in Chrome 52 which was released recently. Well… my friend Gustavo Garcia brought bad news. It still happens and probably is going to be an issue for quite some more time.
I had seen this bug before and it was rather easy to see in the statistics. The round trip time for audio remained -1. Yay, we can detect the problem, maybe we tell users how to solve it by restarting their browser. Unfortunately, Chrome 52 removed all those stats that were -1.
Fortunately, Gustavo knew another way to detect this. The bytesSent value on the (google-specific) stats report with mediatype=audio and will remain 0 all the time. So once your peerconnection is up, start a call-out (maybe 3 second) and check whether any audio bytes have been sent:
pc.getStats(null).then(function (result) {
Object.keys(result).forEach(function(id) {
var report = result[id];
if (id.indexOf('_send') !== -1 &&
report.type === 'ssrc' &&
report.mediaType === 'audio' &&
parseInt(report.bytesSent, 10) === 0) {
// show a warning
}
});
});
As of Chrome 58 you need different code because in that version Chrome started exposing spec-style getStats. This should work:
result.forEach((report) => {
if (report.type === 'outbound-rtp' &&
report.mediaType === 'audio' &&
report.bytesSent === 0) {
// show a warning
}
});
That warning could advise the user to restart the browser which should fix the issue. The other option to tackle this is a “haircheck” screen which shows the microphone volume like talky.io does (which uses a very useful webaudio tool called hark).
While this doesn’t fix the problem, it reduces the impact on users.
The camera is used by another application
Sometimes, your camera is used by another application. When testing, I often have both Chrome and Firefox open and both browsers try to access the camera. Firefox throws an InternalError, Chrome allows camera access but never displays or sends any video. This bug has also been known for quite a while (pro-tip: doing triage in the bug tracker pays off). On the other side of the peerconnection this may have some more serious issues like not playing audio at all (sounds confusing? See here for the full story)
Again, this is rather easy to detect. Shortly after the getUserMedia success callback, check whether the MediaStreamTrack’s readyState attribute is ‘ended’ like this:
var track = stream.getVideoTracks()[0];// not useful for desktop sharing
if (track.label === ‘Screen’) return;
if (track.readyState === ‘ended’) {
// show a warning
}
Six lines, very simple code, but quite high user impact. Just do it.
Chrome 59 changed the behaviour and now returns a TrackStartError which makes this a lot easier to catch! Essentially you do this:
navigator.mediaDevices.getUserMedia({video: true})
.then(stream => {
// do your thing with the stream
}, (e) => {
if (e.name === 'TrackStartError') {
// show same warning as before
});
which allows you to handle this error in the same place where the rest of your error handling lives. Note that the error should actually be a NotReadableError. Hopefully this can be fixed before M59 ships.