Neither Chrome nor Firefox quite conforms to the spec for WebRTC statistics, nor is the spec done evolving. I’ll try to document some of their peculiarities here and to update this post when changes arise.

###API

The spec says RTCPeerConnection.getStats takes a nullable MediaStreamTrack, a success callback, and a failure callback, in that order. The success callback is passed an RTCStatsReport which has a getter for accessing its various RTCStats by ID.

Chrome reverses the order of the first two parameters, and its success callback is passed a RTCStatsResponse whose result method returns a list of RTCStatReport objects. Chrome’s RTCStatsReport does not correspond to the spec’s type of the same name, but to its RTCStats; it is just a dictionary.

Firefox matches the spec; but the callbacks are optional, and if both callbacks are omitted then a Promise is returned. Presumably the spec will eventually prefer Promises as it already does for other methods.

The two APIs can be unified with the following code:

function getStats(pc, selector) {
	if (navigator.mozGetUserMedia) {
		return pc.getStats(selector);
	}
	return new Promise(function(resolve, reject) {
		pc.getStats(function(response) {
			var standardReport = {};
			response.result().forEach(function(report) {
				var standardStats = {
					id: report.id,
					type: report.type
				};
				report.names().forEach(function(name) {
					standardStats[name] = report.stat(name);
				});
				standardReport[standardStats.id] = standardStats;
			});
			resolve(standardReport);
		}, selector, reject);
	});
}

###Stats

Each RTCStats (or RTCStatsReport in Chrome) object is a dictionary with three default attributes: timestamp, type, and id; and various other attributes: some required, depending on type, and others that are implementation specific.

Firefox does not include timestamp.

The spec defines various stats report types. Instead of outboundrtp and inboundrtp, Chrome uses ssrc. The exact type can be inferred from the presence of attributes such as bytesSent vs bytesReceived.

The spec decrees that roundtriptime shall be defined on an RTPOutboundRTPStreamStats object. Google calls this googRtt. Firefox calls it mozRtt and puts it on the inbound stats rather than the outbound.

One of the more common bugs that Go programmers write involves shadowing an error variable, as in the following code (taken from this relevant blog post):

var err error
if isSecure {
	config, err := GetTLSConfig()
	if err != nil {
		return err
	}
	conn, err = tls.Dial("tcp", server + ":443", &config) // BUG
} else {
	conn, err = net.Dial("tcp", server)
}
if err != nil {
	return err
}
// use conn...

The intention of the line marked // BUG is to assign to the outermost declared err. However, the err declared in the if-block shadows the outer err, rendering the assignment useless. An error returned from tls.Dial will never reach the final if err != nil check. Not good.

I have on multiple occasions seen people bitten by this type of bug, but it wasn’t until a week ago that I realized that the compiler’s failure to detect it was probably unintentional. Go is kind enough to tell us when we declare but do not use a variable but, strangely, it doesn’t say anything about assigned and thereafter unused variables, which is our situation above.

I pointed out this omission in Issue #10989; and Robert Griesemer, while agreeing that it would be good to flag such “assigned and not used” errors, confirmed my suspicion that it would constitute a backwards-incompatible language change, and thus probably not find its way into the compilers.

Nevertheless, I had a hunch that it would highlight only buggy code, which might justify breaking such code. I decided to investigate. The convenience of the Go standard library’s go/ast package enabled me to quickly whip together a tool that approximates the desired behavior.

The implementation was fairly straightforward. I won’t go into details; the essential observations are of the scopes in which variables are declared and used, especially regarding loops, and of whether a variable can escape (by taking its address or being referenced in a function literal (closure)) and possibly be assigned later. I would later find that Alan Donovan had given a good lowdown of the problem when a nearly identical issue was filed a year and a half ago.

Running my tool against the standard library and a few packages in my GOPATH flagged 72 “assigned and not used” errors. A perusal revealed most of them to be innocuous (but sloppy) code, and 7 of them to be real errors. Not enough to justify a breaking language change, I thought. But when I posted my findings on the Go mailing list, Rob Pike made it clear that he thought the compiler should do this check.

We shall see. But honestly, I don’t see how this could happen without flagrantly violating the Go 1 Compatibility Guarantee.

In the past years I have been speaking at a variety of events, meetups and conferences, but for a JavaScript focused developer there’s only one mother of all conferences: JSConf.us. Originally started in 2009, a time before node.js was released, and where jQuery was the hottest library; it has grown to be the de-facto conference for everyone in the JS world, with sister conferences all over the world. In 2014 I already had the wonderful opportunity to speak at JSConf EU (video) and JSConf.Asia (video), and this year I was invited to complete the trio and speak at the 2015 edition of JSConf.us in Amelia Island, Florida!

Continue reading »

Getting started with WebRTC on iOS

Hangouts does it. Facebook Messenger does it. We at appear.in do it. We all use WebRTC for client to client video conversations in our iOS apps, and so can you!

Introduction to WebRTC on Android

In this blog post we will investigate how you can get started with building WebRTC into your Android apps, using the native libraries provided by the WebRTC Initiative.