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}
This commit is contained in:
deadbeef
2017-04-18 10:27:51 -07:00
committed by Commit bot
parent 0fafb1ac8f
commit 82215872f8
10 changed files with 582 additions and 13 deletions

View File

@ -42,6 +42,8 @@ rtc_static_library("libjingle_peerconnection_jni") {
"src/jni/native_handle_impl.cc", "src/jni/native_handle_impl.cc",
"src/jni/native_handle_impl.h", "src/jni/native_handle_impl.h",
"src/jni/peerconnection_jni.cc", "src/jni/peerconnection_jni.cc",
"src/jni/rtcstatscollectorcallbackwrapper.cc",
"src/jni/rtcstatscollectorcallbackwrapper.h",
"src/jni/surfacetexturehelper_jni.cc", "src/jni/surfacetexturehelper_jni.cc",
"src/jni/surfacetexturehelper_jni.h", "src/jni/surfacetexturehelper_jni.h",
] ]
@ -159,6 +161,9 @@ android_library("libjingle_peerconnection_java") {
"api/org/webrtc/PeerConnection.java", "api/org/webrtc/PeerConnection.java",
"api/org/webrtc/PeerConnectionFactory.java", "api/org/webrtc/PeerConnectionFactory.java",
"api/org/webrtc/RendererCommon.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/RtpParameters.java",
"api/org/webrtc/RtpReceiver.java", "api/org/webrtc/RtpReceiver.java",
"api/org/webrtc/RtpSender.java", "api/org/webrtc/RtpSender.java",

View File

@ -265,8 +265,16 @@ public class PeerConnection {
return Collections.unmodifiableList(receivers); return Collections.unmodifiableList(receivers);
} }
// Older, non-standard implementation of getStats.
@Deprecated
public boolean getStats(StatsObserver observer, MediaStreamTrack track) { 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 // 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 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); private native RtpSender nativeCreateSender(String kind, String stream_id);

View File

@ -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<String, Object> members;
public RTCStats(long timestampUs, String type, String id, Map<String, Object> 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<String, Object> 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<String, Object> 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);
}
}
}

View File

@ -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);
}

View File

@ -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<String, RTCStats> stats;
public RTCStatsReport(long timestampUs, Map<String, RTCStats> 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<String, RTCStats> 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();
}
}

View File

