WebRTCBin: A question about how to obtain statistics

606 views
Skip to first unread message

Neil Young

unread,
Jul 8, 2019, 7:26:10 AM7/8/19
to gstreamer-java
Hi,


For a particular problem I now need to access the output of `get-stats`, which is currently not covered by the Java wrapper. In analogy to the `createOffer` and `createAnswer` realisations I tried to roll my own `getStats` and succeeded to get at least a reply.

private void getStats() {
Promise promise = new Promise(new Promise.PROMISE_CHANGE() {
public void onChange(Promise promise) {
Structure reply = promise.getReply();

Util.logger.trace(reply.toString());

promise.dispose();
}
});

webRTCBin.emit("get-stats", new Object[]{null, promise});
}



The trace line gives me this:

2019-07-08 13:23:22,327 WebRTCBinGStreamer$10 336 TRACE [GstBus]: application/x-webrtc-stats, peer-connection-stats=(structure)"peer-connection\,\ data-channels-opened\=\(uint\)0\,\ data-channels-closed\=\(uint\)0\,\ data-channels-requested\=\(uint\)0\,\ data-channels-accepted\=\(uint\)0\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_PEER_CONNECTION\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)peer-connection-stats\;", codec-stats-sink_0=(structure)"codec\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_CODEC\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)codec-stats-sink_0\,\ payload-type\=\(uint\)96\,\ clock-rate\=\(uint\)90000\,\ ssrc\=\(uint\)1353669487\;", transport-stats_webrtcdtlstransport0=(structure)"transport\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_TRANSPORT\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)transport-stats_webrtcdtlstransport0\;", ice-candidate-pair_webrtcnicetransport0=(structure)"transport\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_TRANSPORT\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)ice-candidate-pair_webrtcnicetransport0\;", codec-stats-src_0=(structure)"codec\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_CODEC\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)codec-stats-src_0\,\ payload-type\=\(uint\)100\,\ clock-rate\=\(uint\)90000\,\ ssrc\=\(uint\)1992815211\;", rtp-inbound-stream-stats_1992815211=(structure)"inbound-rtp\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_INBOUND_RTP\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)rtp-inbound-stream-stats_1992815211\,\ ssrc\=\(uint\)1992815211\,\ codec-id\=\(string\)codec-stats-src_0\,\ transport-id\=\(string\)transport-stats_webrtcdtlstransport0\,\ fir-count\=\(uint\)0\,\ pli-count\=\(uint\)0\,\ nack-count\=\(uint\)0\,\ packets-received\=\(guint64\)129\,\ bytes-received\=\(guint64\)102409\,\ packets-lost\=\(int\)0\,\ jitter\=\(double\)3.1948785117502877e-07\,\ remote-id\=\(string\)rtp-remote-outbound-stream-stats_1992815211\;", rtp-outbound-stream-stats_1992815211=(structure)"outbound-rtp\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_OUTBOUND_RTP\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)rtp-outbound-stream-stats_1992815211\,\ ssrc\=\(uint\)1992815211\,\ codec-id\=\(string\)codec-stats-src_0\,\ transport-id\=\(string\)transport-stats_webrtcdtlstransport0\,\ fir-count\=\(uint\)0\,\ pli-count\=\(uint\)0\,\ nack-count\=\(uint\)0\,\ bytes-sent\=\(guint64\)0\,\ packets-sent\=\(guint64\)0\,\ remote-id\=\(string\)rtp-remote-inbound-stream-stats_1992815211\;", rtp-remote-inbound-stream-stats_1992815211=(structure)"remote-inbound-rtp\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_REMOTE_INBOUND_RTP\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)rtp-remote-inbound-stream-stats_1992815211\,\ ssrc\=\(uint\)1992815211\,\ codec-id\=\(string\)codec-stats-src_0\,\ transport-id\=\(string\)transport-stats_webrtcdtlstransport0\,\ jitter\=\(double\)0\,\ packets-lost\=\(int\)0\,\ local-id\=\(string\)rtp-outbound-stream-stats_1992815211\,\ round-trip-time\=\(double\)0.0075836181640625\;", rtp-remote-outbound-stream-stats_1992815211=(structure)"remote-outbound-rtp\,\ type\=\(GstWebRTCStatsType\)GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP\,\ timestamp\=\(double\)757152.97699999996\,\ id\=\(string\)rtp-remote-outbound-stream-stats_1992815211\,\ ssrc\=\(uint\)1992815211\,\ codec-id\=\(string\)codec-stats-src_0\,\ transport-id\=\(string\)transport-stats_webrtcdtlstransport0\,\ local-id\=\(string\)rtp-inbound-stream-stats_1992815211\;";


I'm now looking for a way to decode this into an object. I tried an analogue approach like the `createAnswer` API:

Structure reply = promise.getReply();
WebRTCSessionDescription description = (WebRTCSessionDescription)reply.getValue("answer");
listener.onAnswerCreated(description);
promise.dispose();

