diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index 0be6195271..202b2c0f62 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -287,6 +287,7 @@ generate_jni("generated_peerconnection_jni") { "api/org/webrtc/DataChannel.java", "api/org/webrtc/IceCandidate.java", "api/org/webrtc/MediaConstraints.java", + "api/org/webrtc/MediaStream.java", "api/org/webrtc/NetworkMonitor.java", "api/org/webrtc/NetworkMonitorAutoDetect.java", "api/org/webrtc/RTCStats.java", diff --git a/sdk/android/api/org/webrtc/MediaStream.java b/sdk/android/api/org/webrtc/MediaStream.java index 798dd9944e..59caf386c9 100644 --- a/sdk/android/api/org/webrtc/MediaStream.java +++ b/sdk/android/api/org/webrtc/MediaStream.java @@ -12,21 +12,25 @@ package org.webrtc; import java.util.ArrayList; import java.util.List; +import java.util.Iterator; /** Java wrapper for a C++ MediaStreamInterface. */ public class MediaStream { + private static final String TAG = "MediaStream"; + public final List audioTracks = new ArrayList<>(); public final List videoTracks = new ArrayList<>(); public final List preservedVideoTracks = new ArrayList<>(); // Package-protected for PeerConnection. final long nativeStream; + @CalledByNative public MediaStream(long nativeStream) { this.nativeStream = nativeStream; } public boolean addTrack(AudioTrack track) { - if (nativeAddAudioTrack(nativeStream, track.nativeTrack)) { + if (addAudioTrackToNativeStream(nativeStream, track.nativeTrack)) { audioTracks.add(track); return true; } @@ -34,7 +38,7 @@ public class MediaStream { } public boolean addTrack(VideoTrack track) { - if (nativeAddVideoTrack(nativeStream, track.nativeTrack)) { + if (addVideoTrackToNativeStream(nativeStream, track.nativeTrack)) { videoTracks.add(track); return true; } @@ -45,7 +49,7 @@ public class MediaStream { // is called. If video track need to be preserved after MediaStream is destroyed it // should be added to MediaStream using addPreservedTrack() call. public boolean addPreservedTrack(VideoTrack track) { - if (nativeAddVideoTrack(nativeStream, track.nativeTrack)) { + if (addVideoTrackToNativeStream(nativeStream, track.nativeTrack)) { preservedVideoTracks.add(track); return true; } @@ -54,15 +58,16 @@ public class MediaStream { public boolean removeTrack(AudioTrack track) { audioTracks.remove(track); - return nativeRemoveAudioTrack(nativeStream, track.nativeTrack); + return removeNativeAudioTrack(nativeStream, track.nativeTrack); } public boolean removeTrack(VideoTrack track) { videoTracks.remove(track); preservedVideoTracks.remove(track); - return nativeRemoveVideoTrack(nativeStream, track.nativeTrack); + return removeNativeVideoTrack(nativeStream, track.nativeTrack); } + @CalledByNative public void dispose() { // Remove and release previously added audio and video tracks. while (!audioTracks.isEmpty()) { @@ -83,7 +88,7 @@ public class MediaStream { } public String label() { - return nativeLabel(nativeStream); + return getNativeLabel(nativeStream); } @Override @@ -91,15 +96,51 @@ public class MediaStream { return "[" + label() + ":A=" + audioTracks.size() + ":V=" + videoTracks.size() + "]"; } - private static native boolean nativeAddAudioTrack(long nativeStream, long nativeAudioTrack); + @CalledByNative + void addNativeAudioTrack(long nativeTrack) { + audioTracks.add(new AudioTrack(nativeTrack)); + } - private static native boolean nativeAddVideoTrack(long nativeStream, long nativeVideoTrack); + @CalledByNative + void addNativeVideoTrack(long nativeTrack) { + videoTracks.add(new VideoTrack(nativeTrack)); + } - private static native boolean nativeRemoveAudioTrack(long nativeStream, long nativeAudioTrack); + @CalledByNative + void removeAudioTrack(long nativeTrack) { + removeMediaStreamTrack(audioTracks, nativeTrack); + } - private static native boolean nativeRemoveVideoTrack(long nativeStream, long nativeVideoTrack); + @CalledByNative + void removeVideoTrack(long nativeTrack) { + removeMediaStreamTrack(videoTracks, nativeTrack); + } - private static native String nativeLabel(long nativeStream); + private static void removeMediaStreamTrack( + List tracks, long nativeTrack) { + final Iterator it = tracks.iterator(); + while (it.hasNext()) { + MediaStreamTrack track = it.next(); + if (track.nativeTrack == nativeTrack) { + track.dispose(); + it.remove(); + return; + } + } + Logging.e(TAG, "Couldn't not find track"); + } + + private static native boolean addAudioTrackToNativeStream( + long nativeStream, long nativeAudioTrack); + + private static native boolean addVideoTrackToNativeStream( + long nativeStream, long nativeVideoTrack); + + private static native boolean removeNativeAudioTrack(long nativeStream, long nativeAudioTrack); + + private static native boolean removeNativeVideoTrack(long nativeStream, long nativeVideoTrack); + + private static native String getNativeLabel(long nativeStream); private static native void free(long nativeStream); } diff --git a/sdk/android/src/jni/classreferenceholder.cc b/sdk/android/src/jni/classreferenceholder.cc index f578c475f7..50d45ed326 100644 --- a/sdk/android/src/jni/classreferenceholder.cc +++ b/sdk/android/src/jni/classreferenceholder.cc @@ -59,7 +59,6 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { 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"); LoadClass(jni, "org/webrtc/CameraEnumerationAndroid"); @@ -76,7 +75,6 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$VideoCodecType"); LoadClass(jni, "org/webrtc/MediaSource$State"); - LoadClass(jni, "org/webrtc/MediaStream"); LoadClass(jni, "org/webrtc/MediaStreamTrack"); LoadClass(jni, "org/webrtc/MediaStreamTrack$MediaType"); LoadClass(jni, "org/webrtc/MediaStreamTrack$State"); @@ -114,7 +112,6 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/VideoFrame$TextureBuffer"); LoadClass(jni, "org/webrtc/VideoRenderer$I420Frame"); LoadClass(jni, "org/webrtc/VideoSink"); - LoadClass(jni, "org/webrtc/VideoTrack"); LoadClass(jni, "org/webrtc/WrappedNativeI420Buffer"); } diff --git a/sdk/android/src/jni/pc/mediastream_jni.cc b/sdk/android/src/jni/pc/mediastream_jni.cc index e9c7309e71..010f355409 100644 --- a/sdk/android/src/jni/pc/mediastream_jni.cc +++ b/sdk/android/src/jni/pc/mediastream_jni.cc @@ -15,7 +15,7 @@ namespace webrtc { namespace jni { JNI_FUNCTION_DECLARATION(jboolean, - MediaStream_nativeAddAudioTrack, + MediaStream_addAudioTrackToNativeStream, JNIEnv* jni, jclass, jlong pointer, @@ -25,7 +25,7 @@ JNI_FUNCTION_DECLARATION(jboolean, } JNI_FUNCTION_DECLARATION(jboolean, - MediaStream_nativeAddVideoTrack, + MediaStream_addVideoTrackToNativeStream, JNIEnv* jni, jclass, jlong pointer, @@ -35,7 +35,7 @@ JNI_FUNCTION_DECLARATION(jboolean, } JNI_FUNCTION_DECLARATION(jboolean, - MediaStream_nativeRemoveAudioTrack, + MediaStream_removeNativeAudioTrack, JNIEnv* jni, jclass, jlong pointer, @@ -45,7 +45,7 @@ JNI_FUNCTION_DECLARATION(jboolean, } JNI_FUNCTION_DECLARATION(jboolean, - MediaStream_nativeRemoveVideoTrack, + MediaStream_removeNativeVideoTrack, JNIEnv* jni, jclass, jlong pointer, @@ -55,7 +55,7 @@ JNI_FUNCTION_DECLARATION(jboolean, } JNI_FUNCTION_DECLARATION(jstring, - MediaStream_nativeLabel, + MediaStream_getNativeLabel, JNIEnv* jni, jclass, jlong j_p) { diff --git a/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc b/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc index 8bb098ef6c..3da15fae4e 100644 --- a/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc +++ b/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc @@ -14,6 +14,7 @@ #include #include "rtc_base/ptr_util.h" +#include "sdk/android/generated_peerconnection_jni/jni/MediaStream_jni.h" #include "sdk/android/src/jni/classreferenceholder.h" #include "sdk/android/src/jni/pc/datachannel.h" #include "sdk/android/src/jni/pc/java_native_conversion.h" @@ -31,22 +32,6 @@ 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_, "", "(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_, "", "(J)V")), - j_video_track_class_(jni, FindClass(jni, "org/webrtc/VideoTrack")), - j_video_track_ctor_( - GetMethodID(jni, *j_video_track_class_, "", "(J)V")), j_rtp_receiver_class_(jni, FindClass(jni, "org/webrtc/RtpReceiver")), j_rtp_receiver_ctor_( GetMethodID(jni, *j_rtp_receiver_class_, "", "(J)V")) {} @@ -159,66 +144,17 @@ void PeerConnectionObserverJni::OnAddStream( void PeerConnectionObserverJni::AddNativeAudioTrackToJavaStream( rtc::scoped_refptr 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(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); + JNIEnv* env = AttachCurrentThreadIfNeeded(); + Java_MediaStream_addNativeAudioTrack(env, j_stream, + jlongFromPointer(track.release())); } void PeerConnectionObserverJni::AddNativeVideoTrackToJavaStream( rtc::scoped_refptr 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(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( - 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(); + JNIEnv* env = AttachCurrentThreadIfNeeded(); + Java_MediaStream_addNativeVideoTrack(env, j_stream, + jlongFromPointer(track.release())); } void PeerConnectionObserverJni::OnAudioTrackAddedToStream( @@ -240,23 +176,19 @@ void PeerConnectionObserverJni::OnVideoTrackAddedToStream( void PeerConnectionObserverJni::OnAudioTrackRemovedFromStream( AudioTrackInterface* track, MediaStreamInterface* stream) { - ScopedLocalRefFrame local_ref_frame(jni()); + JNIEnv* env = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(env); 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); + Java_MediaStream_removeAudioTrack(env, j_stream, jlongFromPointer(track)); } void PeerConnectionObserverJni::OnVideoTrackRemovedFromStream( VideoTrackInterface* track, MediaStreamInterface* stream) { - ScopedLocalRefFrame local_ref_frame(jni()); + JNIEnv* env = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(env); 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); + Java_MediaStream_removeVideoTrack(env, j_stream, jlongFromPointer(track)); } void PeerConnectionObserverJni::OnRemoveStream( @@ -336,10 +268,9 @@ void PeerConnectionObserverJni::DisposeRemoteStream( 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); + JNIEnv* env = AttachCurrentThreadIfNeeded(); + Java_MediaStream_dispose(env, j_stream); + DeleteGlobalRef(env, j_stream); } void PeerConnectionObserverJni::DisposeRtpReceiver( @@ -365,9 +296,7 @@ jobject PeerConnectionObserverJni::GetOrCreateJavaStream( // MediaStream_free, triggered by MediaStream.dispose(). stream->AddRef(); jobject j_stream = - jni()->NewObject(*j_media_stream_class_, j_media_stream_ctor_, - reinterpret_cast(stream.get())); - CHECK_EXCEPTION(jni()) << "error during NewObject"; + Java_MediaStream_Constructor(jni(), jlongFromPointer(stream.get())); remote_streams_[stream] = NewGlobalRef(jni(), j_stream); return j_stream; @@ -376,8 +305,8 @@ jobject PeerConnectionObserverJni::GetOrCreateJavaStream( jobjectArray PeerConnectionObserverJni::NativeToJavaMediaStreamArray( JNIEnv* jni, const std::vector>& streams) { - jobjectArray java_streams = - jni->NewObjectArray(streams.size(), *j_media_stream_class_, nullptr); + jobjectArray java_streams = jni->NewObjectArray( + streams.size(), org_webrtc_MediaStream_clazz(jni), nullptr); CHECK_EXCEPTION(jni) << "error during NewObjectArray"; for (size_t i = 0; i < streams.size(); ++i) { jobject j_stream = GetOrCreateJavaStream(streams[i]); diff --git a/sdk/android/src/jni/pc/peerconnectionobserver_jni.h b/sdk/android/src/jni/pc/peerconnectionobserver_jni.h index 44eb319a61..6f95a38567 100644 --- a/sdk/android/src/jni/pc/peerconnectionobserver_jni.h +++ b/sdk/android/src/jni/pc/peerconnectionobserver_jni.h @@ -87,14 +87,6 @@ class PeerConnectionObserverJni : public PeerConnectionObserver, void AddNativeVideoTrackToJavaStream( rtc::scoped_refptr track, jobject j_stream); - // Remove and dispose the Java MediaStreamTrack object that wraps |track|, - // given |j_tracks| which is a linked list of tracks (either the videoTracks - // or audioTracks member of MediaStream). - // - // DCHECKs if the track isn't found. - void RemoveAndDisposeNativeTrackFromJavaTrackList( - MediaStreamTrackInterface* track, - jobject j_tracks); // Callbacks invoked when a native stream changes, and the Java stream needs // to be updated; MediaStreamObserver is used to make this simpler. @@ -109,15 +101,6 @@ class PeerConnectionObserverJni : public PeerConnectionObserver, const ScopedGlobalRef j_observer_global_; const ScopedGlobalRef j_observer_class_; - const ScopedGlobalRef j_media_stream_class_; - const jmethodID j_media_stream_ctor_; - const ScopedGlobalRef j_media_stream_track_class_; - const jmethodID j_track_dispose_id_; - const jfieldID j_native_track_id_; - const ScopedGlobalRef j_audio_track_class_; - const jmethodID j_audio_track_ctor_; - const ScopedGlobalRef j_video_track_class_; - const jmethodID j_video_track_ctor_; const ScopedGlobalRef j_rtp_receiver_class_; const jmethodID j_rtp_receiver_ctor_;