blob: a542c28e4822b4204e7d82e5e13a6ee31cb135a6 [file] [log] [blame]
/*
* 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.
*/
// Lifecycle notes: objects are owned where they will be called; in other words
// FooObservers are owned by C++-land, and user-callable objects (e.g.
// PeerConnection and VideoTrack) are owned by Java-land.
// When this file (or other files in this directory) allocates C++
// RefCountInterfaces it AddRef()s an artificial ref simulating the jlong held
// in Java-land, and then Release()s the ref in the respective free call.
// Sometimes this AddRef is implicit in the construction of a scoped_refptr<>
// which is then .release()d. Any persistent (non-local) references from C++ to
// Java must be global or weak (in which case they must be checked before use)!
//
// Exception notes: pretty much all JNI calls can throw Java exceptions, so each
// call through a JNIEnv* pointer needs to be followed by an ExceptionCheck()
// call. In this file this is done in CHECK_EXCEPTION, making for much easier
// debugging in case of failure (the alternative is to wait for control to
// return to the Java frame that called code in this file, at which point it's
// impossible to tell which JNI call broke).
#include <limits>
#include <memory>
#include <utility>
#include "api/mediaconstraintsinterface.h"
#include "api/peerconnectioninterface.h"
#include "api/rtpreceiverinterface.h"
#include "api/rtpsenderinterface.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "sdk/android/src/jni/classreferenceholder.h"
#include "sdk/android/src/jni/jni_helpers.h"
#include "sdk/android/src/jni/pc/java_native_conversion.h"
#include "sdk/android/src/jni/pc/mediaconstraints_jni.h"
#include "sdk/android/src/jni/pc/peerconnectionobserver_jni.h"
#include "sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h"
#include "sdk/android/src/jni/pc/sdpobserver_jni.h"
#include "sdk/android/src/jni/pc/statsobserver_jni.h"
namespace webrtc {
namespace jni {
static rtc::scoped_refptr<PeerConnectionInterface> ExtractNativePC(
JNIEnv* jni,
jobject j_pc) {
jfieldID native_pc_id =
GetFieldID(jni, GetObjectClass(jni, j_pc), "nativePeerConnection", "J");
jlong j_p = GetLongField(jni, j_pc, native_pc_id);
return rtc::scoped_refptr<PeerConnectionInterface>(
reinterpret_cast<PeerConnectionInterface*>(j_p));
}
JNI_FUNCTION_DECLARATION(void,
PeerConnection_freeObserver,
JNIEnv*,
jclass,
jlong j_p) {
PeerConnectionObserverJni* p =
reinterpret_cast<PeerConnectionObserverJni*>(j_p);
delete p;
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_getLocalDescription,
JNIEnv* jni,
jobject j_pc) {
const SessionDescriptionInterface* sdp =
ExtractNativePC(jni, j_pc)->local_description();
return sdp ? NativeToJavaSessionDescription(jni, sdp) : NULL;
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_getRemoteDescription,
JNIEnv* jni,
jobject j_pc) {
const SessionDescriptionInterface* sdp =
ExtractNativePC(jni, j_pc)->remote_description();
return sdp ? NativeToJavaSessionDescription(jni, sdp) : NULL;
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_createDataChannel,
JNIEnv* jni,
jobject j_pc,
jstring j_label,
jobject j_init) {
DataChannelInit init = JavaToNativeDataChannelInit(jni, j_init);
rtc::scoped_refptr<DataChannelInterface> channel(
ExtractNativePC(jni, j_pc)->CreateDataChannel(
JavaToStdString(jni, j_label), &init));
// Mustn't pass channel.get() directly through NewObject to avoid reading its
// vararg parameter as 64-bit and reading memory that doesn't belong to the
// 32-bit parameter.
jlong nativeChannelPtr = jlongFromPointer(channel.get());
if (!nativeChannelPtr) {
LOG(LS_ERROR) << "Failed to create DataChannel";
return nullptr;
}
jclass j_data_channel_class = FindClass(jni, "org/webrtc/DataChannel");
jmethodID j_data_channel_ctor =
GetMethodID(jni, j_data_channel_class, "<init>", "(J)V");
jobject j_channel = jni->NewObject(j_data_channel_class, j_data_channel_ctor,
nativeChannelPtr);
CHECK_EXCEPTION(jni) << "error during NewObject";
// Channel is now owned by Java object, and will be freed from there.
channel->AddRef();
return j_channel;
}
JNI_FUNCTION_DECLARATION(void,
PeerConnection_createOffer,
JNIEnv* jni,
jobject j_pc,
jobject j_observer,
jobject j_constraints) {
MediaConstraintsJni* constraints =
new MediaConstraintsJni(jni, j_constraints);
rtc::scoped_refptr<CreateSdpObserverJni> observer(
new rtc::RefCountedObject<CreateSdpObserverJni>(jni, j_observer,
constraints));
ExtractNativePC(jni, j_pc)->CreateOffer(observer, constraints);
}
JNI_FUNCTION_DECLARATION(void,
PeerConnection_createAnswer,
JNIEnv* jni,
jobject j_pc,
jobject j_observer,
jobject j_constraints) {
MediaConstraintsJni* constraints =
new MediaConstraintsJni(jni, j_constraints);
rtc::scoped_refptr<CreateSdpObserverJni> observer(
new rtc::RefCountedObject<CreateSdpObserverJni>(jni, j_observer,
constraints));
ExtractNativePC(jni, j_pc)->CreateAnswer(observer, constraints);
}
JNI_FUNCTION_DECLARATION(void,
PeerConnection_setLocalDescription,
JNIEnv* jni,
jobject j_pc,
jobject j_observer,
jobject j_sdp) {
rtc::scoped_refptr<SetSdpObserverJni> observer(
new rtc::RefCountedObject<SetSdpObserverJni>(jni, j_observer, nullptr));
ExtractNativePC(jni, j_pc)->SetLocalDescription(
observer, JavaToNativeSessionDescription(jni, j_sdp));
}
JNI_FUNCTION_DECLARATION(void,
PeerConnection_setRemoteDescription,
JNIEnv* jni,
jobject j_pc,
jobject j_observer,
jobject j_sdp) {
rtc::scoped_refptr<SetSdpObserverJni> observer(
new rtc::RefCountedObject<SetSdpObserverJni>(jni, j_observer, nullptr));
ExtractNativePC(jni, j_pc)->SetRemoteDescription(
observer, JavaToNativeSessionDescription(jni, j_sdp));
}
JNI_FUNCTION_DECLARATION(jboolean,
PeerConnection_nativeSetConfiguration,
JNIEnv* jni,
jobject j_pc,
jobject j_rtc_config,
jlong native_observer) {
// Need to merge constraints into RTCConfiguration again, which are stored
// in the observer object.
PeerConnectionObserverJni* observer =
reinterpret_cast<PeerConnectionObserverJni*>(native_observer);
PeerConnectionInterface::RTCConfiguration rtc_config(
PeerConnectionInterface::RTCConfigurationType::kAggressive);
JavaToNativeRTCConfiguration(jni, j_rtc_config, &rtc_config);
CopyConstraintsIntoRtcConfiguration(observer->constraints(), &rtc_config);
return ExtractNativePC(jni, j_pc)->SetConfiguration(rtc_config);
}
JNI_FUNCTION_DECLARATION(jboolean,
PeerConnection_nativeAddIceCandidate,
JNIEnv* jni,
jobject j_pc,
jstring j_sdp_mid,
jint j_sdp_mline_index,
jstring j_candidate_sdp) {
std::string sdp_mid = JavaToStdString(jni, j_sdp_mid);
std::string sdp = JavaToStdString(jni, j_candidate_sdp);
std::unique_ptr<IceCandidateInterface> candidate(
CreateIceCandidate(sdp_mid, j_sdp_mline_index, sdp, nullptr));
return ExtractNativePC(jni, j_pc)->AddIceCandidate(candidate.get());
}
JNI_FUNCTION_DECLARATION(jboolean,
PeerConnection_nativeRemoveIceCandidates,
JNIEnv* jni,
jobject j_pc,
jobjectArray j_candidates) {
std::vector<cricket::Candidate> candidates;
size_t num_candidates = jni->GetArrayLength(j_candidates);
for (size_t i = 0; i < num_candidates; ++i) {
jobject j_candidate = jni->GetObjectArrayElement(j_candidates, i);
candidates.push_back(JavaToNativeCandidate(jni, j_candidate));
}
return ExtractNativePC(jni, j_pc)->RemoveIceCandidates(candidates);
}
JNI_FUNCTION_DECLARATION(jboolean,
PeerConnection_nativeAddLocalStream,
JNIEnv* jni,
jobject j_pc,
jlong native_stream) {
return ExtractNativePC(jni, j_pc)->AddStream(
reinterpret_cast<MediaStreamInterface*>(native_stream));
}
JNI_FUNCTION_DECLARATION(void,
PeerConnection_nativeRemoveLocalStream,
JNIEnv* jni,
jobject j_pc,
jlong native_stream) {
ExtractNativePC(jni, j_pc)->RemoveStream(
reinterpret_cast<MediaStreamInterface*>(native_stream));
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_nativeCreateSender,
JNIEnv* jni,
jobject j_pc,
jstring j_kind,
jstring j_stream_id) {
jclass j_rtp_sender_class = FindClass(jni, "org/webrtc/RtpSender");
jmethodID j_rtp_sender_ctor =
GetMethodID(jni, j_rtp_sender_class, "<init>", "(J)V");
std::string kind = JavaToStdString(jni, j_kind);
std::string stream_id = JavaToStdString(jni, j_stream_id);
rtc::scoped_refptr<RtpSenderInterface> sender =
ExtractNativePC(jni, j_pc)->CreateSender(kind, stream_id);
if (!sender.get()) {
return nullptr;
}
jlong nativeSenderPtr = jlongFromPointer(sender.get());
jobject j_sender =
jni->NewObject(j_rtp_sender_class, j_rtp_sender_ctor, nativeSenderPtr);
CHECK_EXCEPTION(jni) << "error during NewObject";
// Sender is now owned by the Java object, and will be freed from
// RtpSender.dispose(), called by PeerConnection.dispose() or getSenders().
sender->AddRef();
return j_sender;
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_nativeGetSenders,
JNIEnv* jni,
jobject j_pc) {
jclass j_array_list_class = FindClass(jni, "java/util/ArrayList");
jmethodID j_array_list_ctor =
GetMethodID(jni, j_array_list_class, "<init>", "()V");
jmethodID j_array_list_add =
GetMethodID(jni, j_array_list_class, "add", "(Ljava/lang/Object;)Z");
jobject j_senders = jni->NewObject(j_array_list_class, j_array_list_ctor);
CHECK_EXCEPTION(jni) << "error during NewObject";
jclass j_rtp_sender_class = FindClass(jni, "org/webrtc/RtpSender");
jmethodID j_rtp_sender_ctor =
GetMethodID(jni, j_rtp_sender_class, "<init>", "(J)V");
auto senders = ExtractNativePC(jni, j_pc)->GetSenders();
for (const auto& sender : senders) {
jlong nativeSenderPtr = jlongFromPointer(sender.get());
jobject j_sender =
jni->NewObject(j_rtp_sender_class, j_rtp_sender_ctor, nativeSenderPtr);
CHECK_EXCEPTION(jni) << "error during NewObject";
// Sender is now owned by the Java object, and will be freed from
// RtpSender.dispose(), called by PeerConnection.dispose() or getSenders().
sender->AddRef();
jni->CallBooleanMethod(j_senders, j_array_list_add, j_sender);
CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
}
return j_senders;
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_nativeGetReceivers,
JNIEnv* jni,
jobject j_pc) {
jclass j_array_list_class = FindClass(jni, "java/util/ArrayList");
jmethodID j_array_list_ctor =
GetMethodID(jni, j_array_list_class, "<init>", "()V");
jmethodID j_array_list_add =
GetMethodID(jni, j_array_list_class, "add", "(Ljava/lang/Object;)Z");
jobject j_receivers = jni->NewObject(j_array_list_class, j_array_list_ctor);
CHECK_EXCEPTION(jni) << "error during NewObject";
jclass j_rtp_receiver_class = FindClass(jni, "org/webrtc/RtpReceiver");
jmethodID j_rtp_receiver_ctor =
GetMethodID(jni, j_rtp_receiver_class, "<init>", "(J)V");
auto receivers = ExtractNativePC(jni, j_pc)->GetReceivers();
for (const auto& receiver : receivers) {
jlong nativeReceiverPtr = jlongFromPointer(receiver.get());
jobject j_receiver = jni->NewObject(j_rtp_receiver_class,
j_rtp_receiver_ctor, nativeReceiverPtr);
CHECK_EXCEPTION(jni) << "error during NewObject";
// Receiver is now owned by Java object, and will be freed from there.
receiver->AddRef();
jni->CallBooleanMethod(j_receivers, j_array_list_add, j_receiver);
CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
}
return j_receivers;
}
JNI_FUNCTION_DECLARATION(bool,
PeerConnection_nativeOldGetStats,
JNIEnv* jni,
jobject j_pc,
jobject j_observer,
jlong native_track) {
rtc::scoped_refptr<StatsObserverJni> observer(
new rtc::RefCountedObject<StatsObserverJni>(jni, j_observer));
return ExtractNativePC(jni, j_pc)->GetStats(
observer, reinterpret_cast<MediaStreamTrackInterface*>(native_track),
PeerConnectionInterface::kStatsOutputLevelStandard);
}
JNI_FUNCTION_DECLARATION(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);
}
JNI_FUNCTION_DECLARATION(jboolean,
PeerConnection_setBitrate,
JNIEnv* jni,
jobject j_pc,
jobject j_min,
jobject j_current,
jobject j_max) {
PeerConnectionInterface::BitrateParameters params;
jclass j_integer_class = jni->FindClass("java/lang/Integer");
jmethodID int_value_id = GetMethodID(jni, j_integer_class, "intValue", "()I");
if (!IsNull(jni, j_min)) {
int min_value = jni->CallIntMethod(j_min, int_value_id);
params.min_bitrate_bps = rtc::Optional<int>(min_value);
}
if (!IsNull(jni, j_current)) {
int current_value = jni->CallIntMethod(j_current, int_value_id);
params.current_bitrate_bps = rtc::Optional<int>(current_value);
}
if (!IsNull(jni, j_max)) {
int max_value = jni->CallIntMethod(j_max, int_value_id);
params.max_bitrate_bps = rtc::Optional<int>(max_value);
}
return ExtractNativePC(jni, j_pc)->SetBitrate(params).ok();
}
JNI_FUNCTION_DECLARATION(bool,
PeerConnection_nativeStartRtcEventLog,
JNIEnv* jni,
jobject j_pc,
int file_descriptor,
int max_size_bytes) {
return ExtractNativePC(jni, j_pc)->StartRtcEventLog(file_descriptor,
max_size_bytes);
}
JNI_FUNCTION_DECLARATION(void,
PeerConnection_nativeStopRtcEventLog,
JNIEnv* jni,
jobject j_pc) {
ExtractNativePC(jni, j_pc)->StopRtcEventLog();
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_signalingState,
JNIEnv* jni,
jobject j_pc) {
PeerConnectionInterface::SignalingState state =
ExtractNativePC(jni, j_pc)->signaling_state();
return JavaEnumFromIndexAndClassName(jni, "PeerConnection$SignalingState",
state);
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_iceConnectionState,
JNIEnv* jni,
jobject j_pc) {
PeerConnectionInterface::IceConnectionState state =
ExtractNativePC(jni, j_pc)->ice_connection_state();
return JavaEnumFromIndexAndClassName(jni, "PeerConnection$IceConnectionState",
state);
}
JNI_FUNCTION_DECLARATION(jobject,
PeerConnection_iceGatheringState,
JNIEnv* jni,
jobject j_pc) {
PeerConnectionInterface::IceGatheringState state =
ExtractNativePC(jni, j_pc)->ice_gathering_state();
return JavaEnumFromIndexAndClassName(jni, "PeerConnection$IceGatheringState",
state);
}
JNI_FUNCTION_DECLARATION(void,
PeerConnection_close,
JNIEnv* jni,
jobject j_pc) {
ExtractNativePC(jni, j_pc)->Close();
return;
}
} // namespace jni
} // namespace webrtc