blob: a0eac9185bfc2e48e4a37531787691d5447a19b5 [file] [log] [blame]
/*
* 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 "sdk/android/src/jni/pc/peerconnectionobserver_jni.h"
#include <pc/mediastreamobserver.h>
#include <string>
#include "rtc_base/ptr_util.h"
#include "sdk/android/src/jni/classreferenceholder.h"
#include "sdk/android/src/jni/pc/java_native_conversion.h"
namespace webrtc {
namespace jni {
// Convenience, used since callbacks occur on the signaling thread, which may
// be a non-Java thread.
static JNIEnv* jni() {
return AttachCurrentThreadIfNeeded();
}
PeerConnectionObserverJni::PeerConnectionObserverJni(JNIEnv* jni,
jobject j_observer)
: j_observer_global_(jni, j_observer),
j_observer_class_(jni, GetObjectClass(jni, *j_observer_global_)),
j_media_stream_class_(jni, FindClass(jni, "org/webrtc/MediaStream")),
j_media_stream_ctor_(
GetMethodID(jni, *j_media_stream_class_, "<init>", "(J)V")),
j_media_stream_track_class_(
jni,
FindClass(jni, "org/webrtc/MediaStreamTrack")),
j_track_dispose_id_(
GetMethodID(jni, *j_media_stream_track_class_, "dispose", "()V")),
j_native_track_id_(
GetFieldID(jni, *j_media_stream_track_class_, "nativeTrack", "J")),
j_audio_track_class_(jni, FindClass(jni, "org/webrtc/AudioTrack")),
j_audio_track_ctor_(
GetMethodID(jni, *j_audio_track_class_, "<init>", "(J)V")),
j_video_track_class_(jni, FindClass(jni, "org/webrtc/VideoTrack")),
j_video_track_ctor_(
GetMethodID(jni, *j_video_track_class_, "<init>", "(J)V")),
j_data_channel_class_(jni, FindClass(jni, "org/webrtc/DataChannel")),
j_data_channel_ctor_(
GetMethodID(jni, *j_data_channel_class_, "<init>", "(J)V")),
j_rtp_receiver_class_(jni, FindClass(jni, "org/webrtc/RtpReceiver")),
j_rtp_receiver_ctor_(
GetMethodID(jni, *j_rtp_receiver_class_, "<init>", "(J)V")) {}
PeerConnectionObserverJni::~PeerConnectionObserverJni() {
ScopedLocalRefFrame local_ref_frame(jni());
while (!remote_streams_.empty())
DisposeRemoteStream(remote_streams_.begin());
while (!rtp_receivers_.empty())
DisposeRtpReceiver(rtp_receivers_.begin());
}
void PeerConnectionObserverJni::OnIceCandidate(
const IceCandidateInterface* candidate) {
ScopedLocalRefFrame local_ref_frame(jni());
std::string sdp;
RTC_CHECK(candidate->ToString(&sdp)) << "got so far: " << sdp;
jclass candidate_class = FindClass(jni(), "org/webrtc/IceCandidate");
jmethodID ctor =
GetMethodID(jni(), candidate_class, "<init>",
"(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V");
jstring j_mid = JavaStringFromStdString(jni(), candidate->sdp_mid());
jstring j_sdp = JavaStringFromStdString(jni(), sdp);
jstring j_url = JavaStringFromStdString(jni(), candidate->candidate().url());
jobject j_candidate = jni()->NewObject(
candidate_class, ctor, j_mid, candidate->sdp_mline_index(), j_sdp, j_url);
CHECK_EXCEPTION(jni()) << "error during NewObject";
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onIceCandidate",
"(Lorg/webrtc/IceCandidate;)V");
jni()->CallVoidMethod(*j_observer_global_, m, j_candidate);
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
}
void PeerConnectionObserverJni::OnIceCandidatesRemoved(
const std::vector<cricket::Candidate>& candidates) {
ScopedLocalRefFrame local_ref_frame(jni());
jobjectArray candidates_array = NativeToJavaCandidateArray(jni(), candidates);
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onIceCandidatesRemoved",
"([Lorg/webrtc/IceCandidate;)V");
jni()->CallVoidMethod(*j_observer_global_, m, candidates_array);
CHECK_EXCEPTION(jni()) << "Error during CallVoidMethod";
}
void PeerConnectionObserverJni::OnSignalingChange(
PeerConnectionInterface::SignalingState new_state) {
ScopedLocalRefFrame local_ref_frame(jni());
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onSignalingChange",
"(Lorg/webrtc/PeerConnection$SignalingState;)V");
jobject new_state_enum = JavaEnumFromIndexAndClassName(
jni(), "PeerConnection$SignalingState", new_state);
jni()->CallVoidMethod(*j_observer_global_, m, new_state_enum);
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
}
void PeerConnectionObserverJni::OnIceConnectionChange(
PeerConnectionInterface::IceConnectionState new_state) {
ScopedLocalRefFrame local_ref_frame(jni());
jmethodID m =
GetMethodID(jni(), *j_observer_class_, "onIceConnectionChange",
"(Lorg/webrtc/PeerConnection$IceConnectionState;)V");
jobject new_state_enum = JavaEnumFromIndexAndClassName(
jni(), "PeerConnection$IceConnectionState", new_state);
jni()->CallVoidMethod(*j_observer_global_, m, new_state_enum);
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
}
void PeerConnectionObserverJni::OnIceConnectionReceivingChange(bool receiving) {
ScopedLocalRefFrame local_ref_frame(jni());
jmethodID m = GetMethodID(jni(), *j_observer_class_,
"onIceConnectionReceivingChange", "(Z)V");
jni()->CallVoidMethod(*j_observer_global_, m, receiving);
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
}
void PeerConnectionObserverJni::OnIceGatheringChange(
PeerConnectionInterface::IceGatheringState new_state) {
ScopedLocalRefFrame local_ref_frame(jni());
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onIceGatheringChange",
"(Lorg/webrtc/PeerConnection$IceGatheringState;)V");
jobject new_state_enum = JavaEnumFromIndexAndClassName(
jni(), "PeerConnection$IceGatheringState", new_state);
jni()->CallVoidMethod(*j_observer_global_, m, new_state_enum);
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
}
void PeerConnectionObserverJni::OnAddStream(
rtc::scoped_refptr<MediaStreamInterface> stream) {
ScopedLocalRefFrame local_ref_frame(jni());
// The stream could be added into the remote_streams_ map when calling
// OnAddTrack.
jobject j_stream = GetOrCreateJavaStream(stream);
for (const auto& track : stream->GetAudioTracks()) {
AddNativeAudioTrackToJavaStream(track, j_stream);
}
for (const auto& track : stream->GetVideoTracks()) {
AddNativeVideoTrackToJavaStream(track, j_stream);
}
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onAddStream",
"(Lorg/webrtc/MediaStream;)V");
jni()->CallVoidMethod(*j_observer_global_, m, j_stream);
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
// Create an observer to update the Java stream when the native stream's set
// of tracks changes.
auto observer = rtc::MakeUnique<MediaStreamObserver>(stream);
observer->SignalAudioTrackRemoved.connect(
this, &PeerConnectionObserverJni::OnAudioTrackRemovedFromStream);
observer->SignalVideoTrackRemoved.connect(
this, &PeerConnectionObserverJni::OnVideoTrackRemovedFromStream);
observer->SignalAudioTrackAdded.connect(
this, &PeerConnectionObserverJni::OnAudioTrackAddedToStream);
observer->SignalVideoTrackAdded.connect(
this, &PeerConnectionObserverJni::OnVideoTrackAddedToStream);
stream_observers_.push_back(std::move(observer));
}
void PeerConnectionObserverJni::AddNativeAudioTrackToJavaStream(
rtc::scoped_refptr<AudioTrackInterface> track,
jobject j_stream) {
jstring id = JavaStringFromStdString(jni(), track->id());
// Java AudioTrack holds one reference. Corresponding Release() is in
// MediaStreamTrack_free, triggered by AudioTrack.dispose().
track->AddRef();
jobject j_track = jni()->NewObject(*j_audio_track_class_, j_audio_track_ctor_,
reinterpret_cast<jlong>(track.get()), id);
CHECK_EXCEPTION(jni()) << "error during NewObject";
// Now add to the audioTracks linked list.
jfieldID audio_tracks_id = GetFieldID(jni(), *j_media_stream_class_,
"audioTracks", "Ljava/util/List;");
jobject audio_tracks = GetObjectField(jni(), j_stream, audio_tracks_id);
jmethodID add = GetMethodID(jni(), GetObjectClass(jni(), audio_tracks), "add",
"(Ljava/lang/Object;)Z");
jboolean added = jni()->CallBooleanMethod(audio_tracks, add, j_track);
CHECK_EXCEPTION(jni()) << "error during CallBooleanMethod";
RTC_CHECK(added);
}
void PeerConnectionObserverJni::AddNativeVideoTrackToJavaStream(
rtc::scoped_refptr<VideoTrackInterface> track,
jobject j_stream) {
jstring id = JavaStringFromStdString(jni(), track->id());
// Java VideoTrack holds one reference. Corresponding Release() is in
// MediaStreamTrack_free, triggered by VideoTrack.dispose().
track->AddRef();
jobject j_track = jni()->NewObject(*j_video_track_class_, j_video_track_ctor_,
reinterpret_cast<jlong>(track.get()), id);
CHECK_EXCEPTION(jni()) << "error during NewObject";
// Now add to the videoTracks linked list.
jfieldID video_tracks_id = GetFieldID(jni(), *j_media_stream_class_,
"videoTracks", "Ljava/util/List;");
jobject video_tracks = GetObjectField(jni(), j_stream, video_tracks_id);
jmethodID add = GetMethodID(jni(), GetObjectClass(jni(), video_tracks), "add",
"(Ljava/lang/Object;)Z");
jboolean added = jni()->CallBooleanMethod(video_tracks, add, j_track);
CHECK_EXCEPTION(jni()) << "error during CallBooleanMethod";
RTC_CHECK(added);
}
void PeerConnectionObserverJni::RemoveAndDisposeNativeTrackFromJavaTrackList(
MediaStreamTrackInterface* track,
jobject j_tracks) {
Iterable iterable_tracks(jni(), j_tracks);
for (auto it = iterable_tracks.begin(); it != iterable_tracks.end(); ++it) {
MediaStreamTrackInterface* native_track =
reinterpret_cast<MediaStreamTrackInterface*>(
jni()->GetLongField(*it, j_native_track_id_));
CHECK_EXCEPTION(jni()) << "error during GetLongField";
if (native_track == track) {
jni()->CallVoidMethod(*it, j_track_dispose_id_);
it.Remove();
return;
}
}
// If we reached this point, we didn't find the track, which means we're
// getting a "track removed" callback but the Java stream doesn't have a
// corresponding track, which indicates a bug somewhere.
RTC_NOTREACHED();
}
void PeerConnectionObserverJni::OnAudioTrackAddedToStream(
AudioTrackInterface* track,
MediaStreamInterface* stream) {
ScopedLocalRefFrame local_ref_frame(jni());
jobject j_stream = GetOrCreateJavaStream(stream);
AddNativeAudioTrackToJavaStream(track, j_stream);
}
void PeerConnectionObserverJni::OnVideoTrackAddedToStream(
VideoTrackInterface* track,
MediaStreamInterface* stream) {
ScopedLocalRefFrame local_ref_frame(jni());
jobject j_stream = GetOrCreateJavaStream(stream);
AddNativeVideoTrackToJavaStream(track, j_stream);
}
void PeerConnectionObserverJni::OnAudioTrackRemovedFromStream(
AudioTrackInterface* track,
MediaStreamInterface* stream) {
ScopedLocalRefFrame local_ref_frame(jni());
jobject j_stream = GetOrCreateJavaStream(stream);
jfieldID audio_tracks_id = GetFieldID(jni(), *j_media_stream_class_,
"audioTracks", "Ljava/util/List;");
jobject audio_tracks = GetObjectField(jni(), j_stream, audio_tracks_id);
RemoveAndDisposeNativeTrackFromJavaTrackList(track, audio_tracks);
}
void PeerConnectionObserverJni::OnVideoTrackRemovedFromStream(
VideoTrackInterface* track,
MediaStreamInterface* stream) {
ScopedLocalRefFrame local_ref_frame(jni());
jobject j_stream = GetOrCreateJavaStream(stream);
jfieldID video_tracks_id = GetFieldID(jni(), *j_media_stream_class_,
"videoTracks", "Ljava/util/List;");
jobject video_tracks = GetObjectField(jni(), j_stream, video_tracks_id);
RemoveAndDisposeNativeTrackFromJavaTrackList(track, video_tracks);
}
void PeerConnectionObserverJni::OnRemoveStream(
rtc::scoped_refptr<MediaStreamInterface> stream) {
ScopedLocalRefFrame local_ref_frame(jni());
NativeToJavaStreamsMap::iterator it = remote_streams_.find(stream);
RTC_CHECK(it != remote_streams_.end())
<< "unexpected stream: " << std::hex << stream;
jobject j_stream = it->second;
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onRemoveStream",
"(Lorg/webrtc/MediaStream;)V");
jni()->CallVoidMethod(*j_observer_global_, m, j_stream);
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
// Release the refptr reference so that DisposeRemoteStream can assert
// it removes the final reference.
stream = nullptr;
DisposeRemoteStream(it);
}
void PeerConnectionObserverJni::OnDataChannel(
rtc::scoped_refptr<DataChannelInterface> channel) {
ScopedLocalRefFrame local_ref_frame(jni());
jobject j_channel =
jni()->NewObject(*j_data_channel_class_, j_data_channel_ctor_,
jlongFromPointer(channel.get()));
CHECK_EXCEPTION(jni()) << "error during NewObject";
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onDataChannel",
"(Lorg/webrtc/DataChannel;)V");
jni()->CallVoidMethod(*j_observer_global_, m, j_channel);
// Channel is now owned by Java object, and will be freed from
// DataChannel.dispose(). Important that this be done _after_ the
// CallVoidMethod above as Java code might call back into native code and be
// surprised to see a refcount of 2.
channel->AddRef();
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
}
void PeerConnectionObserverJni::OnRenegotiationNeeded() {
ScopedLocalRefFrame local_ref_frame(jni());
jmethodID m =
GetMethodID(jni(), *j_observer_class_, "onRenegotiationNeeded", "()V");
jni()->CallVoidMethod(*j_observer_global_, m);
CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
}
void PeerConnectionObserverJni::OnAddTrack(
rtc::scoped_refptr<RtpReceiverInterface> receiver,
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
ScopedLocalRefFrame local_ref_frame(jni());
jobject j_rtp_receiver =
jni()->NewObject(*j_rtp_receiver_class_, j_rtp_receiver_ctor_,
jlongFromPointer(receiver.get()));
CHECK_EXCEPTION(jni()) << "error during NewObject";
receiver->AddRef();
rtp_receivers_[receiver] = NewGlobalRef(jni(), j_rtp_receiver);
jobjectArray j_stream_array = NativeToJavaMediaStreamArray(jni(), streams);
jmethodID m =
GetMethodID(jni(), *j_observer_class_, "onAddTrack",
"(Lorg/webrtc/RtpReceiver;[Lorg/webrtc/MediaStream;)V");
jni()->CallVoidMethod(*j_observer_global_, m, j_rtp_receiver, j_stream_array);
CHECK_EXCEPTION(jni()) << "Error during CallVoidMethod";
}
void PeerConnectionObserverJni::SetConstraints(
std::unique_ptr<MediaConstraintsInterface> constraints) {
RTC_CHECK(!constraints_.get()) << "constraints already set!";
constraints_ = std::move(constraints);
}
void PeerConnectionObserverJni::DisposeRemoteStream(
const NativeToJavaStreamsMap::iterator& it) {
MediaStreamInterface* stream = it->first;
jobject j_stream = it->second;
// Remove the observer first, so it doesn't react to events during deletion.
stream_observers_.erase(
std::remove_if(
stream_observers_.begin(), stream_observers_.end(),
[stream](const std::unique_ptr<MediaStreamObserver>& observer) {
return observer->stream() == stream;
}),
stream_observers_.end());
remote_streams_.erase(it);
jni()->CallVoidMethod(
j_stream, GetMethodID(jni(), *j_media_stream_class_, "dispose", "()V"));
CHECK_EXCEPTION(jni()) << "error during MediaStream.dispose()";
DeleteGlobalRef(jni(), j_stream);
}
void PeerConnectionObserverJni::DisposeRtpReceiver(
const NativeToJavaRtpReceiverMap::iterator& it) {
jobject j_rtp_receiver = it->second;
rtp_receivers_.erase(it);
jni()->CallVoidMethod(
j_rtp_receiver,
GetMethodID(jni(), *j_rtp_receiver_class_, "dispose", "()V"));
CHECK_EXCEPTION(jni()) << "error during RtpReceiver.dispose()";
DeleteGlobalRef(jni(), j_rtp_receiver);
}
// If the NativeToJavaStreamsMap contains the stream, return it.
// Otherwise, create a new Java MediaStream.
jobject PeerConnectionObserverJni::GetOrCreateJavaStream(
const rtc::scoped_refptr<MediaStreamInterface>& stream) {
NativeToJavaStreamsMap::iterator it = remote_streams_.find(stream);
if (it != remote_streams_.end()) {
return it->second;
}
// Java MediaStream holds one reference. Corresponding Release() is in
// MediaStream_free, triggered by MediaStream.dispose().
stream->AddRef();
jobject j_stream =
jni()->NewObject(*j_media_stream_class_, j_media_stream_ctor_,
reinterpret_cast<jlong>(stream.get()));
CHECK_EXCEPTION(jni()) << "error during NewObject";
remote_streams_[stream] = NewGlobalRef(jni(), j_stream);
return j_stream;
}
jobjectArray PeerConnectionObserverJni::NativeToJavaMediaStreamArray(
JNIEnv* jni,
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
jobjectArray java_streams =
jni->NewObjectArray(streams.size(), *j_media_stream_class_, nullptr);
CHECK_EXCEPTION(jni) << "error during NewObjectArray";
for (size_t i = 0; i < streams.size(); ++i) {
jobject j_stream = GetOrCreateJavaStream(streams[i]);
jni->SetObjectArrayElement(java_streams, i, j_stream);
}
return java_streams;
}
} // namespace jni
} // namespace webrtc