Added the JNI interface to get and set RtpParameters and the maximum bitrate limits.
Defined a JavaCollection convenience class to simplify iterating over collections from within JNI code Follow-up to https://codereview.webrtc.org/1788583004/. BUG= Review URL: https://codereview.webrtc.org/1819553002 Cr-Commit-Position: refs/heads/master@{#12125}
This commit is contained in:
parent
a49dc36976
commit
303b3c21a4
@ -694,6 +694,26 @@ public class PeerConnectionTest extends ActivityTestCase {
|
||||
assertEquals(
|
||||
PeerConnection.SignalingState.STABLE, answeringPC.signalingState());
|
||||
|
||||
// Set a bitrate limit for the outgoing video stream for the offerer.
|
||||
RtpSender videoSender = null;
|
||||
for (RtpSender sender : offeringPC.getSenders()) {
|
||||
if (sender.track().kind().equals("video")) {
|
||||
videoSender = sender;
|
||||
}
|
||||
}
|
||||
assertNotNull(videoSender);
|
||||
RtpParameters rtpParameters = videoSender.getParameters();
|
||||
assertNotNull(rtpParameters);
|
||||
assertEquals(1, rtpParameters.encodings.size());
|
||||
assertNull(rtpParameters.encodings.get(0).maxBitrateBps);
|
||||
|
||||
rtpParameters.encodings.get(0).maxBitrateBps = 300000;
|
||||
assertTrue(videoSender.setParameters(rtpParameters));
|
||||
|
||||
// Verify that we can read back the updated value.
|
||||
rtpParameters = videoSender.getParameters();
|
||||
assertEquals(300000, (int) rtpParameters.encodings.get(0).maxBitrateBps);
|
||||
|
||||
// Test send & receive UTF-8 text.
|
||||
answeringExpectations.expectMessage(
|
||||
ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false);
|
||||
|
||||
@ -34,10 +34,10 @@ JavaVM *GetJVM() {
|
||||
|
||||
// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached.
|
||||
JNIEnv* GetEnv() {
|
||||
void* env = NULL;
|
||||
void* env = nullptr;
|
||||
jint status = g_jvm->GetEnv(&env, JNI_VERSION_1_6);
|
||||
RTC_CHECK(((env != NULL) && (status == JNI_OK)) ||
|
||||
((env == NULL) && (status == JNI_EDETACHED)))
|
||||
RTC_CHECK(((env != nullptr) && (status == JNI_OK)) ||
|
||||
((env == nullptr) && (status == JNI_EDETACHED)))
|
||||
<< "Unexpected GetEnv return: " << status << ":" << env;
|
||||
return reinterpret_cast<JNIEnv*>(env);
|
||||
}
|
||||
@ -109,12 +109,12 @@ JNIEnv* AttachCurrentThreadIfNeeded() {
|
||||
JavaVMAttachArgs args;
|
||||
args.version = JNI_VERSION_1_6;
|
||||
args.name = &name[0];
|
||||
args.group = NULL;
|
||||
args.group = nullptr;
|
||||
// Deal with difference in signatures between Oracle's jni.h and Android's.
|
||||
#ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec!
|
||||
void* env = NULL;
|
||||
void* env = nullptr;
|
||||
#else
|
||||
JNIEnv* env = NULL;
|
||||
JNIEnv* env = nullptr;
|
||||
#endif
|
||||
RTC_CHECK(!g_jvm->AttachCurrentThread(&env, &args))
|
||||
<< "Failed to attach thread";
|
||||
@ -215,7 +215,7 @@ jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) {
|
||||
|
||||
// Given a (UTF-16) jstring return a new UTF-8 native string.
|
||||
std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) {
|
||||
const char* chars = jni->GetStringUTFChars(j_string, NULL);
|
||||
const char* chars = jni->GetStringUTFChars(j_string, nullptr);
|
||||
CHECK_EXCEPTION(jni) << "Error during GetStringUTFChars";
|
||||
std::string str(chars, jni->GetStringUTFLength(j_string));
|
||||
CHECK_EXCEPTION(jni) << "Error during GetStringUTFLength";
|
||||
@ -269,7 +269,76 @@ ScopedLocalRefFrame::ScopedLocalRefFrame(JNIEnv* jni) : jni_(jni) {
|
||||
RTC_CHECK(!jni_->PushLocalFrame(0)) << "Failed to PushLocalFrame";
|
||||
}
|
||||
ScopedLocalRefFrame::~ScopedLocalRefFrame() {
|
||||
jni_->PopLocalFrame(NULL);
|
||||
jni_->PopLocalFrame(nullptr);
|
||||
}
|
||||
|
||||
// Creates an iterator representing the end of any collection.
|
||||
Iterable::Iterator::Iterator() : iterator_(nullptr) {}
|
||||
|
||||
// Creates an iterator pointing to the beginning of the specified collection.
|
||||
Iterable::Iterator::Iterator(JNIEnv* jni, jobject iterable) : jni_(jni) {
|
||||
jclass j_class = GetObjectClass(jni, iterable);
|
||||
jmethodID iterator_id =
|
||||
GetMethodID(jni, j_class, "iterator", "()Ljava/util/Iterator;");
|
||||
iterator_ = jni->CallObjectMethod(iterable, iterator_id);
|
||||
CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
|
||||
RTC_CHECK(iterator_ != nullptr);
|
||||
|
||||
jclass iterator_class = GetObjectClass(jni, iterator_);
|
||||
has_next_id_ = GetMethodID(jni, iterator_class, "hasNext", "()Z");
|
||||
next_id_ = GetMethodID(jni, iterator_class, "next", "()Ljava/lang/Object;");
|
||||
|
||||
// Start at the first element in the collection.
|
||||
++(*this);
|
||||
}
|
||||
|
||||
// Move constructor - necessary to be able to return iterator types from
|
||||
// functions.
|
||||
Iterable::Iterator::Iterator(Iterator&& other)
|
||||
: jni_(std::move(other.jni_)),
|
||||
iterator_(std::move(other.iterator_)),
|
||||
value_(std::move(other.value_)),
|
||||
has_next_id_(std::move(other.has_next_id_)),
|
||||
next_id_(std::move(other.next_id_)),
|
||||
thread_checker_(std::move(other.thread_checker_)){};
|
||||
|
||||
// Advances the iterator one step.
|
||||
Iterable::Iterator& Iterable::Iterator::operator++() {
|
||||
RTC_CHECK(thread_checker_.CalledOnValidThread());
|
||||
if (AtEnd()) {
|
||||
// Can't move past the end.
|
||||
return *this;
|
||||
}
|
||||
bool has_next = jni_->CallBooleanMethod(iterator_, has_next_id_);
|
||||
CHECK_EXCEPTION(jni_) << "error during CallBooleanMethod";
|
||||
if (!has_next) {
|
||||
iterator_ = nullptr;
|
||||
value_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_ = jni_->CallObjectMethod(iterator_, next_id_);
|
||||
CHECK_EXCEPTION(jni_) << "error during CallObjectMethod";
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Provides a way to compare the iterator with itself and with the end iterator.
|
||||
// Note: all other comparison results are undefined, just like for C++ input
|
||||
// iterators.
|
||||
bool Iterable::Iterator::operator==(const Iterable::Iterator& other) {
|
||||
// Two different active iterators should never be compared.
|
||||
RTC_DCHECK(this == &other || AtEnd() || other.AtEnd());
|
||||
return AtEnd() == other.AtEnd();
|
||||
}
|
||||
|
||||
jobject Iterable::Iterator::operator*() {
|
||||
RTC_CHECK(!AtEnd());
|
||||
return value_;
|
||||
}
|
||||
|
||||
bool Iterable::Iterator::AtEnd() const {
|
||||
RTC_CHECK(thread_checker_.CalledOnValidThread());
|
||||
return jni_ == nullptr || IsNull(jni_, iterator_);
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
@ -17,7 +17,9 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/thread_checker.h"
|
||||
|
||||
// Abort the process if |jni| has a Java exception pending.
|
||||
// This macros uses the comma operator to execute ExceptionDescribe
|
||||
@ -122,6 +124,65 @@ class ScopedGlobalRef {
|
||||
T obj_;
|
||||
};
|
||||
|
||||
// Provides a convenient way to iterate over a Java Iterable using the
|
||||
// C++ range-for loop.
|
||||
// E.g. for (jobject value : Iterable(jni, j_iterable)) { ... }
|
||||
// Note: Since Java iterators cannot be duplicated, the iterator class is not
|
||||
// copyable to prevent creating multiple C++ iterators that refer to the same
|
||||
// Java iterator.
|
||||
class Iterable {
|
||||
public:
|
||||
Iterable(JNIEnv* jni, jobject iterable) : jni_(jni), iterable_(iterable) {}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
// Creates an iterator representing the end of any collection.
|
||||
Iterator();
|
||||
// Creates an iterator pointing to the beginning of the specified
|
||||
// collection.
|
||||
Iterator(JNIEnv* jni, jobject iterable);
|
||||
|
||||
// Move constructor - necessary to be able to return iterator types from
|
||||
// functions.
|
||||
Iterator(Iterator&& other);
|
||||
|
||||
// Move assignment should not be used.
|
||||
Iterator& operator=(Iterator&&) = delete;
|
||||
|
||||
// Advances the iterator one step.
|
||||
Iterator& operator++();
|
||||
|
||||
// Provides a way to compare the iterator with itself and with the end
|
||||
// iterator.
|
||||
// Note: all other comparison results are undefined, just like for C++ input
|
||||
// iterators.
|
||||
bool operator==(const Iterator& other);
|
||||
bool operator!=(const Iterator& other) { return !(*this == other); }
|
||||
jobject operator*();
|
||||
|
||||
private:
|
||||
bool AtEnd() const;
|
||||
|
||||
JNIEnv* jni_ = nullptr;
|
||||
jobject iterator_ = nullptr;
|
||||
jobject value_ = nullptr;
|
||||
jmethodID has_next_id_ = nullptr;
|
||||
jmethodID next_id_ = nullptr;
|
||||
rtc::ThreadChecker thread_checker_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(Iterator);
|
||||
};
|
||||
|
||||
Iterable::Iterator begin() { return Iterable::Iterator(jni_, iterable_); }
|
||||
Iterable::Iterator end() { return Iterable::Iterator(); }
|
||||
|
||||
private:
|
||||
JNIEnv* jni_;
|
||||
jobject iterable_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(Iterable);
|
||||
};
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_API_JAVA_JNI_JNI_HELPERS_H_
|
||||
|
||||
@ -456,18 +456,7 @@ class ConstraintsWrapper : public MediaConstraintsInterface {
|
||||
jfieldID j_id = GetFieldID(jni,
|
||||
GetObjectClass(jni, j_constraints), field_name, "Ljava/util/List;");
|
||||
jobject j_list = GetObjectField(jni, j_constraints, j_id);
|
||||
jmethodID j_iterator_id = GetMethodID(jni,
|
||||
GetObjectClass(jni, j_list), "iterator", "()Ljava/util/Iterator;");
|
||||
jobject j_iterator = jni->CallObjectMethod(j_list, j_iterator_id);
|
||||
CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
|
||||
jmethodID j_has_next = GetMethodID(jni,
|
||||
GetObjectClass(jni, j_iterator), "hasNext", "()Z");
|
||||
jmethodID j_next = GetMethodID(jni,
|
||||
GetObjectClass(jni, j_iterator), "next", "()Ljava/lang/Object;");
|
||||
while (jni->CallBooleanMethod(j_iterator, j_has_next)) {
|
||||
CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
|
||||
jobject entry = jni->CallObjectMethod(j_iterator, j_next);
|
||||
CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
|
||||
for (jobject entry : Iterable(jni, j_list)) {
|
||||
jmethodID get_key = GetMethodID(jni,
|
||||
GetObjectClass(jni, entry), "getKey", "()Ljava/lang/String;");
|
||||
jstring j_key = reinterpret_cast<jstring>(
|
||||
@ -481,7 +470,6 @@ class ConstraintsWrapper : public MediaConstraintsInterface {
|
||||
field->push_back(Constraint(JavaToStdString(jni, j_key),
|
||||
JavaToStdString(jni, j_value)));
|
||||
}
|
||||
CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
|
||||
}
|
||||
|
||||
Constraints mandatory_;
|
||||
@ -1464,19 +1452,7 @@ static PeerConnectionInterface::ContinualGatheringPolicy
|
||||
static void JavaIceServersToJsepIceServers(
|
||||
JNIEnv* jni, jobject j_ice_servers,
|
||||
PeerConnectionInterface::IceServers* ice_servers) {
|
||||
jclass list_class = GetObjectClass(jni, j_ice_servers);
|
||||
jmethodID iterator_id = GetMethodID(
|
||||
jni, list_class, "iterator", "()Ljava/util/Iterator;");
|
||||
jobject iterator = jni->CallObjectMethod(j_ice_servers, iterator_id);
|
||||
CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
|
||||
jmethodID iterator_has_next = GetMethodID(
|
||||
jni, GetObjectClass(jni, iterator), "hasNext", "()Z");
|
||||
jmethodID iterator_next = GetMethodID(
|
||||
jni, GetObjectClass(jni, iterator), "next", "()Ljava/lang/Object;");
|
||||
while (jni->CallBooleanMethod(iterator, iterator_has_next)) {
|
||||
CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
|
||||
jobject j_ice_server = jni->CallObjectMethod(iterator, iterator_next);
|
||||
CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
|
||||
for (jobject j_ice_server : Iterable(jni, j_ice_servers)) {
|
||||
jclass j_ice_server_class = GetObjectClass(jni, j_ice_server);
|
||||
jfieldID j_ice_server_uri_id =
|
||||
GetFieldID(jni, j_ice_server_class, "uri", "Ljava/lang/String;");
|
||||
@ -1496,7 +1472,6 @@ static void JavaIceServersToJsepIceServers(
|
||||
server.password = JavaToStdString(jni, password);
|
||||
ice_servers->push_back(server);
|
||||
}
|
||||
CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
|
||||
}
|
||||
|
||||
static void JavaRTCConfigurationToJsepRTCConfiguration(
|
||||
@ -2051,6 +2026,94 @@ JOW(jlong, RtpSender_nativeGetTrack)(JNIEnv* jni,
|
||||
.release());
|
||||
}
|
||||
|
||||
static bool JavaEncodingToJsepRtpEncodingParameters(
|
||||
JNIEnv* jni,
|
||||
jobject j_encodings,
|
||||
std::vector<webrtc::RtpEncodingParameters>* encodings) {
|
||||
const int kBitrateUnlimited = -1;
|
||||
jclass j_encoding_parameters_class =
|
||||
jni->FindClass("org/webrtc/RtpParameters$Encoding");
|
||||
jfieldID bitrate_id = GetFieldID(jni, j_encoding_parameters_class,
|
||||
"maxBitrateBps", "Ljava/lang/Integer;");
|
||||
jclass j_integer_class = jni->FindClass("java/lang/Integer");
|
||||
jmethodID int_value_id = GetMethodID(jni, j_integer_class, "intValue", "()I");
|
||||
|
||||
for (jobject j_encoding_parameters : Iterable(jni, j_encodings)) {
|
||||
webrtc::RtpEncodingParameters encoding;
|
||||
jobject j_bitrate = GetObjectField(jni, j_encoding_parameters, bitrate_id);
|
||||
if (!IsNull(jni, j_bitrate)) {
|
||||
int bitrate_value = jni->CallIntMethod(j_bitrate, int_value_id);
|
||||
CHECK_EXCEPTION(jni) << "error during CallIntMethod";
|
||||
encoding.max_bitrate_bps = bitrate_value;
|
||||
} else {
|
||||
encoding.max_bitrate_bps = kBitrateUnlimited;
|
||||
}
|
||||
encodings->push_back(encoding);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JOW(jboolean, RtpSender_nativeSetParameters)
|
||||
(JNIEnv* jni, jclass, jlong j_rtp_sender_pointer, jobject j_parameters) {
|
||||
if (IsNull(jni, j_parameters)) {
|
||||
return false;
|
||||
}
|
||||
jclass parameters_class = jni->FindClass("org/webrtc/RtpParameters");
|
||||
jclass encoding_class = jni->FindClass("org/webrtc/RtpParameters$Encoding");
|
||||
jfieldID encodings_id =
|
||||
GetFieldID(jni, parameters_class, "encodings", "Ljava/util/LinkedList;");
|
||||
|
||||
jobject j_encodings = GetObjectField(jni, j_parameters, encodings_id);
|
||||
webrtc::RtpParameters parameters;
|
||||
JavaEncodingToJsepRtpEncodingParameters(jni, j_encodings,
|
||||
¶meters.encodings);
|
||||
return reinterpret_cast<RtpSenderInterface*>(j_rtp_sender_pointer)
|
||||
->SetParameters(parameters);
|
||||
}
|
||||
|
||||
JOW(jobject, RtpSender_nativeGetParameters)
|
||||
(JNIEnv* jni, jclass, jlong j_rtp_sender_pointer) {
|
||||
webrtc::RtpParameters parameters =
|
||||
reinterpret_cast<RtpSenderInterface*>(j_rtp_sender_pointer)
|
||||
->GetParameters();
|
||||
|
||||
jclass parameters_class = jni->FindClass("org/webrtc/RtpParameters");
|
||||
jmethodID parameters_ctor =
|
||||
GetMethodID(jni, parameters_class, "<init>", "()V");
|
||||
jobject j_parameters = jni->NewObject(parameters_class, parameters_ctor);
|
||||
CHECK_EXCEPTION(jni) << "error during NewObject";
|
||||
|
||||
jclass encoding_class = jni->FindClass("org/webrtc/RtpParameters$Encoding");
|
||||
jmethodID encoding_ctor = GetMethodID(jni, encoding_class, "<init>", "()V");
|
||||
jfieldID encodings_id =
|
||||
GetFieldID(jni, parameters_class, "encodings", "Ljava/util/LinkedList;");
|
||||
jobject j_encodings = GetObjectField(jni, j_parameters, encodings_id);
|
||||
jmethodID add = GetMethodID(jni, GetObjectClass(jni, j_encodings), "add",
|
||||
"(Ljava/lang/Object;)Z");
|
||||
jfieldID bitrate_id =
|
||||
GetFieldID(jni, encoding_class, "maxBitrateBps", "Ljava/lang/Integer;");
|
||||
|
||||
jclass integer_class = jni->FindClass("java/lang/Integer");
|
||||
jmethodID integer_ctor = GetMethodID(jni, integer_class, "<init>", "(I)V");
|
||||
|
||||
for (webrtc::RtpEncodingParameters encoding : parameters.encodings) {
|
||||
jobject j_encoding_parameters =
|
||||
jni->NewObject(encoding_class, encoding_ctor);
|
||||
CHECK_EXCEPTION(jni) << "error during NewObject";
|
||||
if (encoding.max_bitrate_bps > 0) {
|
||||
jobject j_bitrate_value =
|
||||
jni->NewObject(integer_class, integer_ctor, encoding.max_bitrate_bps);
|
||||
CHECK_EXCEPTION(jni) << "error during NewObject";
|
||||
jni->SetObjectField(j_encoding_parameters, bitrate_id, j_bitrate_value);
|
||||
CHECK_EXCEPTION(jni) << "error during SetObjectField";
|
||||
}
|
||||
jboolean added =
|
||||
jni->CallBooleanMethod(j_encodings, add, j_encoding_parameters);
|
||||
CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
|
||||
}
|
||||
return j_parameters;
|
||||
}
|
||||
|
||||
JOW(jstring, RtpSender_nativeId)(
|
||||
JNIEnv* jni, jclass, jlong j_rtp_sender_pointer) {
|
||||
return JavaStringFromStdString(
|
||||
|
||||
28
webrtc/api/java/src/org/webrtc/RtpParameters.java
Normal file
28
webrtc/api/java/src/org/webrtc/RtpParameters.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2013 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.List;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* The parameters for an {@code RtpSender}, as defined in
|
||||
* http://w3c.github.io/webrtc-pc/#rtcrtpsender-interface.
|
||||
*/
|
||||
public class RtpParameters {
|
||||
public static class Encoding { public Integer maxBitrateBps; }
|
||||
|
||||
public final LinkedList<Encoding> encodings;
|
||||
|
||||
public RtpParameters() {
|
||||
encodings = new LinkedList<Encoding>();
|
||||
}
|
||||
}
|
||||
@ -46,6 +46,14 @@ public class RtpSender {
|
||||
return cachedTrack;
|
||||
}
|
||||
|
||||
public boolean setParameters(RtpParameters parameters) {
|
||||
return nativeSetParameters(nativeRtpSender, parameters);
|
||||
}
|
||||
|
||||
public RtpParameters getParameters() {
|
||||
return nativeGetParameters(nativeRtpSender);
|
||||
}
|
||||
|
||||
public String id() {
|
||||
return nativeId(nativeRtpSender);
|
||||
}
|
||||
@ -64,6 +72,11 @@ public class RtpSender {
|
||||
// Will be released in dispose() or setTrack().
|
||||
private static native long nativeGetTrack(long nativeRtpSender);
|
||||
|
||||
private static native boolean nativeSetParameters(long nativeRtpSender,
|
||||
RtpParameters parameters);
|
||||
|
||||
private static native RtpParameters nativeGetParameters(long nativeRtpSender);
|
||||
|
||||
private static native String nativeId(long nativeRtpSender);
|
||||
|
||||
private static native void free(long nativeRtpSender);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user