
This transcript captures a technical walkthrough of the WebRTC RTCPeerConnection.getStats() API by Tsahi Levent-Levi and Philipp Hancke. They explain that getStats() returns a Promise resolving to a Map of RTCStats reports (each with id, type, timestamp and fields), describe how some fields act as pointers to other reports, and show two useful trees for analysis: the connectivity tree (transport → selected candidate pair → local/remote candidates) and the media tree (outbound/inbound RTP → remote RTP → codec → media source/playout). The discussion emphasizes practical guidance for locating the active transport/candidate pair and for comparing timestamps across calls to compute metric deltas.
Chapters
Philip, I think it is time that we go and look at Get Stats a bit and understand what exactly is returned from that function and how we're actually going to read that. I've been doing that for quite a lot of time and analysis part and monitoring and it all relies on or a lot of it relies on Get Stats. But at the end of the day, just preparing for this session taught me a few new things that I just didn't know were remember well. So I think it's a good time to start looking at it. So if I think about it, the first thing that we do is we go call Get Stats. When we do that, it returns a promise. It doesn't store everything and just gives it to us immediately. It needs to go inside WebRTC, take the data from different objects, collect it for us and then serve it to us.
Yes, that's a very heavy operation inside Library it. Basically this single call touches all objects in different parts on different threads. It's super complicated internally, but the JavaScript is just like a promise with an object.
Yeah, and I can do it on a single media stream check if I want to.
Yes, but I wouldn't do that because that is just taking the doing the same amount of effort internally and then filters it. Yeah.
Okay, so let's say we went into the peer connection called get stats, waited for the reply like we're doing here in this code example, and then the function returns. In this case it's show remotestats. It's not the one that we're actually showing on the right, because there is somewhere in between. We just took one of the samples of WebRTC, the official samples, and copy pasted the code from there into here. Now I get this results object. For me it's like it's a blob. I don't know what to do with it. What is it that I should do in order to figure out things inside Get Stats then?
Yes. So this results object is a map object, which means it's a key value thing where the key is an identifier id and the actual object it points to is something I tend to call a report, but the terminology is a bit confusing. So what we do in this JavaScript is we iterate over the results map and look at each report. Each report has a type, an id, which is also the IDE that is the index in the map. And it has a timestamp which is relative to January 1, 1970. It's Unix timestamp. You can put it into the date function and then you get a nicely printed date.
Is this the timestamp of when I actually called get stats, got the results, or got the measurement of the metric?
It is the timestamp at which the metric was sampled. Typically, it will be close to the current time, but you may also have the case where it is the time the thing was last gathered. Because objects don't simply disappear.
Okay.
But in the end, it doesn't really matter because you're taking this timestamp, comparing it to a previous timestamp for that entity, and then you calculate the difference. But we'll come to that in a bit.
Okay, so I've got this object, or let's say a map of objects. If I print it out, it would look something like this then, right?
Yes. Okay, see, id, timestamp, type. And then we have additional things.
Okay, so it's like the IDs are over here and these are their values. And then I've got the timestamp for each. It's the same for all of these, at least in this sample. And then there is a type. It can be a codec candidate pair, inbound rtp or something else. And then lots and lots of additional parameters for each one of them. Or just a few.
Yes. Okay, that's quite complicated. So we need to look at each individual type in detail.
Okay, so if I had to map that out for me, it would look something like that, right? It's like I've got different statistical objects. Each one is RTC stats. They have id, which is how I identify them. Inside this map, they have a timestamp and a type and then keys and values. Now, some of these keys might be an id, and that ID would actually go and point into a different RTC stats object within the same results. The result map that I got.
Yes, yes. One example for that is inbound rtp, which has a codec id, which tells you, this is a codec I'm receiving.
Okay. Now, the other way that I started looking at it, when I tried to look at all of the different objects that we have, there is to say, you know what, let's look at the types and let's try to build a structure, a kind of a tree of the dependencies between them, which IDs go and point to each others. And then you get this from the specification. So I've got the different stats like codec, inbound, rtp, outbound, rtp, remote, inbound, remote, outbound, all of these, each one of them has a dictionary, which is a list of fields that they have. So we marked in yellow the ones that are internal pointers. So you said that the inbound rtp, I think, has a remote ID that would point to the out remote outbound RTP object. Okay.
Also, it has a codec ID that inherits from the RTC RTP stream stats. You can see how this is built up. The definition of inbound RTP is built up from multiple dictionaries. It's a merger for all of them, which makes it complicated, but it's actually pleasant to work with.
Yeah. So it's kind of hard to grasp when you try to read it, but it's easy if you're actually trying to type the program. Like when you develop. It's easier to use it this way.
Yes.
Okay. Now I marked also in orange, the orange, the track identifier and the data channel identifier. Because these are not identifiers into the stats object itself.
Yes. So we have a name clash for these. These actually are identifiers that relate to other objects. Not in the stats, but in the case of the track identifier, the actual MediaStream track ID, you can't use them as a pointer, but you can use them for comparing things.
Okay. So we have all of these different types of fields. And I might have, for example, I might have multiple inbound RTP ones. One for audio, one for video, maybe few for audio and video on the same peer connection. So the way I distinguish between them is by their id, I might have multiple objects of the same type with a different ID to them.
Yes. And you can look up things in the map by that id.
Okay. Yes. Right. So then we went and said, let's look at it as a tree structure. And that brings us to a few trees that are interesting to look at. The first one is the one relating to connectivity. Right. So if I am trying to connect a peer connection in WebRTC and I want to look at the objects related to that for monitoring purposes, analysis, debugging, whatever. The one big object that I have there is transport.
Yes. That's the thing you're typically looking for first. If you're interested in anything related to connectivity, you go to the transport object.
And then find that from there I can get to certificate objects. Remote and local ones.
Yes. Those are not particularly useful, but they're there.
I can go from one certificate to another. Because there might be a hierarchy of certificates in there.
Yes, yes. You typically should avoid that hierarchy of remote certificates, but it's there.
Yes. Then the more interesting objects we connect to transport are actually the candidate pair.
Yes, that is pointed to by the selected candidate pair ID on the transport, which gives you the candidate pair that is being used. Because there are multiple of them. Some of them are not used, they still exist. But the transport tells you, this is the one I'm using. This is one you should be looking at.
And this is bidirectional. So the candidate pair would point to who each transport is and also back. But the back pointer is only to the selected one.
Yes. Yes.
So I might have a lot of different candidate pair objects inside my get stats return results, but only one of them would be pointed to from the transport.
Yes, which also explains why you're going to look for the transport and not for the candidate pair.
Because.
Because you have many of them. Which one do you pick? The transport object tells you.
Okay, now the candidate pair, because it's a pair, points to two different objects, the local candidate and the remote candidate.
And that lets you, for example, figure out the IP addresses, the type of the candidate. So is it a host candidate, is it the relay candidate? Pretty common use case.
And again, we're not trying to create arrays, which is why the local candidate doesn't point back to the candidate pair. Because it might be part of multiple different candidate pair objects.
Yes. So it's not a 1:1 relationship between local candidate and candidate pair.
Okay, so everything related to connectivity we can go and check on. Get stats from transport to candidate pair to local and remote candidates. Okay, let's move on to the media part. I got the things connected, now I want to check things. What's the media quality, for example? So I took the liberty of looking first on the outgoing media stream we've got the transport object again, to that we have something that points to it, which is an outbound rtp. Again, I can have multiple of those. For example, audio and video separately.
Yes.
The outbound RTP is essentially reporting back to the application. Look, this is what I'm lib WebRTC, or I'm the WebRTC library that you're using. Sending out. This is what I know about the things that I've sent out.
Yes, and you might even be sending it out on multiple transport objects. If you're not using bundle, then you would have multiple transports. You would know I'm sending these many bytes on this transportation, all audio on that transport.
Okay. And then we've got a remote inbound RTP object which is connected to the outbound one and they're linked. This is actually what the remote peer told me that he notices from the thing that I've sent him.
Yes, that comes from RTCP and the.
Receiver reports, okay, then on top of that we have the codec object.
Yes, that's basically, oh, I'm sending this payload type. This means I'm sending OPUS as a codec or H264, all VP8.
And on the outbound we also have the media source.
Yes, that's basically the object that's telling you this is where my frames come from. This is where my microphone comes from.
The microphone and the camera. Okay. Okay. On the inbound or the incoming with something similar but different, we've got transport and then inbound RTP connected to it. The other object would be the remote outbound rtp. So in both, RGB is what I actually got from the remote side. In remote outbound RTP is what the remote side told me that he sent me.
Yes. In rtcp, sender reports, okay, and then.
Again we have the connectivity to the codec and here we have an object connected for media playout that is only relevant for the audio.
Yes. Because for audio we know we have a single thing that is playing at the end of the day for the video. We can have the video from the same media stream displayed in multiple videos, and we don't have a single thing that says, oh, this is what we rendered. Yeah. Okay.
Now we have other different objects. They're like orphans from my point of view. Like the peer connection or the data channel there, you have a single peer connection object there. And you can have multiple data channels, one with additional metrics in them, but they're not that interesting in a way.
Yes. I mean, data channel is interesting if you're looking at statistics. How much did you send over which data channel? But I've never done that in practice.
I think when I thought about it and preparing for that, I thought about the data channel. And the reason it's not interesting, at least the way I see it, is because at the end of the day, the information that you get from the data channel, in terms of metrics, you already know.
Yes.
It's like I sent it, I received it, you're not going to tell me things like packet loss or jitter. If you got me this, that would be interesting to me, but it's not. So it's like I can calculate it on my own. So thank you for doing that. But it's just that, you know, that's about it.
Yes.
Okay.
Whereas, for example, for all the media, you don't even know how the purpose is. B, without get stats what is the target bitrate? We don't know. Get Set tells us.
Okay, so we've seen a very powerful API called Get Stats. We looked into it to understand how it is built and structured so that it would be easier for you to go and look at stuff in there. If you want to learn more about WebRTC, the things that we're doing, the courses that we have, the WebRTC insight service, come and follow us at Blogic Me.
Thank you, thank you.
More from Tsahi

Meta rebuilt AV1 for mobile scale. Should smaller teams follow? | Chats with Arin









