From c4adabf967e6965cc83de43ec27631085e29663f Mon Sep 17 00:00:00 2001 From: zhihuang Date: Wed, 7 Dec 2016 10:36:40 -0800 Subject: [PATCH] Create the Java Wrapper of RtpReceiverObserverInterface. Create the RtpReceiver.Observer which is a Java wrapper over the webrtc::RtpReceiverObserverInterface. The callback function onFirstPacketReceived will be called whenever the first audio or video packet it received. BUG=webrtc:6742 Review-Url: https://codereview.webrtc.org/2531333003 Cr-Commit-Position: refs/heads/master@{#15464} --- webrtc/api/rtpreceiver.cc | 4 +- .../api/org/webrtc/MediaStreamTrack.java | 5 ++ .../android/api/org/webrtc/RtpReceiver.java | 23 ++++++ .../src/org/webrtc/PeerConnectionTest.java | 42 ++++++++++- .../android/src/jni/classreferenceholder.cc | 1 + webrtc/sdk/android/src/jni/jni_helpers.cc | 17 +++++ webrtc/sdk/android/src/jni/jni_helpers.h | 7 ++ .../sdk/android/src/jni/peerconnection_jni.cc | 72 +++++++++++++++++-- 8 files changed, 161 insertions(+), 10 deletions(-) diff --git a/webrtc/api/rtpreceiver.cc b/webrtc/api/rtpreceiver.cc index 02e1650cb9..80a0256a02 100644 --- a/webrtc/api/rtpreceiver.cc +++ b/webrtc/api/rtpreceiver.cc @@ -114,7 +114,7 @@ void AudioRtpReceiver::Reconfigure() { void AudioRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) { observer_ = observer; // Deliver any notifications the observer may have missed by being set late. - if (received_first_packet_) { + if (received_first_packet_ && observer_) { observer_->OnFirstPacketReceived(media_type()); } } @@ -212,7 +212,7 @@ void VideoRtpReceiver::Stop() { void VideoRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) { observer_ = observer; // Deliver any notifications the observer may have missed by being set late. - if (received_first_packet_) { + if (received_first_packet_ && observer_) { observer_->OnFirstPacketReceived(media_type()); } } diff --git a/webrtc/sdk/android/api/org/webrtc/MediaStreamTrack.java b/webrtc/sdk/android/api/org/webrtc/MediaStreamTrack.java index 31a3736675..95de372245 100644 --- a/webrtc/sdk/android/api/org/webrtc/MediaStreamTrack.java +++ b/webrtc/sdk/android/api/org/webrtc/MediaStreamTrack.java @@ -15,6 +15,11 @@ public class MediaStreamTrack { /** Tracks MediaStreamTrackInterface.TrackState */ public enum State { LIVE, ENDED } + public enum MediaType { + MEDIA_TYPE_AUDIO, + MEDIA_TYPE_VIDEO, + } + final long nativeTrack; public MediaStreamTrack(long nativeTrack) { diff --git a/webrtc/sdk/android/api/org/webrtc/RtpReceiver.java b/webrtc/sdk/android/api/org/webrtc/RtpReceiver.java index 7df030de11..d4244ed62c 100644 --- a/webrtc/sdk/android/api/org/webrtc/RtpReceiver.java +++ b/webrtc/sdk/android/api/org/webrtc/RtpReceiver.java @@ -12,7 +12,14 @@ package org.webrtc; /** Java wrapper for a C++ RtpReceiverInterface. */ public class RtpReceiver { + /** Java wrapper for a C++ RtpReceiverObserverInterface*/ + public static interface Observer { + // Called when the first audio or video packet is received. + public void onFirstPacketReceived(MediaStreamTrack.MediaType media_type); + } + final long nativeRtpReceiver; + private long nativeObserver; private MediaStreamTrack cachedTrack; @@ -41,9 +48,21 @@ public class RtpReceiver { public void dispose() { cachedTrack.dispose(); + if (nativeObserver != 0) { + nativeUnsetObserver(nativeRtpReceiver, nativeObserver); + nativeObserver = 0; + } free(nativeRtpReceiver); } + public void SetObserver(Observer observer) { + // Unset the existing one before setting a new one. + if (nativeObserver != 0) { + nativeUnsetObserver(nativeRtpReceiver, nativeObserver); + } + nativeObserver = nativeSetObserver(nativeRtpReceiver, observer); + } + // This should increment the reference count of the track. // Will be released in dispose(). private static native long nativeGetTrack(long nativeRtpReceiver); @@ -56,4 +75,8 @@ public class RtpReceiver { private static native String nativeId(long nativeRtpReceiver); private static native void free(long nativeRtpReceiver); + + private static native long nativeSetObserver(long nativeRtpReceiver, Observer observer); + + private static native long nativeUnsetObserver(long nativeRtpReceiver, long nativeObserver); }; diff --git a/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java b/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java index a4cb3093bc..59809365d4 100644 --- a/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java +++ b/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java @@ -43,9 +43,9 @@ public class PeerConnectionTest extends ActivityTestCase { getInstrumentation().getContext(), true, true, true)); } - private static class ObserverExpectations implements PeerConnection.Observer, - VideoRenderer.Callbacks, - DataChannel.Observer, StatsObserver { + private static class ObserverExpectations + implements PeerConnection.Observer, VideoRenderer.Callbacks, DataChannel.Observer, + StatsObserver, RtpReceiver.Observer { private final String name; private int expectedIceCandidates = 0; private int expectedErrors = 0; @@ -71,6 +71,8 @@ public class PeerConnectionTest extends ActivityTestCase { private int expectedStatsCallbacks = 0; private LinkedList gotStatsReports = new LinkedList(); private final HashSet gotRemoteStreams = new HashSet(); + private int expectedFirstAudioPacket = 0; + private int expectedFirstVideoPacket = 0; public ObserverExpectations(String name) { this.name = name; @@ -258,6 +260,23 @@ public class PeerConnectionTest extends ActivityTestCase { gotStatsReports.add(reports); } + @Override + public synchronized void onFirstPacketReceived(MediaStreamTrack.MediaType mediaType) { + if (mediaType == MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO) { + expectedFirstAudioPacket--; + } else { + expectedFirstVideoPacket--; + } + if (expectedFirstAudioPacket < 0 || expectedFirstVideoPacket < 0) { + throw new RuntimeException("Unexpected call of onFirstPacketReceived"); + } + } + + public synchronized void expectFirstPacketReceived() { + expectedFirstAudioPacket = 1; + expectedFirstVideoPacket = 1; + } + public synchronized void expectStatsCallback() { ++expectedStatsCallbacks; } @@ -314,6 +333,12 @@ public class PeerConnectionTest extends ActivityTestCase { if (expectedStatsCallbacks != 0) { stillWaitingForExpectations.add("expectedStatsCallbacks: " + expectedStatsCallbacks); } + if (expectedFirstAudioPacket > 0) { + stillWaitingForExpectations.add("expectedFirstAudioPacket: " + expectedFirstAudioPacket); + } + if (expectedFirstVideoPacket > 0) { + stillWaitingForExpectations.add("expectedFirstVideoPacket: " + expectedFirstVideoPacket); + } return stillWaitingForExpectations; } @@ -626,6 +651,17 @@ public class PeerConnectionTest extends ActivityTestCase { assertEquals(answeringPC.getSenders().size(), 2); assertEquals(answeringPC.getReceivers().size(), 2); + offeringExpectations.expectFirstPacketReceived(); + answeringExpectations.expectFirstPacketReceived(); + + for (RtpReceiver receiver : offeringPC.getReceivers()) { + receiver.SetObserver(offeringExpectations); + } + + for (RtpReceiver receiver : answeringPC.getReceivers()) { + receiver.SetObserver(answeringExpectations); + } + // Wait for at least some frames to be delivered at each end (number // chosen arbitrarily). offeringExpectations.expectFramesDelivered(10); diff --git a/webrtc/sdk/android/src/jni/classreferenceholder.cc b/webrtc/sdk/android/src/jni/classreferenceholder.cc index c5dca97f9d..565f47f4b4 100644 --- a/webrtc/sdk/android/src/jni/classreferenceholder.cc +++ b/webrtc/sdk/android/src/jni/classreferenceholder.cc @@ -69,6 +69,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/MediaSource$State"); LoadClass(jni, "org/webrtc/MediaStream"); LoadClass(jni, "org/webrtc/MediaStreamTrack$State"); + LoadClass(jni, "org/webrtc/MediaStreamTrack$MediaType"); LoadClass(jni, "org/webrtc/NetworkMonitor"); LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$ConnectionType"); LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$IPAddress"); diff --git a/webrtc/sdk/android/src/jni/jni_helpers.cc b/webrtc/sdk/android/src/jni/jni_helpers.cc index 5262f067ed..48145f86cc 100644 --- a/webrtc/sdk/android/src/jni/jni_helpers.cc +++ b/webrtc/sdk/android/src/jni/jni_helpers.cc @@ -166,6 +166,16 @@ jfieldID GetFieldID( return f; } +jfieldID GetStaticFieldID(JNIEnv* jni, + jclass c, + const char* name, + const char* signature) { + jfieldID f = jni->GetStaticFieldID(c, name, signature); + CHECK_EXCEPTION(jni) << "error during GetStaticFieldID"; + RTC_CHECK(f) << name << ", " << signature; + return f; +} + jclass GetObjectClass(JNIEnv* jni, jobject object) { jclass c = jni->GetObjectClass(object); CHECK_EXCEPTION(jni) << "error during GetObjectClass"; @@ -180,6 +190,13 @@ jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) { return o; } +jobject GetStaticObjectField(JNIEnv* jni, jclass c, jfieldID id) { + jobject o = jni->GetStaticObjectField(c, id); + CHECK_EXCEPTION(jni) << "error during GetStaticObjectField"; + RTC_CHECK(!IsNull(jni, o)) << "GetStaticObjectField returned NULL"; + return o; +} + jobject GetNullableObjectField(JNIEnv* jni, jobject object, jfieldID id) { jobject o = jni->GetObjectField(object, id); CHECK_EXCEPTION(jni) << "error during GetObjectField"; diff --git a/webrtc/sdk/android/src/jni/jni_helpers.h b/webrtc/sdk/android/src/jni/jni_helpers.h index 91f0a41a36..bed56838d5 100644 --- a/webrtc/sdk/android/src/jni/jni_helpers.h +++ b/webrtc/sdk/android/src/jni/jni_helpers.h @@ -62,11 +62,18 @@ jmethodID GetStaticMethodID( jfieldID GetFieldID(JNIEnv* jni, jclass c, const char* name, const char* signature); +jfieldID GetStaticFieldID(JNIEnv* jni, + jclass c, + const char* name, + const char* signature); + jclass GetObjectClass(JNIEnv* jni, jobject object); // Throws an exception if the object field is null. jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id); +jobject GetStaticObjectField(JNIEnv* jni, jclass c, jfieldID id); + jobject GetNullableObjectField(JNIEnv* jni, jobject object, jfieldID id); jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id); diff --git a/webrtc/sdk/android/src/jni/peerconnection_jni.cc b/webrtc/sdk/android/src/jni/peerconnection_jni.cc index 6416a17489..d22c0a32e7 100644 --- a/webrtc/sdk/android/src/jni/peerconnection_jni.cc +++ b/webrtc/sdk/android/src/jni/peerconnection_jni.cc @@ -99,6 +99,7 @@ using webrtc::PeerConnectionFactoryInterface; using webrtc::PeerConnectionInterface; using webrtc::PeerConnectionObserver; using webrtc::RtpReceiverInterface; +using webrtc::RtpReceiverObserverInterface; using webrtc::RtpSenderInterface; using webrtc::SessionDescriptionInterface; using webrtc::SetSessionDescriptionObserver; @@ -829,6 +830,46 @@ class JavaVideoRendererWrapper ScopedGlobalRef j_byte_buffer_class_; }; +// Adapter between the C++ RtpReceiverObserverInterface and the Java +// RtpReceiver.Observer interface. Wraps an instance of the Java interface and +// dispatches C++ callbacks to Java. +class RtpReceiverObserver : public RtpReceiverObserverInterface { + public: + RtpReceiverObserver(JNIEnv* jni, jobject j_observer) + : j_observer_global_(jni, j_observer) {} + + ~RtpReceiverObserver() override {} + + void OnFirstPacketReceived(cricket::MediaType media_type) override { + JNIEnv* const jni = AttachCurrentThreadIfNeeded(); + + jmethodID j_on_first_packet_received_mid = GetMethodID( + jni, GetObjectClass(jni, *j_observer_global_), "onFirstPacketReceived", + "(Lorg/webrtc/MediaStreamTrack$MediaType;)V"); + // Get the Java version of media type. + jclass j_media_type_class = + FindClass(jni, "org/webrtc/MediaStreamTrack$MediaType"); + + RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO) + << "Media type: " << media_type; + const char* media_type_str = media_type == cricket::MEDIA_TYPE_AUDIO + ? "MEDIA_TYPE_AUDIO" + : "MEDIA_TYPE_VIDEO"; + jfieldID j_media_type_fid = + GetStaticFieldID(jni, j_media_type_class, media_type_str, + "Lorg/webrtc/MediaStreamTrack$MediaType;"); + jobject JavaMediaType = + GetStaticObjectField(jni, j_media_type_class, j_media_type_fid); + // Trigger the callback function. + jni->CallVoidMethod(*j_observer_global_, j_on_first_packet_received_mid, + JavaMediaType); + CHECK_EXCEPTION(jni) << "error during CallVoidMethod"; + } + + private: + const ScopedGlobalRef j_observer_global_; +}; static DataChannelInterface* ExtractNativeDC(JNIEnv* jni, jobject j_dc) { jfieldID native_dc_id = GetFieldID(jni, @@ -2382,20 +2423,20 @@ JOW(jlong, RtpReceiver_nativeGetTrack)(JNIEnv* jni, } JOW(jboolean, RtpReceiver_nativeSetParameters) -(JNIEnv* jni, jclass, jlong j_rtp_sender_pointer, jobject j_parameters) { +(JNIEnv* jni, jclass, jlong j_rtp_receiver_pointer, jobject j_parameters) { if (IsNull(jni, j_parameters)) { return false; } webrtc::RtpParameters parameters; JavaRtpParametersToJsepRtpParameters(jni, j_parameters, ¶meters); - return reinterpret_cast(j_rtp_sender_pointer) + return reinterpret_cast(j_rtp_receiver_pointer) ->SetParameters(parameters); } JOW(jobject, RtpReceiver_nativeGetParameters) -(JNIEnv* jni, jclass, jlong j_rtp_sender_pointer) { +(JNIEnv* jni, jclass, jlong j_rtp_receiver_pointer) { webrtc::RtpParameters parameters = - reinterpret_cast(j_rtp_sender_pointer) + reinterpret_cast(j_rtp_receiver_pointer) ->GetParameters(); return JsepRtpParametersToJavaRtpParameters(jni, parameters); } @@ -2407,8 +2448,29 @@ JOW(jstring, RtpReceiver_nativeId)( reinterpret_cast(j_rtp_receiver_pointer)->id()); } -JOW(void, RtpReceiver_free)(JNIEnv* jni, jclass, jlong j_rtp_receiver_pointer) { +JOW(void, RtpReceiver_free) +(JNIEnv* jni, jclass, jlong j_rtp_receiver_pointer) { reinterpret_cast(j_rtp_receiver_pointer)->Release(); } +JOW(jlong, RtpReceiver_nativeSetObserver) +(JNIEnv* jni, jclass, jlong j_rtp_receiver_pointer, jobject j_observer) { + RtpReceiverObserver* rtpReceiverObserver = + new RtpReceiverObserver(jni, j_observer); + reinterpret_cast(j_rtp_receiver_pointer) + ->SetObserver(rtpReceiverObserver); + return jlongFromPointer(rtpReceiverObserver); +} + +JOW(void, RtpReceiver_nativeUnsetObserver) +(JNIEnv* jni, jclass, jlong j_rtp_receiver_pointer, jlong j_observer_pointer) { + reinterpret_cast(j_rtp_receiver_pointer) + ->SetObserver(nullptr); + RtpReceiverObserver* observer = + reinterpret_cast(j_observer_pointer); + if (observer) { + delete observer; + } +} + } // namespace webrtc_jni