by attempting

Structure reply = promise.getReply();
Object o = reply.getValue("peer-connection-stats");

but o is alway NULL. Tried various other things to no avail. 

Anybody able to provide a hint, how to make it a manageable structure/object?

TIA


Neil Young

unread,
Jul 8, 2019, 10:23:19 AM7/8/19
to gstreamer-java
This is really weird. Nobody?

For reply.getFields(); I get "9", which is probably the number of structures.

For reply.getName(0); I'm getting "peer-connection-stats".

For reply.getValue(reply.getName(0)); the result is null.

For reply.getValues(Object.class, getName(0)) I'm getting "Structure does not contain Object field 'peer-connection-stats'"

No idea, how to parse that. 

Neil C Smith

unread,
Jul 8, 2019, 10:32:27 AM7/8/19
to gstream...@googlegroups.com
On Mon, 8 Jul 2019 at 15:23, 'Neil Young' via gstreamer-java
<gstream...@googlegroups.com> wrote:
>
> This is really weird. Nobody?

Was just looking at this. We don't have mappers to Java objects for
everything that can be contained in a GValue. You might need to delve
around lowlevel and GValueAPI a little here for now. This whole
aspect needs a rewrite from what we inherited from the 0.10 bindings.

At a look it seems this is a structure within a structure? Maybe step
through in a debugger and see exactly what's going on. Particularly
in this https://github.com/gstreamer-java/gst1-java-core/blob/master/src/org/freedesktop/gstreamer/lowlevel/GValueAPI.java#L152

Best wishes,

Neil



--
Neil C Smith
Artist & Technologist
www.neilcsmith.net

PraxisLIVE - hybrid visual live programming
for creatives, for programmers, for students, for tinkerers
www.praxislive.org

Neil Young

unread,
Jul 8, 2019, 10:35:54 AM7/8/19
to gstreamer-java
Thanks again, Neil. Did that already to no avail. Even in a debugger it is hard to bite. Casts over casts, don't get it.

Anyway. My GStreamer attempts will end here. Thanks to all.

Regard

Neil C Smith

unread,
Jul 8, 2019, 10:38:36 AM7/8/19
to gstream...@googlegroups.com
On Mon, 8 Jul 2019 at 15:35, 'Neil Young' via gstreamer-java
<gstream...@googlegroups.com> wrote:
>
> Thanks again, Neil. Did that already to no avail. Even in a debugger it is hard to bite. Casts over casts, don't get it.
>
> Anyway. My GStreamer attempts will end here. Thanks to all.

Fair enough. That's a shame. It's a great tech when you get into it.

Neil Young

unread,
Jul 8, 2019, 10:40:55 AM7/8/19
to gstreamer-java
I agree, but for me it is just one stone to roll on a road full of stones ahead. And I have some alternatives, as I told you. I gave it a chance, but had a handful of issues, which all are not there with other solutions.

Thanks again. Will miss your profound comments :)

Neil Young

unread,
Jul 10, 2019, 5:30:38 AM7/10/19
to gstreamer-java
OK, after a day of successful fighting my frustrations I resumed today and this is the receipt:

This is the Java function, which needs to be called periodically in order to obtain and understand the statistics:

The sample just concentrates on the rtp_inbound_stream_stats, but there is more.

Note: To obtain an integer from the structure did not work for me (commented). It always excepts with

[GstBus] ERROR WebrtcSendRecv - org.freedesktop.gstreamer.Structure$InvalidFieldException: Structure does not contain integer field 'pli-count'

Maybe because it is an UINT.

Also I could not directly access a structure within a structure. 


private void getStats() {
Promise promise = new Promise(new Promise.PROMISE_CHANGE() {
public void onChange(Promise promise) {
Structure reply = promise.getReply();
for (int i=0; i < reply.getFields(); i++) {
String stat_name = reply.getName(i);
if (stat_name.startsWith("rtp-inbound-stream-stats_")) {
Structure rtp_inbound_stream_stats = GstStructureAPI.GSTSTRUCTURE_API.gst_structure_from_string(GstStructureAPI.GSTSTRUCTURE_API.gst_structure_get_value(reply, stat_name).toString(), null);
try {
Integer packets_received = Integer.parseInt(rtp_inbound_stream_stats.getValue("packets-received").toString());
Integer packets_lost = Integer.parseInt(rtp_inbound_stream_stats.getValue("packets-lost").toString());
//Integer pli_count = rtp_inbound_stream_stats.getInteger("pli-count");
logger.info("packets_received {}, packets_lost {}", packets_received, packets_lost);
} catch (Exception e) {
logger.error(e.toString());
}
}
}
promise.dispose();
}
});

webRTCBin.emit("get-stats", new Object[] { null, promise });
}

HTH
Reply all
Reply to author
Forward
0 new messages