@ -53,7 +53,7 @@ public class PeerConnectionTest {
private static class ObserverExpectations private static class ObserverExpectations
implements PeerConnection.Observer, VideoRenderer.Callbacks, DataChannel.Observer, implements PeerConnection.Observer, VideoRenderer.Callbacks, DataChannel.Observer,
StatsObserver, RtpReceiver.Observer { StatsObserver, RTCStatsCollectorCallback, RtpReceiver.Observer {
private final String name; private final String name;
private int expectedIceCandidates = 0; private int expectedIceCandidates = 0;
private int expectedErrors = 0; private int expectedErrors = 0;
@ -77,7 +77,8 @@ public class PeerConnectionTest {
private LinkedList<DataChannel.State> expectedStateChanges = private LinkedList<DataChannel.State> expectedStateChanges =
new LinkedList<DataChannel.State>(); new LinkedList<DataChannel.State>();
private LinkedList<String> expectedRemoteDataChannelLabels = new LinkedList<String>(); private LinkedList<String> expectedRemoteDataChannelLabels = new LinkedList<String>();
private int expectedStatsCallbacks = 0; private int expectedOldStatsCallbacks = 0;
private int expectedNewStatsCallbacks = 0;
private LinkedList<StatsReport[]> gotStatsReports = new LinkedList<StatsReport[]>(); private LinkedList<StatsReport[]> gotStatsReports = new LinkedList<StatsReport[]>();
private final HashSet<MediaStream> gotRemoteStreams = new HashSet<MediaStream>(); private final HashSet<MediaStream> gotRemoteStreams = new HashSet<MediaStream>();
private int expectedFirstAudioPacket = 0; private int expectedFirstAudioPacket = 0;
@ -270,14 +271,23 @@ public class PeerConnectionTest {
expectedStateChanges.add(state); expectedStateChanges.add(state);
} }
// Old getStats callback.
@Override @Override
public synchronized void onComplete(StatsReport[] reports) { public synchronized void onComplete(StatsReport[] reports) {
if (--expectedStatsCallbacks < 0) { if (--expectedOldStatsCallbacks < 0) {
throw new RuntimeException("Unexpected stats report: " + reports); throw new RuntimeException("Unexpected stats report: " + reports);
} }
gotStatsReports.add(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 @Override
public synchronized void onFirstPacketReceived(MediaStreamTrack.MediaType mediaType) { public synchronized void onFirstPacketReceived(MediaStreamTrack.MediaType mediaType) {
if (mediaType == MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO) { if (mediaType == MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO) {
@ -295,8 +305,12 @@ public class PeerConnectionTest {
expectedFirstVideoPacket = 1; expectedFirstVideoPacket = 1;
} }
public synchronized void expectStatsCallback() { public synchronized void expectOldStatsCallback() {
++expectedStatsCallbacks; ++expectedOldStatsCallbacks;
}
public synchronized void expectNewStatsCallback() {
++expectedNewStatsCallbacks;
} }
public synchronized LinkedList<StatsReport[]> takeStatsReports() { public synchronized LinkedList<StatsReport[]> takeStatsReports() {
@ -348,8 +362,11 @@ public class PeerConnectionTest {
stillWaitingForExpectations.add( stillWaitingForExpectations.add(
"expectedRemoteDataChannelLabels: " + expectedRemoteDataChannelLabels.size()); "expectedRemoteDataChannelLabels: " + expectedRemoteDataChannelLabels.size());
} }
if (expectedStatsCallbacks != 0) { if (expectedOldStatsCallbacks != 0) {
stillWaitingForExpectations.add("expectedStatsCallbacks: " + expectedStatsCallbacks); stillWaitingForExpectations.add("expectedOldStatsCallbacks: " + expectedOldStatsCallbacks);
}
if (expectedNewStatsCallbacks != 0) {
stillWaitingForExpectations.add("expectedNewStatsCallbacks: " + expectedNewStatsCallbacks);
} }
if (expectedFirstAudioPacket > 0) { if (expectedFirstAudioPacket > 0) {
stillWaitingForExpectations.add("expectedFirstAudioPacket: " + expectedFirstAudioPacket); stillWaitingForExpectations.add("expectedFirstAudioPacket: " + expectedFirstAudioPacket);
@ -1035,14 +1052,25 @@ public class PeerConnectionTest {
expectations.dataChannel.unregisterObserver(); expectations.dataChannel.unregisterObserver();
expectations.dataChannel.dispose(); expectations.dataChannel.dispose();
} }
expectations.expectStatsCallback();
// Call getStats (old implementation) before shutting down PC.
expectations.expectOldStatsCallback();
assertTrue(pc.getStats(expectations, null)); assertTrue(pc.getStats(expectations, null));
assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); 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.expectIceConnectionChange(IceConnectionState.CLOSED);
expectations.expectSignalingChange(SignalingState.CLOSED); expectations.expectSignalingChange(SignalingState.CLOSED);
pc.close(); pc.close();
assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); 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(pc.getStats(expectations, null));
assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS));

View File

@ -45,8 +45,15 @@ void FreeGlobalClassReferenceHolder() {
ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) {
LoadClass(jni, "android/graphics/SurfaceTexture"); 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/nio/ByteBuffer");
LoadClass(jni, "java/util/ArrayList"); LoadClass(jni, "java/util/ArrayList");
LoadClass(jni, "java/util/LinkedHashMap");
LoadClass(jni, "org/webrtc/AudioTrack"); LoadClass(jni, "org/webrtc/AudioTrack");
LoadClass(jni, "org/webrtc/Camera1Enumerator"); LoadClass(jni, "org/webrtc/Camera1Enumerator");
LoadClass(jni, "org/webrtc/Camera2Enumerator"); LoadClass(jni, "org/webrtc/Camera2Enumerator");
@ -86,6 +93,8 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) {
LoadClass(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy"); LoadClass(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy");
LoadClass(jni, "org/webrtc/PeerConnection$KeyType"); LoadClass(jni, "org/webrtc/PeerConnection$KeyType");
LoadClass(jni, "org/webrtc/PeerConnection$SignalingState"); 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/RtpReceiver");
LoadClass(jni, "org/webrtc/RtpSender"); LoadClass(jni, "org/webrtc/RtpSender");
LoadClass(jni, "org/webrtc/SessionDescription"); LoadClass(jni, "org/webrtc/SessionDescription");

View File

@ -73,6 +73,7 @@
#include "webrtc/sdk/android/src/jni/classreferenceholder.h" #include "webrtc/sdk/android/src/jni/classreferenceholder.h"
#include "webrtc/sdk/android/src/jni/jni_helpers.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/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/field_trial_default.h"
#include "webrtc/system_wrappers/include/logcat_trace_context.h" #include "webrtc/system_wrappers/include/logcat_trace_context.h"
#include "webrtc/system_wrappers/include/trace.h" #include "webrtc/system_wrappers/include/trace.h"
@ -2123,8 +2124,8 @@ JOW(jobject, PeerConnection_nativeGetReceivers)(JNIEnv* jni, jobject j_pc) {
return j_receivers; return j_receivers;
} }
JOW(bool, PeerConnection_nativeGetStats)( JOW(bool, PeerConnection_nativeOldGetStats)
JNIEnv* jni, jobject j_pc, jobject j_observer, jlong native_track) { (JNIEnv* jni, jobject j_pc, jobject j_observer, jlong native_track) {
rtc::scoped_refptr<StatsObserverWrapper> observer( rtc::scoped_refptr<StatsObserverWrapper> observer(
new rtc::RefCountedObject<StatsObserverWrapper>(jni, j_observer)); new rtc::RefCountedObject<StatsObserverWrapper>(jni, j_observer));
return ExtractNativePC(jni, j_pc)->GetStats( return ExtractNativePC(jni, j_pc)->GetStats(
@ -2133,6 +2134,14 @@ JOW(bool, PeerConnection_nativeGetStats)(
PeerConnectionInterface::kStatsOutputLevelStandard); PeerConnectionInterface::kStatsOutputLevelStandard);
} }
JOW(void, PeerConnection_nativeNewGetStats)
(JNIEnv* jni, jobject j_pc, jobject j_callback) {
rtc::scoped_refptr<RTCStatsCollectorCallbackWrapper> callback(
new rtc::RefCountedObject<RTCStatsCollectorCallbackWrapper>(jni,
j_callback));
ExtractNativePC(jni, j_pc)->GetStats(callback);
}
JOW(bool, PeerConnection_nativeStartRtcEventLog)( JOW(bool, PeerConnection_nativeStartRtcEventLog)(
JNIEnv* jni, jobject j_pc, int file_descriptor, int max_size_bytes) { JNIEnv* jni, jobject j_pc, int file_descriptor, int max_size_bytes) {
return ExtractNativePC(jni, j_pc)->StartRtcEventLog(file_descriptor, return ExtractNativePC(jni, j_pc)->StartRtcEventLog(file_descriptor,

View File

@ -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 <string>
#include <vector>
#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_,
"<init>",
"(JLjava/util/Map;)V")),
j_stats_class_(FindClass(jni, "org/webrtc/RTCStats")),
j_stats_ctor_(GetMethodID(
jni,
j_stats_class_,
"<init>",
"(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_, "<init>", "()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_, "<init>", "(Z)V")),
j_integer_class_(FindClass(jni, "java/lang/Integer")),
j_integer_ctor_(GetMethodID(jni, j_integer_class_, "<init>", "(I)V")),
j_long_class_(FindClass(jni, "java/lang/Long")),
j_long_ctor_(GetMethodID(jni, j_long_class_, "<init>", "(J)V")),
j_big_integer_class_(FindClass(jni, "java/math/BigInteger")),
j_big_integer_ctor_(GetMethodID(jni,
j_big_integer_class_,
"<init>",
"(Ljava/lang/String;)V")),
j_double_class_(FindClass(jni, "java/lang/Double")),
j_double_ctor_(GetMethodID(jni, j_double_class_, "<init>", "(D)V")),
j_string_class_(FindClass(jni, "java/lang/String")) {}
void RTCStatsCollectorCallbackWrapper::OnStatsDelivered(
const rtc::scoped_refptr<const webrtc::RTCStatsReport>& 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<const webrtc::RTCStatsReport>& 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<webrtc::RTCStatsMember<bool>>());
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<webrtc::RTCStatsMember<int32_t>>());
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<webrtc::RTCStatsMember<uint32_t>>());
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<webrtc::RTCStatsMember<int64_t>>());
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<webrtc::RTCStatsMember<double>>());
CHECK_EXCEPTION(jni) << "error during NewObject";
return value;
}
case webrtc::RTCStatsMemberInterface::kString: {
return JavaStringFromStdString(
jni, *member->cast_to<webrtc::RTCStatsMember<std::string>>());
}
case webrtc::RTCStatsMemberInterface::kSequenceBool: {
const std::vector<bool>& values =
*member->cast_to<webrtc::RTCStatsMember<std::vector<bool>>>();
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<int32_t>& values =
*member->cast_to<webrtc::RTCStatsMember<std::vector<int32_t>>>();
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<uint32_t>& values =
*member->cast_to<webrtc::RTCStatsMember<std::vector<uint32_t>>>();
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<int64_t>& values =
*member->cast_to<webrtc::RTCStatsMember<std::vector<int64_t>>>();
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<uint64_t>& values =
*member->cast_to<webrtc::RTCStatsMember<std::vector<uint64_t>>>();
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<double>& values =
*member->cast_to<webrtc::RTCStatsMember<std::vector<double>>>();
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<std::string>& values =
*member->cast_to<webrtc::RTCStatsMember<std::vector<std::string>>>();
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

View File

@ -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 <jni.h>
#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<const webrtc::RTCStatsReport>& report) override;
private:
// Helper functions for converting C++ RTCStatsReport to Java equivalent.
jobject ReportToJava(
JNIEnv* jni,
const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report);
jobject StatsToJava(JNIEnv* jni, const webrtc::RTCStats& stats);
jobject MemberToJava(JNIEnv* jni,
const webrtc::RTCStatsMemberInterface* member);
const ScopedGlobalRef<jobject> j_callback_global_;
const ScopedGlobalRef<jclass> 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_