diff --git a/webrtc/api/androidtests/src/org/webrtc/PeerConnectionTest.java b/webrtc/api/androidtests/src/org/webrtc/PeerConnectionTest.java index ebeeecd9a9..809603a3b4 100644 --- a/webrtc/api/androidtests/src/org/webrtc/PeerConnectionTest.java +++ b/webrtc/api/androidtests/src/org/webrtc/PeerConnectionTest.java @@ -10,6 +10,8 @@ package org.webrtc; +import org.webrtc.Metrics; +import org.webrtc.Metrics.HistogramInfo; import org.webrtc.PeerConnection.IceConnectionState; import org.webrtc.PeerConnection.IceGatheringState; import org.webrtc.PeerConnection.SignalingState; @@ -530,6 +532,7 @@ public class PeerConnectionTest extends ActivityTestCase { @MediumTest public void testCompleteSession() throws Exception { + Metrics.enable(); // Allow loopback interfaces too since our Android devices often don't // have those. PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); @@ -741,6 +744,7 @@ public class PeerConnectionTest extends ActivityTestCase { answeringExpectations.expectStateChange(DataChannel.State.CLOSED); answeringExpectations.dataChannel.close(); offeringExpectations.dataChannel.close(); + getMetrics(); // Free the Java-land objects, collect them, and sleep a bit to make sure we // don't get late-arrival crashes after the Java-land objects have been @@ -968,6 +972,18 @@ public class PeerConnectionTest extends ActivityTestCase { System.gc(); } + private static void getMetrics() { + Metrics metrics = Metrics.getAndReset(); + assertTrue(metrics.map.size() > 0); + // Test for example that the configured video codec is recorded when a + // VideoSendStream is created. + String name = "WebRTC.Video.Encoder.CodecType"; + assertTrue(metrics.map.containsKey(name)); + HistogramInfo info = metrics.map.get(name); + assertEquals(1, info.samples.size()); // samples: + assertTrue(info.samples.containsValue(2)); // , same codec configured + } + private static void shutdownPC( PeerConnection pc, ObserverExpectations expectations) { if (expectations.dataChannel != null) { diff --git a/webrtc/api/api.gyp b/webrtc/api/api.gyp index 377ef8f70b..ab1e0b3213 100644 --- a/webrtc/api/api.gyp +++ b/webrtc/api/api.gyp @@ -39,6 +39,7 @@ 'java/jni/androidmediadecoder_jni.h', 'java/jni/androidmediaencoder_jni.cc', 'java/jni/androidmediaencoder_jni.h', + 'java/jni/androidmetrics_jni.cc', 'java/jni/androidnetworkmonitor_jni.cc', 'java/jni/androidnetworkmonitor_jni.h', 'java/jni/androidvideocapturer_jni.cc', diff --git a/webrtc/api/java/jni/androidmetrics_jni.cc b/webrtc/api/java/jni/androidmetrics_jni.cc new file mode 100644 index 0000000000..c4b3fd1794 --- /dev/null +++ b/webrtc/api/java/jni/androidmetrics_jni.cc @@ -0,0 +1,59 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "webrtc/api/java/jni/classreferenceholder.h" +#include "webrtc/api/java/jni/jni_helpers.h" +#include "webrtc/api/java/jni/native_handle_impl.h" +#include "webrtc/system_wrappers/include/metrics_default.h" + +// Enables collection of native histograms. +namespace webrtc_jni { +JOW(void, Metrics_nativeEnable)(JNIEnv* jni, jclass) { + webrtc::metrics::Enable(); +} + +// Gets and clears native histograms. +JOW(jobject, Metrics_nativeGetAndReset)(JNIEnv* jni, jclass) { + jclass j_metrics_class = FindClass(jni, "org/webrtc/Metrics"); + jmethodID j_add = + GetMethodID(jni, j_metrics_class, "add", + "(Ljava/lang/String;Lorg/webrtc/Metrics$HistogramInfo;)V"); + jclass j_info_class = FindClass(jni, "org/webrtc/Metrics$HistogramInfo"); + jmethodID j_add_sample = GetMethodID(jni, j_info_class, "addSample", "(II)V"); + + // Create |Metrics|. + jobject j_metrics = jni->NewObject( + j_metrics_class, GetMethodID(jni, j_metrics_class, "", "()V")); + + std::map> + histograms; + webrtc::metrics::GetAndReset(&histograms); + for (const auto& kv : histograms) { + // Create and add samples to |HistogramInfo|. + jobject j_info = jni->NewObject( + j_info_class, GetMethodID(jni, j_info_class, "", "(III)V"), + kv.second->min, kv.second->max, + static_cast(kv.second->bucket_count)); + for (const auto& sample : kv.second->samples) { + jni->CallVoidMethod(j_info, j_add_sample, sample.first, sample.second); + } + // Add |HistogramInfo| to |Metrics|. + jstring j_name = jni->NewStringUTF(kv.first.c_str()); + jni->CallVoidMethod(j_metrics, j_add, j_name, j_info); + jni->DeleteLocalRef(j_name); + jni->DeleteLocalRef(j_info); + } + CHECK_EXCEPTION(jni); + return j_metrics; +} +} // namespace webrtc_jni diff --git a/webrtc/api/java/jni/classreferenceholder.cc b/webrtc/api/java/jni/classreferenceholder.cc index d6c7d567b6..d332e11711 100644 --- a/webrtc/api/java/jni/classreferenceholder.cc +++ b/webrtc/api/java/jni/classreferenceholder.cc @@ -69,6 +69,8 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/MediaSource$State"); LoadClass(jni, "org/webrtc/MediaStream"); LoadClass(jni, "org/webrtc/MediaStreamTrack$State"); + LoadClass(jni, "org/webrtc/Metrics"); + LoadClass(jni, "org/webrtc/Metrics$HistogramInfo"); LoadClass(jni, "org/webrtc/NetworkMonitor"); LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$ConnectionType"); LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$IPAddress"); diff --git a/webrtc/api/java/src/org/webrtc/Metrics.java b/webrtc/api/java/src/org/webrtc/Metrics.java new file mode 100644 index 0000000000..90209ad062 --- /dev/null +++ b/webrtc/api/java/src/org/webrtc/Metrics.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +import java.util.HashMap; +import java.util.Map; + +// Java-side of androidmetrics_jni.cc. +// +// Rtc histograms can be queried through the API, getAndReset(). +// The returned map holds the name of a histogram and its samples. +// +// Example of |map| with one histogram: +// |name|: "WebRTC.Video.InputFramesPerSecond" +// |min|: 1 +// |max|: 100 +// |bucketCount|: 50 +// |samples|: [30]:1 +// +// Most histograms are not updated frequently (e.g. most video metrics are an +// average over the call and recorded when a stream is removed). +// The metrics can for example be retrieved when a peer connection is closed. + +public class Metrics { + static { + System.loadLibrary("jingle_peerconnection_so"); + } + public final Map map = + new HashMap(); // + + /** + * Class holding histogram information. + */ + public static class HistogramInfo { + public final int min; + public final int max; + public final int bucketCount; + public final Map samples = + new HashMap(); // + + public HistogramInfo(int min, int max, int bucketCount) { + this.min = min; + this.max = max; + this.bucketCount = bucketCount; + } + + public void addSample(int value, int numEvents) { + samples.put(value, numEvents); + } + } + + private void add(String name, HistogramInfo info) { + map.put(name, info); + } + + // Enables gathering of metrics (which can be fetched with getAndReset()). + // Must be called before PeerConnectionFactory is created. + public static void enable() { + nativeEnable(); + } + + // Gets and clears native histograms. + public static Metrics getAndReset() { + return nativeGetAndReset(); + } + + private static native void nativeEnable(); + private static native Metrics nativeGetAndReset(); +}