diff --git a/talk/app/webrtc/java/jni/classreferenceholder.cc b/talk/app/webrtc/java/jni/classreferenceholder.cc index 426e76b719..25b72da65a 100644 --- a/talk/app/webrtc/java/jni/classreferenceholder.cc +++ b/talk/app/webrtc/java/jni/classreferenceholder.cc @@ -63,6 +63,7 @@ void FreeGlobalClassReferenceHolder() { ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "java/nio/ByteBuffer"); + LoadClass(jni, "java/util/ArrayList"); LoadClass(jni, "org/webrtc/AudioTrack"); LoadClass(jni, "org/webrtc/DataChannel"); LoadClass(jni, "org/webrtc/DataChannel$Buffer"); @@ -107,6 +108,8 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/PeerConnection$TcpCandidatePolicy"); LoadClass(jni, "org/webrtc/PeerConnection$KeyType"); LoadClass(jni, "org/webrtc/PeerConnection$SignalingState"); + LoadClass(jni, "org/webrtc/RtpReceiver"); + LoadClass(jni, "org/webrtc/RtpSender"); LoadClass(jni, "org/webrtc/SessionDescription"); LoadClass(jni, "org/webrtc/SessionDescription$Type"); LoadClass(jni, "org/webrtc/StatsReport"); diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index 5fa1617644..33b08907be 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -64,6 +64,8 @@ #include "talk/app/webrtc/dtlsidentitystore.h" #include "talk/app/webrtc/mediaconstraintsinterface.h" #include "talk/app/webrtc/peerconnectioninterface.h" +#include "talk/app/webrtc/rtpreceiverinterface.h" +#include "talk/app/webrtc/rtpsenderinterface.h" #include "talk/app/webrtc/videosourceinterface.h" #include "talk/media/base/videocapturer.h" #include "talk/media/base/videorenderer.h" @@ -113,6 +115,8 @@ using webrtc::MediaStreamTrackInterface; using webrtc::PeerConnectionFactoryInterface; using webrtc::PeerConnectionInterface; using webrtc::PeerConnectionObserver; +using webrtc::RtpReceiverInterface; +using webrtc::RtpSenderInterface; using webrtc::SessionDescriptionInterface; using webrtc::SetSessionDescriptionObserver; using webrtc::StatsObserver; @@ -971,7 +975,7 @@ JOW(void, VideoRenderer_releaseNativeFrame)( } JOW(void, MediaStreamTrack_free)(JNIEnv*, jclass, jlong j_p) { - CHECK_RELEASE(reinterpret_cast(j_p)); + reinterpret_cast(j_p)->Release(); } JOW(jboolean, MediaStream_nativeAddAudioTrack)( @@ -1626,6 +1630,60 @@ JOW(void, PeerConnection_nativeRemoveLocalStream)( reinterpret_cast(native_stream)); } +JOW(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, "", "()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, "", "(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 Java object, and will be freed from there. + sender->AddRef(); + jni->CallBooleanMethod(j_senders, j_array_list_add, j_sender); + CHECK_EXCEPTION(jni) << "error during CallBooleanMethod"; + } + return j_senders; +} + +JOW(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, "", "()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, "", "(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; +} + JOW(bool, PeerConnection_nativeGetStats)( JNIEnv* jni, jobject j_pc, jobject j_observer, jlong native_track) { rtc::scoped_refptr observer( @@ -1871,4 +1929,53 @@ JOW(jbyteArray, CallSessionFileRotatingLogSink_nativeGetLogData)( return result; } +JOW(void, RtpSender_nativeSetTrack)(JNIEnv* jni, + jclass, + jlong j_rtp_sender_pointer, + jlong j_track_pointer) { + reinterpret_cast(j_rtp_sender_pointer) + ->SetTrack(reinterpret_cast(j_track_pointer)); +} + +JOW(jlong, RtpSender_nativeGetTrack)(JNIEnv* jni, + jclass, + jlong j_rtp_sender_pointer, + jlong j_track_pointer) { + return jlongFromPointer( + reinterpret_cast(j_rtp_sender_pointer) + ->track() + .release()); +} + +JOW(jstring, RtpSender_nativeId)( + JNIEnv* jni, jclass, jlong j_rtp_sender_pointer) { + return JavaStringFromStdString( + jni, reinterpret_cast(j_rtp_sender_pointer)->id()); +} + +JOW(void, RtpSender_free)(JNIEnv* jni, jclass, jlong j_rtp_sender_pointer) { + reinterpret_cast(j_rtp_sender_pointer)->Release(); +} + +JOW(jlong, RtpReceiver_nativeGetTrack)(JNIEnv* jni, + jclass, + jlong j_rtp_receiver_pointer, + jlong j_track_pointer) { + return jlongFromPointer( + reinterpret_cast(j_rtp_receiver_pointer) + ->track() + .release()); +} + +JOW(jstring, RtpReceiver_nativeId)( + JNIEnv* jni, jclass, jlong j_rtp_receiver_pointer) { + return JavaStringFromStdString( + jni, + reinterpret_cast(j_rtp_receiver_pointer)->id()); +} + +JOW(void, RtpReceiver_free)(JNIEnv* jni, jclass, jlong j_rtp_receiver_pointer) { + reinterpret_cast(j_rtp_receiver_pointer)->Release(); +} + } // namespace webrtc_jni diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java index 40ac615dcc..50023001d7 100644 --- a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java +++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java @@ -29,6 +29,7 @@ package org.webrtc; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -170,11 +171,15 @@ public class PeerConnection { private final List localStreams; private final long nativePeerConnection; private final long nativeObserver; + private List senders; + private List receivers; PeerConnection(long nativePeerConnection, long nativeObserver) { this.nativePeerConnection = nativePeerConnection; this.nativeObserver = nativeObserver; localStreams = new LinkedList(); + senders = new LinkedList(); + receivers = new LinkedList(); } // JsepInterface. @@ -218,6 +223,24 @@ public class PeerConnection { localStreams.remove(stream); } + // Note that calling getSenders will dispose of the senders previously + // returned (and same goes for getReceivers). + public List getSenders() { + for (RtpSender sender : senders) { + sender.dispose(); + } + senders = nativeGetSenders(); + return Collections.unmodifiableList(senders); + } + + public List getReceivers() { + for (RtpReceiver receiver : receivers) { + receiver.dispose(); + } + receivers = nativeGetReceivers(); + return Collections.unmodifiableList(receivers); + } + public boolean getStats(StatsObserver observer, MediaStreamTrack track) { return nativeGetStats(observer, (track == null) ? 0 : track.nativeTrack); } @@ -239,6 +262,14 @@ public class PeerConnection { stream.dispose(); } localStreams.clear(); + for (RtpSender sender : senders) { + sender.dispose(); + } + senders.clear(); + for (RtpReceiver receiver : receivers) { + receiver.dispose(); + } + receivers.clear(); freePeerConnection(nativePeerConnection); freeObserver(nativeObserver); } @@ -256,4 +287,8 @@ public class PeerConnection { private native boolean nativeGetStats( StatsObserver observer, long nativeTrack); + + private native List nativeGetSenders(); + + private native List nativeGetReceivers(); } diff --git a/talk/app/webrtc/java/src/org/webrtc/RtpReceiver.java b/talk/app/webrtc/java/src/org/webrtc/RtpReceiver.java new file mode 100644 index 0000000000..597f441334 --- /dev/null +++ b/talk/app/webrtc/java/src/org/webrtc/RtpReceiver.java @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.webrtc; + +/** Java wrapper for a C++ RtpReceiverInterface. */ +public class RtpReceiver { + final long nativeRtpReceiver; + + private MediaStreamTrack cachedTrack; + + public RtpReceiver(long nativeRtpReceiver) { + this.nativeRtpReceiver = nativeRtpReceiver; + long track = nativeGetTrack(nativeRtpReceiver); + // We can assume that an RtpReceiver always has an associated track. + cachedTrack = new MediaStreamTrack(track); + } + + public MediaStreamTrack track() { + return cachedTrack; + } + + public String id() { + return nativeId(nativeRtpReceiver); + } + + public void dispose() { + cachedTrack.dispose(); + free(nativeRtpReceiver); + } + + // This should increment the reference count of the track. + // Will be released in dispose(). + private static native long nativeGetTrack(long nativeRtpReceiver); + + private static native String nativeId(long nativeRtpReceiver); + + private static native void free(long nativeRtpReceiver); +}; diff --git a/talk/app/webrtc/java/src/org/webrtc/RtpSender.java b/talk/app/webrtc/java/src/org/webrtc/RtpSender.java new file mode 100644 index 0000000000..37357c0657 --- /dev/null +++ b/talk/app/webrtc/java/src/org/webrtc/RtpSender.java @@ -0,0 +1,79 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.webrtc; + +/** Java wrapper for a C++ RtpSenderInterface. */ +public class RtpSender { + final long nativeRtpSender; + + private MediaStreamTrack cachedTrack; + + public RtpSender(long nativeRtpSender) { + this.nativeRtpSender = nativeRtpSender; + long track = nativeGetTrack(nativeRtpSender); + // It may be possible for an RtpSender to be created without a track. + cachedTrack = (track == 0) ? null : new MediaStreamTrack(track); + } + + // NOTE: This should not be called with a track that's already used by + // another RtpSender, because then it would be double-disposed. + public void setTrack(MediaStreamTrack track) { + if (cachedTrack != null) { + cachedTrack.dispose(); + } + cachedTrack = track; + nativeSetTrack(nativeRtpSender, (track == null) ? 0 : track.nativeTrack); + } + + public MediaStreamTrack track() { + return cachedTrack; + } + + public String id() { + return nativeId(nativeRtpSender); + } + + public void dispose() { + if (cachedTrack != null) { + cachedTrack.dispose(); + } + free(nativeRtpSender); + } + + private static native void nativeSetTrack(long nativeRtpSender, + long nativeTrack); + + // This should increment the reference count of the track. + // Will be released in dispose() or setTrack(). + private static native long nativeGetTrack(long nativeRtpSender); + + private static native String nativeId(long nativeRtpSender); + + private static native void free(long nativeRtpSender); +} +; diff --git a/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java b/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java index 001980f821..f9bc495fae 100644 --- a/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java +++ b/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java @@ -645,6 +645,11 @@ public class PeerConnectionTest { assertEquals(answeringPC.getLocalDescription().type, answerSdp.type); assertEquals(answeringPC.getRemoteDescription().type, offerSdp.type); + assertEquals(offeringPC.getSenders().size(), 2); + assertEquals(offeringPC.getReceivers().size(), 2); + assertEquals(answeringPC.getSenders().size(), 2); + assertEquals(answeringPC.getReceivers().size(), 2); + if (!RENDER_TO_GUI) { // Wait for at least some frames to be delivered at each end (number // chosen arbitrarily). diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index c7e45f7714..fd2d969d5d 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -134,6 +134,8 @@ 'app/webrtc/java/src/org/webrtc/MediaStreamTrack.java', 'app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java', 'app/webrtc/java/src/org/webrtc/PeerConnection.java', + 'app/webrtc/java/src/org/webrtc/RtpReceiver.java', + 'app/webrtc/java/src/org/webrtc/RtpSender.java', 'app/webrtc/java/src/org/webrtc/SdpObserver.java', 'app/webrtc/java/src/org/webrtc/StatsObserver.java', 'app/webrtc/java/src/org/webrtc/StatsReport.java',