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}
This commit is contained in:
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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<StatsReport[]> gotStatsReports = new LinkedList<StatsReport[]>();
|
||||
private final HashSet<MediaStream> gotRemoteStreams = new HashSet<MediaStream>();
|
||||
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);
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<jclass> 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<jobject> 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<RtpReceiverInterface*>(j_rtp_sender_pointer)
|
||||
return reinterpret_cast<RtpReceiverInterface*>(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<RtpReceiverInterface*>(j_rtp_sender_pointer)
|
||||
reinterpret_cast<RtpReceiverInterface*>(j_rtp_receiver_pointer)
|
||||
->GetParameters();
|
||||
return JsepRtpParametersToJavaRtpParameters(jni, parameters);
|
||||
}
|
||||
@ -2407,8 +2448,29 @@ JOW(jstring, RtpReceiver_nativeId)(
|
||||
reinterpret_cast<RtpReceiverInterface*>(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<RtpReceiverInterface*>(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<RtpReceiverInterface*>(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<RtpReceiverInterface*>(j_rtp_receiver_pointer)
|
||||
->SetObserver(nullptr);
|
||||
RtpReceiverObserver* observer =
|
||||
reinterpret_cast<RtpReceiverObserver*>(j_observer_pointer);
|
||||
if (observer) {
|
||||
delete observer;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
Reference in New Issue
Block a user