From 82215872f8f75410366cb5cb986e816fe8f77364 Mon Sep 17 00:00:00 2001 From: deadbeef Date: Tue, 18 Apr 2017 10:27:51 -0700 Subject: [PATCH] Add Java binding for new getStats implementation. Very similar to the current interface, but matches the new C++ structure, and exposes the stats values as Objects which can be downcast to more specific types (where the previous API only exposed the values as strings). BUG=webrtc:6871 Review-Url: https://codereview.webrtc.org/2807933003 Cr-Commit-Position: refs/heads/master@{#17746} --- webrtc/sdk/android/BUILD.gn | 5 + .../api/org/webrtc/PeerConnection.java | 14 +- .../sdk/android/api/org/webrtc/RTCStats.java | 105 +++++++ .../org/webrtc/RTCStatsCollectorCallback.java | 17 ++ .../api/org/webrtc/RTCStatsReport.java | 54 ++++ .../src/org/webrtc/PeerConnectionTest.java | 46 ++- .../android/src/jni/classreferenceholder.cc | 9 + .../sdk/android/src/jni/peerconnection_jni.cc | 13 +- .../jni/rtcstatscollectorcallbackwrapper.cc | 267 ++++++++++++++++++ .../jni/rtcstatscollectorcallbackwrapper.h | 65 +++++ 10 files changed, 582 insertions(+), 13 deletions(-) create mode 100644 webrtc/sdk/android/api/org/webrtc/RTCStats.java create mode 100644 webrtc/sdk/android/api/org/webrtc/RTCStatsCollectorCallback.java create mode 100644 webrtc/sdk/android/api/org/webrtc/RTCStatsReport.java create mode 100644 webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.cc create mode 100644 webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.h diff --git a/webrtc/sdk/android/BUILD.gn b/webrtc/sdk/android/BUILD.gn index 12ebb8358a..a9137222a1 100644 --- a/webrtc/sdk/android/BUILD.gn +++ b/webrtc/sdk/android/BUILD.gn @@ -42,6 +42,8 @@ rtc_static_library("libjingle_peerconnection_jni") { "src/jni/native_handle_impl.cc", "src/jni/native_handle_impl.h", "src/jni/peerconnection_jni.cc", + "src/jni/rtcstatscollectorcallbackwrapper.cc", + "src/jni/rtcstatscollectorcallbackwrapper.h", "src/jni/surfacetexturehelper_jni.cc", "src/jni/surfacetexturehelper_jni.h", ] @@ -159,6 +161,9 @@ android_library("libjingle_peerconnection_java") { "api/org/webrtc/PeerConnection.java", "api/org/webrtc/PeerConnectionFactory.java", "api/org/webrtc/RendererCommon.java", + "api/org/webrtc/RTCStats.java", + "api/org/webrtc/RTCStatsCollectorCallback.java", + "api/org/webrtc/RTCStatsReport.java", "api/org/webrtc/RtpParameters.java", "api/org/webrtc/RtpReceiver.java", "api/org/webrtc/RtpSender.java", diff --git a/webrtc/sdk/android/api/org/webrtc/PeerConnection.java b/webrtc/sdk/android/api/org/webrtc/PeerConnection.java index 4e91a1141c..8de7f34d9e 100644 --- a/webrtc/sdk/android/api/org/webrtc/PeerConnection.java +++ b/webrtc/sdk/android/api/org/webrtc/PeerConnection.java @@ -265,8 +265,16 @@ public class PeerConnection { return Collections.unmodifiableList(receivers); } + // Older, non-standard implementation of getStats. + @Deprecated public boolean getStats(StatsObserver observer, MediaStreamTrack track) { - return nativeGetStats(observer, (track == null) ? 0 : track.nativeTrack); + return nativeOldGetStats(observer, (track == null) ? 0 : track.nativeTrack); + } + + // Gets stats using the new stats collection API, see webrtc/api/stats/. These + // will replace old stats collection API when the new API has matured enough. + public void getStats(RTCStatsCollectorCallback callback) { + nativeNewGetStats(callback); } // Starts recording an RTC event log. Ownership of the file is transfered to @@ -328,7 +336,9 @@ public class PeerConnection { private native void nativeRemoveLocalStream(long nativeStream); - private native boolean nativeGetStats(StatsObserver observer, long nativeTrack); + private native boolean nativeOldGetStats(StatsObserver observer, long nativeTrack); + + private native void nativeNewGetStats(RTCStatsCollectorCallback callback); private native RtpSender nativeCreateSender(String kind, String stream_id); diff --git a/webrtc/sdk/android/api/org/webrtc/RTCStats.java b/webrtc/sdk/android/api/org/webrtc/RTCStats.java new file mode 100644 index 0000000000..c7035a129b --- /dev/null +++ b/webrtc/sdk/android/api/org/webrtc/RTCStats.java @@ -0,0 +1,105 @@ +/* + * Copyright 2017 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.Map; + +/** + * Java version of webrtc::RTCStats. Represents an RTCStats object, as + * described in https://w3c.github.io/webrtc-stats/. The |id|, |timestampUs| + * and |type| accessors have the same meaning for this class as for the + * RTCStats dictionary. Each RTCStatsReport produced by getStats contains + * multiple RTCStats objects; one for each underlying object (codec, stream, + * transport, etc.) that was inspected to produce the stats. + */ +public class RTCStats { + private final long timestampUs; + private final String type; + private final String id; + private final Map members; + + public RTCStats(long timestampUs, String type, String id, Map members) { + this.timestampUs = timestampUs; + this.type = type; + this.id = id; + this.members = members; + } + + // Timestamp in microseconds. + public double getTimestampUs() { + return timestampUs; + } + + // Equivalent to RTCStatsType in the stats spec. Indicates the type of the + // object that was inspected to produce the stats. + public String getType() { + return type; + } + + // Unique ID representing this stats object. May be referred to by members of + // other stats objects. + public String getId() { + return id; + } + + /** + * Returns map of member names to values. Returns as an ordered map so that + * the stats object can be serialized with a consistent ordering. + * + * Values will be one of the following objects: + * - Boolean + * - Integer (for 32-bit signed integers) + * - Long (for 32-bit unsigned and 64-bit signed integers) + * - BigInteger (for 64-bit unsigned integers) + * - Double + * - String + * - The array form of any of the above (e.g., Integer[]) + */ + public Map getMembers() { + return members; + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{ timestampUs: ") + .append(timestampUs) + .append(", type: ") + .append(type) + .append(", id: ") + .append(id); + boolean first = true; + for (Map.Entry entry : members.entrySet()) { + builder.append(", ").append(entry.getKey()).append(": "); + appendValue(builder, entry.getValue()); + } + builder.append(" }"); + return builder.toString(); + } + + private static void appendValue(StringBuilder builder, Object value) { + if (value instanceof Object[]) { + Object[] arrayValue = (Object[]) value; + builder.append('['); + for (int i = 0; i < arrayValue.length; ++i) { + if (i != 0) { + builder.append(", "); + } + appendValue(builder, arrayValue[i]); + } + builder.append(']'); + } else if (value instanceof String) { + // Enclose strings in quotes to make it clear they're strings. + builder.append('"').append(value).append('"'); + } else { + builder.append(value); + } + } +} diff --git a/webrtc/sdk/android/api/org/webrtc/RTCStatsCollectorCallback.java b/webrtc/sdk/android/api/org/webrtc/RTCStatsCollectorCallback.java new file mode 100644 index 0000000000..027c1587df --- /dev/null +++ b/webrtc/sdk/android/api/org/webrtc/RTCStatsCollectorCallback.java @@ -0,0 +1,17 @@ +/* + * Copyright 2017 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; + +/** Interface for receiving stats reports (see webrtc::RTCStatsCollectorCallback). */ +public interface RTCStatsCollectorCallback { + /** Called when the stats report is ready. */ + public void onStatsDelivered(RTCStatsReport report); +} diff --git a/webrtc/sdk/android/api/org/webrtc/RTCStatsReport.java b/webrtc/sdk/android/api/org/webrtc/RTCStatsReport.java new file mode 100644 index 0000000000..00a7c0230e --- /dev/null +++ b/webrtc/sdk/android/api/org/webrtc/RTCStatsReport.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017 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.Map; + +/** + * Java version of webrtc::RTCStatsReport. Each RTCStatsReport produced by + * getStats contains multiple RTCStats objects; one for each underlying object + * (codec, stream, transport, etc.) that was inspected to produce the stats. + */ +public class RTCStatsReport { + private final long timestampUs; + private final Map stats; + + public RTCStatsReport(long timestampUs, Map stats) { + this.timestampUs = timestampUs; + this.stats = stats; + } + + // Timestamp in microseconds. + public double getTimestampUs() { + return timestampUs; + } + + // Map of stats object IDs to stats objects. Can be used to easily look up + // other stats objects, when they refer to each other by ID. + public Map getStatsMap() { + return stats; + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{ timestampUs: ").append(timestampUs).append(", stats: [\n"); + boolean first = true; + for (RTCStats stat : stats.values()) { + if (!first) { + builder.append(",\n"); + } + builder.append(stat); + first = false; + } + builder.append(" ] }"); + return builder.toString(); + } +} diff --git a/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java b/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java index 9d702033ea..44c537031f 100644 --- a/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java +++ b/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java @@ -53,7 +53,7 @@ public class PeerConnectionTest { private static class ObserverExpectations implements PeerConnection.Observer, VideoRenderer.Callbacks, DataChannel.Observer, - StatsObserver, RtpReceiver.Observer { + StatsObserver, RTCStatsCollectorCallback, RtpReceiver.Observer { private final String name; private int expectedIceCandidates = 0; private int expectedErrors = 0; @@ -77,7 +77,8 @@ public class PeerConnectionTest { private LinkedList expectedStateChanges = new LinkedList(); private LinkedList expectedRemoteDataChannelLabels = new LinkedList(); - private int expectedStatsCallbacks = 0; + private int expectedOldStatsCallbacks = 0; + private int expectedNewStatsCallbacks = 0; private LinkedList gotStatsReports = new LinkedList(); private final HashSet gotRemoteStreams = new HashSet(); private int expectedFirstAudioPacket = 0; @@ -270,14 +271,23 @@ public class PeerConnectionTest { expectedStateChanges.add(state); } + // Old getStats callback. @Override public synchronized void onComplete(StatsReport[] reports) { - if (--expectedStatsCallbacks < 0) { + if (--expectedOldStatsCallbacks < 0) { throw new RuntimeException("Unexpected stats report: " + reports); } gotStatsReports.add(reports); } + // New getStats callback. + @Override + public synchronized void onStatsDelivered(RTCStatsReport report) { + if (--expectedNewStatsCallbacks < 0) { + throw new RuntimeException("Unexpected stats report: " + report); + } + } + @Override public synchronized void onFirstPacketReceived(MediaStreamTrack.MediaType mediaType) { if (mediaType == MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO) { @@ -295,8 +305,12 @@ public class PeerConnectionTest { expectedFirstVideoPacket = 1; } - public synchronized void expectStatsCallback() { - ++expectedStatsCallbacks; + public synchronized void expectOldStatsCallback() { + ++expectedOldStatsCallbacks; + } + + public synchronized void expectNewStatsCallback() { + ++expectedNewStatsCallbacks; } public synchronized LinkedList takeStatsReports() { @@ -348,8 +362,11 @@ public class PeerConnectionTest { stillWaitingForExpectations.add( "expectedRemoteDataChannelLabels: " + expectedRemoteDataChannelLabels.size()); } - if (expectedStatsCallbacks != 0) { - stillWaitingForExpectations.add("expectedStatsCallbacks: " + expectedStatsCallbacks); + if (expectedOldStatsCallbacks != 0) { + stillWaitingForExpectations.add("expectedOldStatsCallbacks: " + expectedOldStatsCallbacks); + } + if (expectedNewStatsCallbacks != 0) { + stillWaitingForExpectations.add("expectedNewStatsCallbacks: " + expectedNewStatsCallbacks); } if (expectedFirstAudioPacket > 0) { stillWaitingForExpectations.add("expectedFirstAudioPacket: " + expectedFirstAudioPacket); @@ -1035,14 +1052,25 @@ public class PeerConnectionTest { expectations.dataChannel.unregisterObserver(); expectations.dataChannel.dispose(); } - expectations.expectStatsCallback(); + + // Call getStats (old implementation) before shutting down PC. + expectations.expectOldStatsCallback(); assertTrue(pc.getStats(expectations, null)); assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); + + // Call the new getStats implementation as well. + expectations.expectNewStatsCallback(); + pc.getStats(expectations); + assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); + expectations.expectIceConnectionChange(IceConnectionState.CLOSED); expectations.expectSignalingChange(SignalingState.CLOSED); pc.close(); assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); - expectations.expectStatsCallback(); + + // Call getStats (old implementation) after calling close(). Should still + // work. + expectations.expectOldStatsCallback(); assertTrue(pc.getStats(expectations, null)); assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); diff --git a/webrtc/sdk/android/src/jni/classreferenceholder.cc b/webrtc/sdk/android/src/jni/classreferenceholder.cc index b163be1841..263b564154 100644 --- a/webrtc/sdk/android/src/jni/classreferenceholder.cc +++ b/webrtc/sdk/android/src/jni/classreferenceholder.cc @@ -45,8 +45,15 @@ void FreeGlobalClassReferenceHolder() { ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "android/graphics/SurfaceTexture"); + LoadClass(jni, "java/lang/Boolean"); + LoadClass(jni, "java/lang/Double"); + LoadClass(jni, "java/lang/Integer"); + LoadClass(jni, "java/lang/Long"); + LoadClass(jni, "java/lang/String"); + LoadClass(jni, "java/math/BigInteger"); LoadClass(jni, "java/nio/ByteBuffer"); LoadClass(jni, "java/util/ArrayList"); + LoadClass(jni, "java/util/LinkedHashMap"); LoadClass(jni, "org/webrtc/AudioTrack"); LoadClass(jni, "org/webrtc/Camera1Enumerator"); LoadClass(jni, "org/webrtc/Camera2Enumerator"); @@ -86,6 +93,8 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy"); LoadClass(jni, "org/webrtc/PeerConnection$KeyType"); LoadClass(jni, "org/webrtc/PeerConnection$SignalingState"); + LoadClass(jni, "org/webrtc/RTCStats"); + LoadClass(jni, "org/webrtc/RTCStatsReport"); LoadClass(jni, "org/webrtc/RtpReceiver"); LoadClass(jni, "org/webrtc/RtpSender"); LoadClass(jni, "org/webrtc/SessionDescription"); diff --git a/webrtc/sdk/android/src/jni/peerconnection_jni.cc b/webrtc/sdk/android/src/jni/peerconnection_jni.cc index 0c380917b3..ecce13e0a0 100644 --- a/webrtc/sdk/android/src/jni/peerconnection_jni.cc +++ b/webrtc/sdk/android/src/jni/peerconnection_jni.cc @@ -73,6 +73,7 @@ #include "webrtc/sdk/android/src/jni/classreferenceholder.h" #include "webrtc/sdk/android/src/jni/jni_helpers.h" #include "webrtc/sdk/android/src/jni/native_handle_impl.h" +#include "webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.h" #include "webrtc/system_wrappers/include/field_trial_default.h" #include "webrtc/system_wrappers/include/logcat_trace_context.h" #include "webrtc/system_wrappers/include/trace.h" @@ -2123,8 +2124,8 @@ JOW(jobject, PeerConnection_nativeGetReceivers)(JNIEnv* jni, jobject j_pc) { return j_receivers; } -JOW(bool, PeerConnection_nativeGetStats)( - JNIEnv* jni, jobject j_pc, jobject j_observer, jlong native_track) { +JOW(bool, PeerConnection_nativeOldGetStats) +(JNIEnv* jni, jobject j_pc, jobject j_observer, jlong native_track) { rtc::scoped_refptr observer( new rtc::RefCountedObject(jni, j_observer)); return ExtractNativePC(jni, j_pc)->GetStats( @@ -2133,6 +2134,14 @@ JOW(bool, PeerConnection_nativeGetStats)( PeerConnectionInterface::kStatsOutputLevelStandard); } +JOW(void, PeerConnection_nativeNewGetStats) +(JNIEnv* jni, jobject j_pc, jobject j_callback) { + rtc::scoped_refptr callback( + new rtc::RefCountedObject(jni, + j_callback)); + ExtractNativePC(jni, j_pc)->GetStats(callback); +} + JOW(bool, PeerConnection_nativeStartRtcEventLog)( JNIEnv* jni, jobject j_pc, int file_descriptor, int max_size_bytes) { return ExtractNativePC(jni, j_pc)->StartRtcEventLog(file_descriptor, diff --git a/webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.cc b/webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.cc new file mode 100644 index 0000000000..3d78c00f87 --- /dev/null +++ b/webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.cc @@ -0,0 +1,267 @@ +/* + * Copyright 2017 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 "webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.h" + +#include +#include + +#include "webrtc/sdk/android/src/jni/classreferenceholder.h" + +namespace webrtc_jni { + +RTCStatsCollectorCallbackWrapper::RTCStatsCollectorCallbackWrapper( + JNIEnv* jni, + jobject j_callback) + : j_callback_global_(jni, j_callback), + j_callback_class_(jni, GetObjectClass(jni, j_callback)), + j_stats_report_class_(FindClass(jni, "org/webrtc/RTCStatsReport")), + j_stats_report_ctor_(GetMethodID(jni, + j_stats_report_class_, + "", + "(JLjava/util/Map;)V")), + j_stats_class_(FindClass(jni, "org/webrtc/RTCStats")), + j_stats_ctor_(GetMethodID( + jni, + j_stats_class_, + "", + "(JLjava/lang/String;Ljava/lang/String;Ljava/util/Map;)V")), + j_linked_hash_map_class_(FindClass(jni, "java/util/LinkedHashMap")), + j_linked_hash_map_ctor_( + GetMethodID(jni, j_linked_hash_map_class_, "", "()V")), + j_linked_hash_map_put_(GetMethodID( + jni, + j_linked_hash_map_class_, + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")), + j_boolean_class_(FindClass(jni, "java/lang/Boolean")), + j_boolean_ctor_(GetMethodID(jni, j_boolean_class_, "", "(Z)V")), + j_integer_class_(FindClass(jni, "java/lang/Integer")), + j_integer_ctor_(GetMethodID(jni, j_integer_class_, "", "(I)V")), + j_long_class_(FindClass(jni, "java/lang/Long")), + j_long_ctor_(GetMethodID(jni, j_long_class_, "", "(J)V")), + j_big_integer_class_(FindClass(jni, "java/math/BigInteger")), + j_big_integer_ctor_(GetMethodID(jni, + j_big_integer_class_, + "", + "(Ljava/lang/String;)V")), + j_double_class_(FindClass(jni, "java/lang/Double")), + j_double_ctor_(GetMethodID(jni, j_double_class_, "", "(D)V")), + j_string_class_(FindClass(jni, "java/lang/String")) {} + +void RTCStatsCollectorCallbackWrapper::OnStatsDelivered( + const rtc::scoped_refptr& report) { + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(jni); + jobject j_report = ReportToJava(jni, report); + jmethodID m = GetMethodID(jni, *j_callback_class_, "onStatsDelivered", + "(Lorg/webrtc/RTCStatsReport;)V"); + jni->CallVoidMethod(*j_callback_global_, m, j_report); + CHECK_EXCEPTION(jni) << "error during CallVoidMethod"; +} + +jobject RTCStatsCollectorCallbackWrapper::ReportToJava( + JNIEnv* jni, + const rtc::scoped_refptr& report) { + jobject j_stats_map = + jni->NewObject(j_linked_hash_map_class_, j_linked_hash_map_ctor_); + CHECK_EXCEPTION(jni) << "error during NewObject"; + for (const webrtc::RTCStats& stats : *report) { + // Create a local reference frame for each RTCStats, since there is a + // maximum number of references that can be created in one frame. + ScopedLocalRefFrame local_ref_frame(jni); + jstring j_id = JavaStringFromStdString(jni, stats.id()); + jobject j_stats = StatsToJava(jni, stats); + jni->CallObjectMethod(j_stats_map, j_linked_hash_map_put_, j_id, j_stats); + CHECK_EXCEPTION(jni) << "error during CallObjectMethod"; + } + jobject j_report = jni->NewObject(j_stats_report_class_, j_stats_report_ctor_, + report->timestamp_us(), j_stats_map); + CHECK_EXCEPTION(jni) << "error during NewObject"; + return j_report; +} + +jobject RTCStatsCollectorCallbackWrapper::StatsToJava( + JNIEnv* jni, + const webrtc::RTCStats& stats) { + jstring j_type = JavaStringFromStdString(jni, stats.type()); + jstring j_id = JavaStringFromStdString(jni, stats.id()); + jobject j_members = + jni->NewObject(j_linked_hash_map_class_, j_linked_hash_map_ctor_); + for (const webrtc::RTCStatsMemberInterface* member : stats.Members()) { + if (!member->is_defined()) { + continue; + } + // Create a local reference frame for each member as well. + ScopedLocalRefFrame local_ref_frame(jni); + jstring j_name = JavaStringFromStdString(jni, member->name()); + jobject j_member = MemberToJava(jni, member); + jni->CallObjectMethod(j_members, j_linked_hash_map_put_, j_name, j_member); + CHECK_EXCEPTION(jni) << "error during CallObjectMethod"; + } + jobject j_stats = + jni->NewObject(j_stats_class_, j_stats_ctor_, stats.timestamp_us(), + j_type, j_id, j_members); + CHECK_EXCEPTION(jni) << "error during NewObject"; + return j_stats; +} + +jobject RTCStatsCollectorCallbackWrapper::MemberToJava( + JNIEnv* jni, + const webrtc::RTCStatsMemberInterface* member) { + switch (member->type()) { + case webrtc::RTCStatsMemberInterface::kBool: { + jobject value = + jni->NewObject(j_boolean_class_, j_boolean_ctor_, + *member->cast_to>()); + CHECK_EXCEPTION(jni) << "error during NewObject"; + return value; + } + case webrtc::RTCStatsMemberInterface::kInt32: { + jobject value = + jni->NewObject(j_integer_class_, j_integer_ctor_, + *member->cast_to>()); + CHECK_EXCEPTION(jni) << "error during NewObject"; + return value; + } + case webrtc::RTCStatsMemberInterface::kUint32: { + jobject value = jni->NewObject( + j_long_class_, j_long_ctor_, + (jlong)*member->cast_to>()); + CHECK_EXCEPTION(jni) << "error during NewObject"; + return value; + } + case webrtc::RTCStatsMemberInterface::kInt64: { + jobject value = + jni->NewObject(j_long_class_, j_long_ctor_, + *member->cast_to>()); + CHECK_EXCEPTION(jni) << "error during NewObject"; + return value; + } + case webrtc::RTCStatsMemberInterface::kUint64: { + jobject value = + jni->NewObject(j_big_integer_class_, j_big_integer_ctor_, + JavaStringFromStdString(jni, member->ValueToString())); + CHECK_EXCEPTION(jni) << "error during NewObject"; + return value; + } + case webrtc::RTCStatsMemberInterface::kDouble: { + jobject value = + jni->NewObject(j_double_class_, j_double_ctor_, + *member->cast_to>()); + CHECK_EXCEPTION(jni) << "error during NewObject"; + return value; + } + case webrtc::RTCStatsMemberInterface::kString: { + return JavaStringFromStdString( + jni, *member->cast_to>()); + } + case webrtc::RTCStatsMemberInterface::kSequenceBool: { + const std::vector& values = + *member->cast_to>>(); + jobjectArray j_values = + jni->NewObjectArray(values.size(), j_boolean_class_, nullptr); + CHECK_EXCEPTION(jni) << "error during NewObjectArray"; + for (size_t i = 0; i < values.size(); ++i) { + jobject value = + jni->NewObject(j_boolean_class_, j_boolean_ctor_, values[i]); + jni->SetObjectArrayElement(j_values, i, value); + CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement"; + } + return j_values; + } + case webrtc::RTCStatsMemberInterface::kSequenceInt32: { + const std::vector& values = + *member->cast_to>>(); + jobjectArray j_values = + jni->NewObjectArray(values.size(), j_integer_class_, nullptr); + CHECK_EXCEPTION(jni) << "error during NewObjectArray"; + for (size_t i = 0; i < values.size(); ++i) { + jobject value = + jni->NewObject(j_integer_class_, j_integer_ctor_, values[i]); + jni->SetObjectArrayElement(j_values, i, value); + CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement"; + } + return j_values; + } + case webrtc::RTCStatsMemberInterface::kSequenceUint32: { + const std::vector& values = + *member->cast_to>>(); + jobjectArray j_values = + jni->NewObjectArray(values.size(), j_long_class_, nullptr); + CHECK_EXCEPTION(jni) << "error during NewObjectArray"; + for (size_t i = 0; i < values.size(); ++i) { + jobject value = jni->NewObject(j_long_class_, j_long_ctor_, values[i]); + jni->SetObjectArrayElement(j_values, i, value); + CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement"; + } + return j_values; + } + case webrtc::RTCStatsMemberInterface::kSequenceInt64: { + const std::vector& values = + *member->cast_to>>(); + jobjectArray j_values = + jni->NewObjectArray(values.size(), j_long_class_, nullptr); + CHECK_EXCEPTION(jni) << "error during NewObjectArray"; + for (size_t i = 0; i < values.size(); ++i) { + jobject value = jni->NewObject(j_long_class_, j_long_ctor_, values[i]); + jni->SetObjectArrayElement(j_values, i, value); + CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement"; + } + return j_values; + } + case webrtc::RTCStatsMemberInterface::kSequenceUint64: { + const std::vector& values = + *member->cast_to>>(); + jobjectArray j_values = + jni->NewObjectArray(values.size(), j_big_integer_class_, nullptr); + CHECK_EXCEPTION(jni) << "error during NewObjectArray"; + for (size_t i = 0; i < values.size(); ++i) { + jobject value = jni->NewObject( + j_big_integer_class_, j_big_integer_ctor_, + JavaStringFromStdString(jni, rtc::ToString(values[i]))); + jni->SetObjectArrayElement(j_values, i, value); + CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement"; + } + return j_values; + } + case webrtc::RTCStatsMemberInterface::kSequenceDouble: { + const std::vector& values = + *member->cast_to>>(); + jobjectArray j_values = + jni->NewObjectArray(values.size(), j_double_class_, nullptr); + CHECK_EXCEPTION(jni) << "error during NewObjectArray"; + for (size_t i = 0; i < values.size(); ++i) { + jobject value = + jni->NewObject(j_double_class_, j_double_ctor_, values[i]); + jni->SetObjectArrayElement(j_values, i, value); + CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement"; + } + return j_values; + } + case webrtc::RTCStatsMemberInterface::kSequenceString: { + const std::vector& values = + *member->cast_to>>(); + jobjectArray j_values = + jni->NewObjectArray(values.size(), j_string_class_, nullptr); + CHECK_EXCEPTION(jni) << "error during NewObjectArray"; + for (size_t i = 0; i < values.size(); ++i) { + jni->SetObjectArrayElement(j_values, i, + JavaStringFromStdString(jni, values[i])); + CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement"; + } + return j_values; + } + } + RTC_NOTREACHED(); + return nullptr; +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.h b/webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.h new file mode 100644 index 0000000000..5f1eb66bd2 --- /dev/null +++ b/webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.h @@ -0,0 +1,65 @@ +/* + * Copyright 2017 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. + */ + +#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_RTCSTATSCOLLECTORCALLBACKWRAPPER_H_ +#define WEBRTC_SDK_ANDROID_SRC_JNI_RTCSTATSCOLLECTORCALLBACKWRAPPER_H_ + +#include + +#include "webrtc/api/peerconnectioninterface.h" +#include "webrtc/sdk/android/src/jni/jni_helpers.h" + +namespace webrtc_jni { + +// Adapter for a Java RTCStatsCollectorCallback presenting a C++ +// RTCStatsCollectorCallback and dispatching the callback from C++ back to +// Java. +class RTCStatsCollectorCallbackWrapper + : public webrtc::RTCStatsCollectorCallback { + public: + RTCStatsCollectorCallbackWrapper(JNIEnv* jni, jobject j_callback); + + void OnStatsDelivered( + const rtc::scoped_refptr& report) override; + + private: + // Helper functions for converting C++ RTCStatsReport to Java equivalent. + jobject ReportToJava( + JNIEnv* jni, + const rtc::scoped_refptr& report); + jobject StatsToJava(JNIEnv* jni, const webrtc::RTCStats& stats); + jobject MemberToJava(JNIEnv* jni, + const webrtc::RTCStatsMemberInterface* member); + + const ScopedGlobalRef j_callback_global_; + const ScopedGlobalRef j_callback_class_; + const jclass j_stats_report_class_; + const jmethodID j_stats_report_ctor_; + const jclass j_stats_class_; + const jmethodID j_stats_ctor_; + const jclass j_linked_hash_map_class_; + const jmethodID j_linked_hash_map_ctor_; + const jmethodID j_linked_hash_map_put_; + const jclass j_boolean_class_; + const jmethodID j_boolean_ctor_; + const jclass j_integer_class_; + const jmethodID j_integer_ctor_; + const jclass j_long_class_; + const jmethodID j_long_ctor_; + const jclass j_big_integer_class_; + const jmethodID j_big_integer_ctor_; + const jclass j_double_class_; + const jmethodID j_double_ctor_; + const jclass j_string_class_; +}; + +} // namespace webrtc_jni + +#endif // WEBRTC_SDK_ANDROID_SRC_JNI_RTCSTATSCOLLECTORCALLBACKWRAPPER_H_