diff --git a/talk/app/webrtc/OWNERS b/talk/app/webrtc/OWNERS new file mode 100644 index 000000000..548f6d1ba --- /dev/null +++ b/talk/app/webrtc/OWNERS @@ -0,0 +1,12 @@ +hellner +juberti +mallinath +perkj +ronghuawu +tommi +hellner +juberti +mallinath +perkj +ronghuawu +tommi \ No newline at end of file diff --git a/talk/app/webrtc/datachannelinterface.h b/talk/app/webrtc/datachannelinterface.h index 9c66a50fb..6054e1be1 100644 --- a/talk/app/webrtc/datachannelinterface.h +++ b/talk/app/webrtc/datachannelinterface.h @@ -95,7 +95,7 @@ class DataChannelObserver { class DataChannelInterface : public talk_base::RefCountInterface { public: - enum DataState { + enum DataState { // Keep in sync with DataChannel.java:State. kConnecting, kOpen, // The DataChannel is ready to send data. kClosing, diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index 6b5a6a48c..63a32e8c1 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -56,6 +56,7 @@ #undef JNIEXPORT #define JNIEXPORT __attribute__((visibility("default"))) +#include #include #include "talk/app/webrtc/mediaconstraintsinterface.h" @@ -77,6 +78,10 @@ using webrtc::AudioSourceInterface; using webrtc::AudioTrackInterface; using webrtc::AudioTrackVector; using webrtc::CreateSessionDescriptionObserver; +using webrtc::DataBuffer; +using webrtc::DataChannelInit; +using webrtc::DataChannelInterface; +using webrtc::DataChannelObserver; using webrtc::IceCandidateInterface; using webrtc::MediaConstraintsInterface; using webrtc::MediaSourceInterface; @@ -156,6 +161,10 @@ class ClassReferenceHolder { explicit ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "java/nio/ByteBuffer"); LoadClass(jni, "org/webrtc/AudioTrack"); + LoadClass(jni, "org/webrtc/DataChannel"); + LoadClass(jni, "org/webrtc/DataChannel$Buffer"); + LoadClass(jni, "org/webrtc/DataChannel$Init"); + LoadClass(jni, "org/webrtc/DataChannel$State"); LoadClass(jni, "org/webrtc/IceCandidate"); LoadClass(jni, "org/webrtc/MediaSource$State"); LoadClass(jni, "org/webrtc/MediaStream"); @@ -254,13 +263,28 @@ jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) { return o; } +jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id) { + return static_cast(GetObjectField(jni, object, id)); +} + jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id) { jlong l = jni->GetLongField(object, id); CHECK_EXCEPTION(jni, "error during GetLongField"); - CHECK(l, ""); return l; } +jint GetIntField(JNIEnv* jni, jobject object, jfieldID id) { + jint i = jni->GetIntField(object, id); + CHECK_EXCEPTION(jni, "error during GetIntField"); + return i; +} + +bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id) { + jboolean b = jni->GetBooleanField(object, id); + CHECK_EXCEPTION(jni, "error during GetBooleanField"); + return b; +} + jobject NewGlobalRef(JNIEnv* jni, jobject o) { jobject ret = jni->NewGlobalRef(o); CHECK_EXCEPTION(jni, "error during NewGlobalRef"); @@ -314,18 +338,19 @@ class ScopedLocalRef { }; // Scoped holder for global Java refs. +template // T is jclass, jobject, jintArray, etc. class ScopedGlobalRef { public: - explicit ScopedGlobalRef(JNIEnv* jni, jobject obj) - : obj_(jni->NewGlobalRef(obj)) {} + explicit ScopedGlobalRef(JNIEnv* jni, T obj) + : obj_(static_cast(jni->NewGlobalRef(obj))) {} ~ScopedGlobalRef() { DeleteGlobalRef(AttachCurrentThreadIfNeeded(), obj_); } - jobject operator*() const { + T operator*() const { return obj_; } private: - jobject obj_; + T obj_; }; // Return the (singleton) Java Enum object corresponding to |index|; @@ -365,6 +390,32 @@ static std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) { return ustr.toUTF8String(ret); } +static DataChannelInit JavaDataChannelInitToNative( + JNIEnv* jni, jobject j_init) { + DataChannelInit init; + + jclass j_init_class = FindClass(jni, "org/webrtc/DataChannel$Init"); + jfieldID ordered_id = GetFieldID(jni, j_init_class, "ordered", "Z"); + jfieldID max_retransmit_time_id = + GetFieldID(jni, j_init_class, "maxRetransmitTimeMs", "I"); + jfieldID max_retransmits_id = + GetFieldID(jni, j_init_class, "maxRetransmits", "I"); + jfieldID protocol_id = + GetFieldID(jni, j_init_class, "protocol", "Ljava/lang/String;"); + jfieldID negotiated_id = GetFieldID(jni, j_init_class, "negotiated", "Z"); + jfieldID id_id = GetFieldID(jni, j_init_class, "id", "I"); + + init.ordered = GetBooleanField(jni, j_init, ordered_id); + init.maxRetransmitTime = GetIntField(jni, j_init, max_retransmit_time_id); + init.maxRetransmits = GetIntField(jni, j_init, max_retransmits_id); + init.protocol = JavaToStdString( + jni, GetStringField(jni, j_init, protocol_id)); + init.negotiated = GetBooleanField(jni, j_init, negotiated_id); + init.id = GetIntField(jni, j_init, id_id); + + return init; +} + class ConstraintsWrapper; // Adapter between the C++ PeerConnectionObserver interface and the Java @@ -374,25 +425,24 @@ class PCOJava : public PeerConnectionObserver { public: PCOJava(JNIEnv* jni, jobject j_observer) : j_observer_global_(jni, j_observer), - j_observer_class_((jclass)NewGlobalRef( - jni, GetObjectClass(jni, *j_observer_global_))), - j_media_stream_class_((jclass)NewGlobalRef( - jni, FindClass(jni, "org/webrtc/MediaStream"))), - j_media_stream_ctor_(GetMethodID(jni, - j_media_stream_class_, "", "(J)V")), - j_audio_track_class_((jclass)NewGlobalRef( - jni, FindClass(jni, "org/webrtc/AudioTrack"))), + 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_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_((jclass)NewGlobalRef( - jni, FindClass(jni, "org/webrtc/VideoTrack"))), - j_video_track_ctor_(GetMethodID(jni, - j_video_track_class_, "", "(J)V")) { + 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_data_channel_class_(jni, FindClass(jni, "org/webrtc/DataChannel")), + j_data_channel_ctor_(GetMethodID( + jni, *j_data_channel_class_, "", "(J)V")) { } virtual ~PCOJava() {} - virtual void OnIceCandidate(const IceCandidateInterface* candidate) { + virtual void OnIceCandidate(const IceCandidateInterface* candidate) OVERRIDE { std::string sdp; CHECK(candidate->ToString(&sdp), "got so far: " << sdp); jclass candidate_class = FindClass(jni(), "org/webrtc/IceCandidate"); @@ -404,22 +454,22 @@ class PCOJava : public PeerConnectionObserver { ScopedLocalRef j_candidate(jni(), jni()->NewObject( candidate_class, ctor, *j_mid, candidate->sdp_mline_index(), *j_sdp)); CHECK_EXCEPTION(jni(), "error during NewObject"); - jmethodID m = GetMethodID(jni(), j_observer_class_, + 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"); } - virtual void OnError() { - jmethodID m = GetMethodID(jni(), j_observer_class_, "onError", "(V)V"); + virtual void OnError() OVERRIDE { + jmethodID m = GetMethodID(jni(), *j_observer_class_, "onError", "(V)V"); jni()->CallVoidMethod(*j_observer_global_, m); CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); } virtual void OnSignalingChange( - PeerConnectionInterface::SignalingState new_state) { + PeerConnectionInterface::SignalingState new_state) OVERRIDE { jmethodID m = GetMethodID( - jni(), j_observer_class_, "onSignalingChange", + jni(), *j_observer_class_, "onSignalingChange", "(Lorg/webrtc/PeerConnection$SignalingState;)V"); ScopedLocalRef new_state_enum(jni(), JavaEnumFromIndex( jni(), "PeerConnection$SignalingState", new_state)); @@ -428,9 +478,9 @@ class PCOJava : public PeerConnectionObserver { } virtual void OnIceConnectionChange( - PeerConnectionInterface::IceConnectionState new_state) { + PeerConnectionInterface::IceConnectionState new_state) OVERRIDE { jmethodID m = GetMethodID( - jni(), j_observer_class_, "onIceConnectionChange", + jni(), *j_observer_class_, "onIceConnectionChange", "(Lorg/webrtc/PeerConnection$IceConnectionState;)V"); ScopedLocalRef new_state_enum(jni(), JavaEnumFromIndex( jni(), "PeerConnection$IceConnectionState", new_state)); @@ -439,9 +489,9 @@ class PCOJava : public PeerConnectionObserver { } virtual void OnIceGatheringChange( - PeerConnectionInterface::IceGatheringState new_state) { + PeerConnectionInterface::IceGatheringState new_state) OVERRIDE { jmethodID m = GetMethodID( - jni(), j_observer_class_, "onIceGatheringChange", + jni(), *j_observer_class_, "onIceGatheringChange", "(Lorg/webrtc/PeerConnection$IceGatheringState;)V"); ScopedLocalRef new_state_enum(jni(), JavaEnumFromIndex( jni(), "PeerConnection$IceGatheringState", new_state)); @@ -449,9 +499,9 @@ class PCOJava : public PeerConnectionObserver { CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); } - virtual void OnAddStream(MediaStreamInterface* stream) { + virtual void OnAddStream(MediaStreamInterface* stream) OVERRIDE { ScopedLocalRef j_stream(jni(), jni()->NewObject( - j_media_stream_class_, j_media_stream_ctor_, (jlong)stream)); + *j_media_stream_class_, j_media_stream_ctor_, (jlong)stream)); CHECK_EXCEPTION(jni(), "error during NewObject"); AudioTrackVector audio_tracks = stream->GetAudioTracks(); @@ -460,10 +510,10 @@ class PCOJava : public PeerConnectionObserver { ScopedLocalRef id( jni(), JavaStringFromStdString(jni(), track->id())); ScopedLocalRef j_track(jni(), jni()->NewObject( - j_audio_track_class_, j_audio_track_ctor_, (jlong)track, *id)); + *j_audio_track_class_, j_audio_track_ctor_, (jlong)track, *id)); CHECK_EXCEPTION(jni(), "error during NewObject"); - jfieldID audio_tracks_id = GetFieldID(jni(), - j_media_stream_class_, "audioTracks", "Ljava/util/List;"); + jfieldID audio_tracks_id = GetFieldID( + jni(), *j_media_stream_class_, "audioTracks", "Ljava/util/List;"); ScopedLocalRef audio_tracks(jni(), GetObjectField( jni(), *j_stream, audio_tracks_id)); jmethodID add = GetMethodID(jni(), @@ -479,10 +529,10 @@ class PCOJava : public PeerConnectionObserver { ScopedLocalRef id( jni(), JavaStringFromStdString(jni(), track->id())); ScopedLocalRef j_track(jni(), jni()->NewObject( - j_video_track_class_, j_video_track_ctor_, (jlong)track, *id)); + *j_video_track_class_, j_video_track_ctor_, (jlong)track, *id)); CHECK_EXCEPTION(jni(), "error during NewObject"); - jfieldID video_tracks_id = GetFieldID(jni(), - j_media_stream_class_, "videoTracks", "Ljava/util/List;"); + jfieldID video_tracks_id = GetFieldID( + jni(), *j_media_stream_class_, "videoTracks", "Ljava/util/List;"); ScopedLocalRef video_tracks(jni(), GetObjectField( jni(), *j_stream, video_tracks_id)); jmethodID add = GetMethodID(jni(), @@ -494,13 +544,13 @@ class PCOJava : public PeerConnectionObserver { streams_[stream] = jni()->NewWeakGlobalRef(*j_stream); CHECK_EXCEPTION(jni(), "error during NewWeakGlobalRef"); - jmethodID m = GetMethodID(jni(), - j_observer_class_, "onAddStream", "(Lorg/webrtc/MediaStream;)V"); + 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"); } - virtual void OnRemoveStream(MediaStreamInterface* stream) { + virtual void OnRemoveStream(MediaStreamInterface* stream) OVERRIDE { NativeToJavaStreamsMap::iterator it = streams_.find(stream); CHECK(it != streams_.end(), "unexpected stream: " << std::hex << stream); @@ -509,12 +559,26 @@ class PCOJava : public PeerConnectionObserver { if (!s.obj()) return; - jmethodID m = GetMethodID(jni(), - j_observer_class_, "onRemoveStream", "(Lorg/webrtc/MediaStream;)V"); + jmethodID m = GetMethodID(jni(), *j_observer_class_, "onRemoveStream", + "(Lorg/webrtc/MediaStream;)V"); jni()->CallVoidMethod(*j_observer_global_, m, s.obj()); CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); } + virtual void OnDataChannel(DataChannelInterface* channel) OVERRIDE { + ScopedLocalRef j_channel(jni(), jni()->NewObject( + *j_data_channel_class_, j_data_channel_ctor_, (jlong)channel)); + CHECK_EXCEPTION(jni(), "error during NewObject"); + // Channel is now owned by Java object, and will be freed from + // DataChannel.dispose(). + channel->AddRef(); + + jmethodID m = GetMethodID(jni(), *j_observer_class_, "onDataChannel", + "(Lorg/webrtc/DataChannel;)V"); + jni()->CallVoidMethod(*j_observer_global_, m, *j_channel); + CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); + } + void SetConstraints(ConstraintsWrapper* constraints) { CHECK(!constraints_.get(), "constraints already set!"); constraints_.reset(constraints); @@ -527,14 +591,16 @@ class PCOJava : public PeerConnectionObserver { return AttachCurrentThreadIfNeeded(); } - const ScopedGlobalRef j_observer_global_; - const jclass j_observer_class_; - const jclass j_media_stream_class_; + const ScopedGlobalRef j_observer_global_; + const ScopedGlobalRef j_observer_class_; + const ScopedGlobalRef j_media_stream_class_; const jmethodID j_media_stream_ctor_; - const jclass j_audio_track_class_; + const ScopedGlobalRef j_audio_track_class_; const jmethodID j_audio_track_ctor_; - const jclass j_video_track_class_; + const ScopedGlobalRef j_video_track_class_; const jmethodID j_video_track_ctor_; + const ScopedGlobalRef j_data_channel_class_; + const jmethodID j_data_channel_ctor_; typedef std::map NativeToJavaStreamsMap; NativeToJavaStreamsMap streams_; // C++ -> Java streams. talk_base::scoped_ptr constraints_; @@ -554,8 +620,13 @@ class ConstraintsWrapper : public MediaConstraintsInterface { virtual ~ConstraintsWrapper() {} // MediaConstraintsInterface. - virtual const Constraints& GetMandatory() const { return mandatory_; } - virtual const Constraints& GetOptional() const { return optional_; } + virtual const Constraints& GetMandatory() const OVERRIDE { + return mandatory_; + } + + virtual const Constraints& GetOptional() const OVERRIDE { + return optional_; + } private: // Helper for translating a List> to a Constraints. @@ -630,28 +701,26 @@ class SdpObserverWrapper : public T { SdpObserverWrapper(JNIEnv* jni, jobject j_observer, ConstraintsWrapper* constraints) : constraints_(constraints), - j_observer_global_(NewGlobalRef(jni, j_observer)), - j_observer_class_((jclass)NewGlobalRef( - jni, GetObjectClass(jni, j_observer))) { + j_observer_global_(jni, j_observer), + j_observer_class_(jni, GetObjectClass(jni, j_observer)) { } - virtual ~SdpObserverWrapper() { - DeleteGlobalRef(jni(), j_observer_global_); - DeleteGlobalRef(jni(), j_observer_class_); - } + virtual ~SdpObserverWrapper() {} + // Can't mark OVERRIDE because of templating. virtual void OnSuccess() { - jmethodID m = GetMethodID(jni(), j_observer_class_, "onSetSuccess", "()V"); - jni()->CallVoidMethod(j_observer_global_, m); + jmethodID m = GetMethodID(jni(), *j_observer_class_, "onSetSuccess", "()V"); + jni()->CallVoidMethod(*j_observer_global_, m); CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); } + // Can't mark OVERRIDE because of templating. virtual void OnSuccess(SessionDescriptionInterface* desc) { jmethodID m = GetMethodID( - jni(), j_observer_class_, "onCreateSuccess", + jni(), *j_observer_class_, "onCreateSuccess", "(Lorg/webrtc/SessionDescription;)V"); ScopedLocalRef j_sdp(jni(), JavaSdpFromNativeSdp(jni(), desc)); - jni()->CallVoidMethod(j_observer_global_, m, *j_sdp); + jni()->CallVoidMethod(*j_observer_global_, m, *j_sdp); CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); } @@ -659,11 +728,11 @@ class SdpObserverWrapper : public T { // Common implementation for failure of Set & Create types, distinguished by // |op| being "Set" or "Create". void OnFailure(const std::string& op, const std::string& error) { - jmethodID m = GetMethodID(jni(), - j_observer_class_, "on" + op + "Failure", "(Ljava/lang/String;)V"); + jmethodID m = GetMethodID(jni(), *j_observer_class_, "on" + op + "Failure", + "(Ljava/lang/String;)V"); ScopedLocalRef j_error_string( jni(), JavaStringFromStdString(jni(), error)); - jni()->CallVoidMethod(j_observer_global_, m, *j_error_string); + jni()->CallVoidMethod(*j_observer_global_, m, *j_error_string); CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); } @@ -673,8 +742,8 @@ class SdpObserverWrapper : public T { } talk_base::scoped_ptr constraints_; - const jobject j_observer_global_; - const jclass j_observer_class_; + const ScopedGlobalRef j_observer_global_; + const ScopedGlobalRef j_observer_class_; }; class CreateSdpObserverWrapper @@ -684,7 +753,7 @@ class CreateSdpObserverWrapper ConstraintsWrapper* constraints) : SdpObserverWrapper(jni, j_observer, constraints) {} - virtual void OnFailure(const std::string& error) { + virtual void OnFailure(const std::string& error) OVERRIDE { SdpObserverWrapper::OnFailure(std::string("Create"), error); } }; @@ -696,42 +765,84 @@ class SetSdpObserverWrapper ConstraintsWrapper* constraints) : SdpObserverWrapper(jni, j_observer, constraints) {} - virtual void OnFailure(const std::string& error) { + virtual void OnFailure(const std::string& error) OVERRIDE { SdpObserverWrapper::OnFailure(std::string("Set"), error); } }; +// Adapter for a Java DataChannel$Observer presenting a C++ DataChannelObserver +// and dispatching the callback from C++ back to Java. +class DataChannelObserverWrapper : public DataChannelObserver { + public: + DataChannelObserverWrapper(JNIEnv* jni, jobject j_observer) + : j_observer_global_(jni, j_observer), + j_observer_class_(jni, GetObjectClass(jni, j_observer)), + j_on_state_change_mid_(GetMethodID(jni, *j_observer_class_, + "onStateChange", "()V")), + j_on_message_mid_(GetMethodID(jni, *j_observer_class_, "onMessage", + "(Lorg/webrtc/DataChannel$Buffer;)V")), + j_buffer_class_(jni, FindClass(jni, "org/webrtc/DataChannel$Buffer")), + j_buffer_ctor_(GetMethodID(jni, *j_buffer_class_, + "", "(Ljava/nio/ByteBuffer;Z)V")) { + } + + virtual ~DataChannelObserverWrapper() {} + + virtual void OnStateChange() OVERRIDE { + jni()->CallVoidMethod(*j_observer_global_, j_on_state_change_mid_); + CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); + } + + virtual void OnMessage(const DataBuffer& buffer) OVERRIDE { + jobject byte_buffer = + jni()->NewDirectByteBuffer(const_cast(buffer.data.data()), + buffer.data.length()); + jobject j_buffer = jni()->NewObject(*j_buffer_class_, j_buffer_ctor_, + byte_buffer, buffer.binary); + jni()->CallVoidMethod(*j_observer_global_, j_on_message_mid_, j_buffer); + CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); + } + + private: + JNIEnv* jni() { + return AttachCurrentThreadIfNeeded(); + } + + const ScopedGlobalRef j_observer_global_; + const ScopedGlobalRef j_observer_class_; + const ScopedGlobalRef j_buffer_class_; + const jmethodID j_on_state_change_mid_; + const jmethodID j_on_message_mid_; + const jmethodID j_buffer_ctor_; +}; + // Adapter for a Java StatsObserver presenting a C++ StatsObserver and // dispatching the callback from C++ back to Java. class StatsObserverWrapper : public StatsObserver { public: StatsObserverWrapper(JNIEnv* jni, jobject j_observer) - : j_observer_global_(NewGlobalRef(jni, j_observer)), - j_observer_class_((jclass)NewGlobalRef( - jni, GetObjectClass(jni, j_observer))), - j_stats_report_class_(FindClass(jni, "org/webrtc/StatsReport")), + : j_observer_global_(jni, j_observer), + j_observer_class_(jni, GetObjectClass(jni, j_observer)), + j_stats_report_class_(jni, FindClass(jni, "org/webrtc/StatsReport")), j_stats_report_ctor_(GetMethodID( - jni, j_stats_report_class_, "", + jni, *j_stats_report_class_, "", "(Ljava/lang/String;Ljava/lang/String;D" "[Lorg/webrtc/StatsReport$Value;)V")), - j_value_class_(FindClass( + j_value_class_(jni, FindClass( jni, "org/webrtc/StatsReport$Value")), j_value_ctor_(GetMethodID( - jni, j_value_class_, "", + jni, *j_value_class_, "", "(Ljava/lang/String;Ljava/lang/String;)V")) { } - virtual ~StatsObserverWrapper() { - DeleteGlobalRef(jni(), j_observer_global_); - DeleteGlobalRef(jni(), j_observer_class_); - } + virtual ~StatsObserverWrapper() {} - virtual void OnComplete(const std::vector& reports) { + virtual void OnComplete(const std::vector& reports) OVERRIDE { ScopedLocalRef j_reports(jni(), ReportsToJava(jni(), reports)); - jmethodID m = GetMethodID( - jni(), j_observer_class_, "onComplete", "([Lorg/webrtc/StatsReport;)V"); - jni()->CallVoidMethod(j_observer_global_, m, *j_reports); + jmethodID m = GetMethodID(jni(), *j_observer_class_, "onComplete", + "([Lorg/webrtc/StatsReport;)V"); + jni()->CallVoidMethod(*j_observer_global_, m, *j_reports); CHECK_EXCEPTION(jni(), "error during CallVoidMethod"); } @@ -739,7 +850,7 @@ class StatsObserverWrapper : public StatsObserver { jobjectArray ReportsToJava( JNIEnv* jni, const std::vector& reports) { jobjectArray reports_array = jni->NewObjectArray( - reports.size(), j_stats_report_class_, NULL); + reports.size(), *j_stats_report_class_, NULL); for (int i = 0; i < reports.size(); ++i) { const StatsReport& report = reports[i]; ScopedLocalRef j_id( @@ -749,7 +860,7 @@ class StatsObserverWrapper : public StatsObserver { ScopedLocalRef j_values( jni, ValuesToJava(jni, report.values)); ScopedLocalRef j_report(jni, jni->NewObject( - j_stats_report_class_, j_stats_report_ctor_, *j_id, *j_type, + *j_stats_report_class_, j_stats_report_ctor_, *j_id, *j_type, report.timestamp, *j_values)); jni->SetObjectArrayElement(reports_array, i, *j_report); } @@ -758,7 +869,7 @@ class StatsObserverWrapper : public StatsObserver { jobjectArray ValuesToJava(JNIEnv* jni, const StatsReport::Values& values) { jobjectArray j_values = jni->NewObjectArray( - values.size(), j_value_class_, NULL); + values.size(), *j_value_class_, NULL); for (int i = 0; i < values.size(); ++i) { const StatsReport::Value& value = values[i]; ScopedLocalRef j_name( @@ -766,7 +877,7 @@ class StatsObserverWrapper : public StatsObserver { ScopedLocalRef j_value( jni, JavaStringFromStdString(jni, value.value)); ScopedLocalRef j_element_value(jni, jni->NewObject( - j_value_class_, j_value_ctor_, *j_name, *j_value)); + *j_value_class_, j_value_ctor_, *j_name, *j_value)); jni->SetObjectArrayElement(j_values, i, *j_element_value); } return j_values; @@ -776,11 +887,11 @@ class StatsObserverWrapper : public StatsObserver { return AttachCurrentThreadIfNeeded(); } - const jobject j_observer_global_; - const jclass j_observer_class_; - const jclass j_stats_report_class_; + const ScopedGlobalRef j_observer_global_; + const ScopedGlobalRef j_observer_class_; + const ScopedGlobalRef j_stats_report_class_; const jmethodID j_stats_report_ctor_; - const jclass j_value_class_; + const ScopedGlobalRef j_value_class_; const jmethodID j_value_ctor_; }; @@ -796,12 +907,12 @@ class VideoRendererWrapper : public VideoRendererInterface { virtual ~VideoRendererWrapper() {} - virtual void SetSize(int width, int height) { + virtual void SetSize(int width, int height) OVERRIDE { const bool kNotReserved = false; // What does this param mean?? renderer_->SetSize(width, height, kNotReserved); } - virtual void RenderFrame(const cricket::VideoFrame* frame) { + virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE { renderer_->RenderFrame(frame); } @@ -817,25 +928,28 @@ class VideoRendererWrapper : public VideoRendererInterface { class JavaVideoRendererWrapper : public VideoRendererInterface { public: JavaVideoRendererWrapper(JNIEnv* jni, jobject j_callbacks) - : j_callbacks_(jni, j_callbacks) { - j_set_size_id_ = GetMethodID( - jni, GetObjectClass(jni, j_callbacks), "setSize", "(II)V"); - j_render_frame_id_ = GetMethodID( - jni, GetObjectClass(jni, j_callbacks), "renderFrame", - "(Lorg/webrtc/VideoRenderer$I420Frame;)V"); - j_frame_class_ = FindClass(jni, "org/webrtc/VideoRenderer$I420Frame"); - j_frame_ctor_id_ = GetMethodID( - jni, j_frame_class_, "", "(II[I[Ljava/nio/ByteBuffer;)V"); - j_byte_buffer_class_ = FindClass(jni, "java/nio/ByteBuffer"); + : j_callbacks_(jni, j_callbacks), + j_set_size_id_(GetMethodID( + jni, GetObjectClass(jni, j_callbacks), "setSize", "(II)V")), + j_render_frame_id_(GetMethodID( + jni, GetObjectClass(jni, j_callbacks), "renderFrame", + "(Lorg/webrtc/VideoRenderer$I420Frame;)V")), + j_frame_class_(jni, + FindClass(jni, "org/webrtc/VideoRenderer$I420Frame")), + j_frame_ctor_id_(GetMethodID( + jni, *j_frame_class_, "", "(II[I[Ljava/nio/ByteBuffer;)V")), + j_byte_buffer_class_(jni, FindClass(jni, "java/nio/ByteBuffer")) { CHECK_EXCEPTION(jni, ""); } - virtual void SetSize(int width, int height) { + virtual ~JavaVideoRendererWrapper() {} + + virtual void SetSize(int width, int height) OVERRIDE { jni()->CallVoidMethod(*j_callbacks_, j_set_size_id_, width, height); CHECK_EXCEPTION(jni(), ""); } - virtual void RenderFrame(const cricket::VideoFrame* frame) { + virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE { ScopedLocalRef j_frame(jni(), CricketToJavaFrame(frame)); jni()->CallVoidMethod(*j_callbacks_, j_render_frame_id_, *j_frame); CHECK_EXCEPTION(jni(), ""); @@ -851,7 +965,7 @@ class JavaVideoRendererWrapper : public VideoRendererInterface { strides_array[2] = frame->GetVPitch(); jni()->ReleaseIntArrayElements(*strides, strides_array, 0); ScopedLocalRef planes( - jni(), jni()->NewObjectArray(3, j_byte_buffer_class_, NULL)); + jni(), jni()->NewObjectArray(3, *j_byte_buffer_class_, NULL)); ScopedLocalRef y_buffer(jni(), jni()->NewDirectByteBuffer( const_cast(frame->GetYPlane()), frame->GetYPitch() * frame->GetHeight())); @@ -863,7 +977,7 @@ class JavaVideoRendererWrapper : public VideoRendererInterface { jni()->SetObjectArrayElement(*planes, 1, *u_buffer); jni()->SetObjectArrayElement(*planes, 2, *v_buffer); return jni()->NewObject( - j_frame_class_, j_frame_ctor_id_, + *j_frame_class_, j_frame_ctor_id_, frame->GetWidth(), frame->GetHeight(), *strides, *planes); } @@ -871,12 +985,12 @@ class JavaVideoRendererWrapper : public VideoRendererInterface { return AttachCurrentThreadIfNeeded(); } - ScopedGlobalRef j_callbacks_; + ScopedGlobalRef j_callbacks_; jmethodID j_set_size_id_; jmethodID j_render_frame_id_; - jclass j_frame_class_; + ScopedGlobalRef j_frame_class_; jmethodID j_frame_ctor_id_; - jclass j_byte_buffer_class_; + ScopedGlobalRef j_byte_buffer_class_; }; } // anonymous namespace @@ -920,6 +1034,63 @@ extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) { CHECK(talk_base::CleanupSSL(), "Failed to CleanupSSL()"); } +static talk_base::scoped_refptr ExtractNativeDC( + JNIEnv* jni, jobject j_dc) { + jfieldID native_dc_id = GetFieldID(jni, + GetObjectClass(jni, j_dc), "nativeDataChannel", "J"); + jlong j_d = GetLongField(jni, j_dc, native_dc_id); + return talk_base::scoped_refptr( + reinterpret_cast(j_d)); +} + +JOW(jlong, DataChannel_registerObserverNative)( + JNIEnv* jni, jobject j_dc, jobject j_observer) { + talk_base::scoped_ptr observer( + new DataChannelObserverWrapper(jni, j_observer)); + ExtractNativeDC(jni, j_dc)->RegisterObserver(observer.get()); + return reinterpret_cast(observer.release()); +} + +JOW(void, DataChannel_unregisterObserverNative)( + JNIEnv* jni, jobject j_dc, jlong native_observer) { + ExtractNativeDC(jni, j_dc)->UnregisterObserver(); + delete reinterpret_cast(native_observer); +} + +JOW(jstring, DataChannel_label)(JNIEnv* jni, jobject j_dc) { + return JavaStringFromStdString(jni, ExtractNativeDC(jni, j_dc)->label()); +} + +JOW(jobject, DataChannel_state)(JNIEnv* jni, jobject j_dc) { + return JavaEnumFromIndex( + jni, "DataChannel$State", ExtractNativeDC(jni, j_dc)->state()); +} + +JOW(jlong, DataChannel_bufferedAmount)(JNIEnv* jni, jobject j_dc) { + uint64 buffered_amount = ExtractNativeDC(jni, j_dc)->buffered_amount(); + CHECK(buffered_amount <= std::numeric_limits::max(), + "buffered_amount overflowed jlong!"); + return static_cast(buffered_amount); +} + +JOW(void, DataChannel_close)(JNIEnv* jni, jobject j_dc) { + ExtractNativeDC(jni, j_dc)->Close(); +} + +JOW(jboolean, DataChannel_sendNative)(JNIEnv* jni, jobject j_dc, + jbyteArray data, jboolean binary) { + jbyte* bytes = jni->GetByteArrayElements(data, NULL); + bool ret = ExtractNativeDC(jni, j_dc)->Send(DataBuffer( + talk_base::Buffer(bytes, jni->GetArrayLength(data)), + binary)); + jni->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + return ret; +} + +JOW(void, DataChannel_dispose)(JNIEnv* jni, jobject j_dc) { + ExtractNativeDC(jni, j_dc)->Release(); +} + JOW(void, PeerConnection_freePeerConnection)(JNIEnv*, jclass, jlong j_p) { reinterpret_cast(j_p)->Release(); } @@ -1134,6 +1305,23 @@ JOW(jobject, PeerConnection_getRemoteDescription)(JNIEnv* jni, jobject j_pc) { return sdp ? JavaSdpFromNativeSdp(jni, sdp) : NULL; } +JOW(jobject, PeerConnection_createDataChannel)( + JNIEnv* jni, jobject j_pc, jstring j_label, jobject j_init) { + DataChannelInit init = JavaDataChannelInitToNative(jni, j_init); + talk_base::scoped_refptr channel( + ExtractNativePC(jni, j_pc)->CreateDataChannel( + JavaToStdString(jni, j_label), &init)); + jclass j_data_channel_class = FindClass(jni, "org/webrtc/DataChannel"); + jmethodID j_data_channel_ctor = GetMethodID( + jni, j_data_channel_class, "", "(J)V"); + jobject j_channel = jni->NewObject( + j_data_channel_class, j_data_channel_ctor, channel.get()); + CHECK_EXCEPTION(jni, "error during NewObject"); + // Channel is now owned by Java object, and will be freed from there. + channel->AddRef(); + return j_channel; +} + JOW(void, PeerConnection_createOffer)( JNIEnv* jni, jobject j_pc, jobject j_observer, jobject j_constraints) { ConstraintsWrapper* constraints = diff --git a/talk/app/webrtc/java/src/org/webrtc/DataChannel.java b/talk/app/webrtc/java/src/org/webrtc/DataChannel.java new file mode 100644 index 000000000..6f8d5325c --- /dev/null +++ b/talk/app/webrtc/java/src/org/webrtc/DataChannel.java @@ -0,0 +1,282 @@ +/* + * libjingle + * Copyright 2013, 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; + +import java.nio.ByteBuffer; + +/** Java wrapper for a C++ DataChannelInterface. */ +public class DataChannel { + /** Java wrapper for WebIDL RTCDataChannel. */ + public static class Init { + public boolean ordered = true; + // Optional unsigned short in WebIDL, -1 means unspecified. + public int maxRetransmitTimeMs = -1; + // Optional unsigned short in WebIDL, -1 means unspecified. + public int maxRetransmits = -1; + public String protocol = ""; + public boolean negotiated = true; + // Optional unsigned short in WebIDL, -1 means unspecified. + public int id = -1; + + public Init() {} + + // Called only by native code. + private Init( + boolean ordered, int maxRetransmitTimeMs, int maxRetransmits, + String protocol, boolean negotiated, int id) { + this.ordered = ordered; + this.maxRetransmitTimeMs = maxRetransmitTimeMs; + this.maxRetransmits = maxRetransmits; + this.protocol = protocol; + this.negotiated = negotiated; + this.id = id; + } + } + + /** Java version of C++ DataBuffer. The atom of data in a DataChannel. */ + public static class Buffer { + /** The underlying data. */ + public final ByteBuffer data; + + /** + * Indicates whether |data| contains UTF-8 text or "binary data" + * (i.e. anything else). + */ + public final boolean binary; + + public Buffer(ByteBuffer data, boolean binary) { + this.data = data; + this.binary = binary; + } + } + + /** Java version of C++ DataChannelObserver. */ + public interface Observer { + /** The data channel state has changed. */ + public void onStateChange(); + /** + * A data buffer was successfully received. NOTE: |buffer.data| will be + * freed once this function returns so callers who want to use the data + * asynchronously must make sure to copy it first. + */ + public void onMessage(Buffer buffer); + } + + /** Keep in sync with DataChannelInterface::DataState. */ + public enum State { CONNECTING, OPEN, CLOSING, CLOSED }; + + private final long nativeDataChannel; + private long nativeObserver; + + public DataChannel(long nativeDataChannel) { + this.nativeDataChannel = nativeDataChannel; + } + + /** Register |observer|, replacing any previously-registered observer. */ + public void registerObserver(Observer observer) { + if (nativeObserver != 0) { + unregisterObserverNative(nativeObserver); + } + nativeObserver = registerObserverNative(observer); + } + private native long registerObserverNative(Observer observer); + + /** Unregister the (only) observer. */ + public void unregisterObserver() { + unregisterObserverNative(nativeObserver); + } + private native void unregisterObserverNative(long nativeObserver); + + public native String label(); + + public native State state(); + + /** + * Return the number of bytes of application data (UTF-8 text and binary data) + * that have been queued using SendBuffer but have not yet been transmitted + * to the network. + */ + public native long bufferedAmount(); + + /** Close the channel. */ + public native void close(); + + /** Send |data| to the remote peer; return success. */ + public boolean send(Buffer buffer) { + // TODO(fischman): this could be cleverer about avoiding copies if the + // ByteBuffer is direct and/or is backed by an array. + byte[] data = new byte[buffer.data.remaining()]; + buffer.data.get(data); + return sendNative(data, buffer.binary); + } + private native boolean sendNative(byte[] data, boolean binary); + + /** Dispose of native resources attached to this channel. */ + public native void dispose(); +}; +/* + * libjingle + * Copyright 2013, 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; + +import java.nio.ByteBuffer; + +/** Java wrapper for a C++ DataChannelInterface. */ +public class DataChannel { + /** Java wrapper for WebIDL RTCDataChannel. */ + public static class Init { + public boolean ordered = true; + // Optional unsigned short in WebIDL, -1 means unspecified. + public int maxRetransmitTimeMs = -1; + // Optional unsigned short in WebIDL, -1 means unspecified. + public int maxRetransmits = -1; + public String protocol = ""; + public boolean negotiated = true; + // Optional unsigned short in WebIDL, -1 means unspecified. + public int id = -1; + + public Init() {} + + // Called only by native code. + private Init( + boolean ordered, int maxRetransmitTimeMs, int maxRetransmits, + String protocol, boolean negotiated, int id) { + this.ordered = ordered; + this.maxRetransmitTimeMs = maxRetransmitTimeMs; + this.maxRetransmits = maxRetransmits; + this.protocol = protocol; + this.negotiated = negotiated; + this.id = id; + } + } + + /** Java version of C++ DataBuffer. The atom of data in a DataChannel. */ + public static class Buffer { + /** The underlying data. */ + public final ByteBuffer data; + + /** + * Indicates whether |data| contains UTF-8 text or "binary data" + * (i.e. anything else). + */ + public final boolean binary; + + public Buffer(ByteBuffer data, boolean binary) { + this.data = data; + this.binary = binary; + } + } + + /** Java version of C++ DataChannelObserver. */ + public interface Observer { + /** The data channel state has changed. */ + public void onStateChange(); + /** + * A data buffer was successfully received. NOTE: |buffer.data| will be + * freed once this function returns so callers who want to use the data + * asynchronously must make sure to copy it first. + */ + public void onMessage(Buffer buffer); + } + + /** Keep in sync with DataChannelInterface::DataState. */ + public enum State { CONNECTING, OPEN, CLOSING, CLOSED }; + + private final long nativeDataChannel; + private long nativeObserver; + + public DataChannel(long nativeDataChannel) { + this.nativeDataChannel = nativeDataChannel; + } + + /** Register |observer|, replacing any previously-registered observer. */ + public void registerObserver(Observer observer) { + if (nativeObserver != 0) { + unregisterObserverNative(nativeObserver); + } + nativeObserver = registerObserverNative(observer); + } + private native long registerObserverNative(Observer observer); + + /** Unregister the (only) observer. */ + public void unregisterObserver() { + unregisterObserverNative(nativeObserver); + } + private native void unregisterObserverNative(long nativeObserver); + + public native String label(); + + public native State state(); + + /** + * Return the number of bytes of application data (UTF-8 text and binary data) + * that have been queued using SendBuffer but have not yet been transmitted + * to the network. + */ + public native long bufferedAmount(); + + /** Close the channel. */ + public native void close(); + + /** Send |data| to the remote peer; return success. */ + public boolean send(Buffer buffer) { + // TODO(fischman): this could be cleverer about avoiding copies if the + // ByteBuffer is direct and/or is backed by an array. + byte[] data = new byte[buffer.data.remaining()]; + buffer.data.get(data); + return sendNative(data, buffer.binary); + } + private native boolean sendNative(byte[] data, boolean binary); + + /** Dispose of native resources attached to this channel. */ + public native void dispose(); +}; diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java index 5d08c0457..0a0a7f636 100644 --- a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java +++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java @@ -79,6 +79,9 @@ public class PeerConnection { /** Triggered when a remote peer close a stream. */ public void onRemoveStream(MediaStream stream); + + /** Triggered when a remote peer opens a DataChannel. */ + public void onDataChannel(DataChannel dataChannel); } /** Java version of PeerConnectionInterface.IceServer. */ @@ -118,6 +121,9 @@ public class PeerConnection { public native SessionDescription getRemoteDescription(); + public native DataChannel createDataChannel( + String label, DataChannel.Init init); + public native void createOffer( SdpObserver observer, MediaConstraints constraints); diff --git a/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java b/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java index cdd8c734f..5d14ee51c 100644 --- a/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java +++ b/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java @@ -35,9 +35,13 @@ import org.webrtc.PeerConnection.IceGatheringState; import org.webrtc.PeerConnection.SignalingState; import java.lang.ref.WeakReference; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Arrays; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.Map; +import java.util.TreeSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -48,7 +52,9 @@ public class PeerConnectionTest extends TestCase { private static class ObserverExpectations implements PeerConnection.Observer, VideoRenderer.Callbacks, + DataChannel.Observer, StatsObserver { + private final String name; private int expectedIceCandidates = 0; private int expectedErrors = 0; private LinkedList expectedSetSizeDimensions = @@ -68,10 +74,28 @@ public class PeerConnectionTest extends TestCase { new LinkedList(); private Map> renderers = new IdentityHashMap>(); + private DataChannel dataChannel; + private LinkedList expectedBuffers = + new LinkedList(); + private LinkedList expectedStateChanges = + new LinkedList(); + private LinkedList expectedRemoteDataChannelLabels = + new LinkedList(); private int expectedStatsCallbacks = 0; private LinkedList gotStatsReports = new LinkedList(); + public ObserverExpectations(String name) { + this.name = name; + } + + public synchronized void setDataChannel(DataChannel dataChannel) { + assertNull(this.dataChannel); + this.dataChannel = dataChannel; + this.dataChannel.registerObserver(this); + assertNotNull(this.dataChannel); + } + public synchronized void expectIceCandidates(int count) { expectedIceCandidates += count; } @@ -93,17 +117,24 @@ public class PeerConnectionTest extends TestCase { } public synchronized void expectSetSize(int width, int height) { + if (RENDER_TO_GUI) { + // When new frames are delivered to the GUI renderer we don't get + // notified of frame size info. + return; + } expectedSetSizeDimensions.add(width); expectedSetSizeDimensions.add(height); } @Override public synchronized void setSize(int width, int height) { + assertFalse(RENDER_TO_GUI); assertEquals(width, expectedSetSizeDimensions.removeFirst().intValue()); assertEquals(height, expectedSetSizeDimensions.removeFirst().intValue()); } public synchronized void expectFramesDelivered(int count) { + assertFalse(RENDER_TO_GUI); expectedFramesDelivered += count; } @@ -127,7 +158,8 @@ public class PeerConnectionTest extends TestCase { } @Override - public void onIceConnectionChange(IceConnectionState newState) { + public synchronized void onIceConnectionChange( + IceConnectionState newState) { assertEquals(expectedIceConnectionChanges.removeFirst(), newState); } @@ -137,7 +169,7 @@ public class PeerConnectionTest extends TestCase { } @Override - public void onIceGatheringChange(IceGatheringState newState) { + public synchronized void onIceGatheringChange(IceGatheringState newState) { // It's fine to get a variable number of GATHERING messages before // COMPLETE fires (depending on how long the test runs) so we don't assert // any particular count. @@ -178,6 +210,40 @@ public class PeerConnectionTest extends TestCase { stream.videoTracks.get(0).removeRenderer(renderer.get()); } + public synchronized void expectDataChannel(String label) { + expectedRemoteDataChannelLabels.add(label); + } + + @Override + public synchronized void onDataChannel(DataChannel remoteDataChannel) { + assertEquals(expectedRemoteDataChannelLabels.removeFirst(), + remoteDataChannel.label()); + setDataChannel(remoteDataChannel); + assertEquals(DataChannel.State.CONNECTING, dataChannel.state()); + } + + public synchronized void expectMessage(ByteBuffer expectedBuffer, + boolean expectedBinary) { + expectedBuffers.add( + new DataChannel.Buffer(expectedBuffer, expectedBinary)); + } + + @Override + public synchronized void onMessage(DataChannel.Buffer buffer) { + DataChannel.Buffer expected = expectedBuffers.removeFirst(); + assertEquals(expected.binary, buffer.binary); + assertTrue(expected.data.equals(buffer.data)); + } + + @Override + public synchronized void onStateChange() { + assertEquals(expectedStateChanges.removeFirst(), dataChannel.state()); + } + + public synchronized void expectStateChange(DataChannel.State state) { + expectedStateChanges.add(state); + } + @Override public synchronized void onComplete(StatsReport[] reports) { if (--expectedStatsCallbacks < 0) { @@ -196,17 +262,61 @@ public class PeerConnectionTest extends TestCase { return got; } - public synchronized boolean areAllExpectationsSatisfied() { - return expectedIceCandidates <= 0 && // See comment in onIceCandidate. - expectedErrors == 0 && - expectedSignalingChanges.size() == 0 && - expectedIceConnectionChanges.size() == 0 && - expectedIceGatheringChanges.size() == 0 && - expectedAddStreamLabels.size() == 0 && - expectedRemoveStreamLabels.size() == 0 && - expectedSetSizeDimensions.isEmpty() && - expectedFramesDelivered <= 0 && - expectedStatsCallbacks == 0; + // Return a set of expectations that haven't been satisfied yet, possibly + // empty if no such expectations exist. + public synchronized TreeSet unsatisfiedExpectations() { + TreeSet stillWaitingForExpectations = new TreeSet(); + if (expectedIceCandidates > 0) { // See comment in onIceCandidate. + stillWaitingForExpectations.add("expectedIceCandidates"); + } + if (expectedErrors != 0) { + stillWaitingForExpectations.add("expectedErrors: " + expectedErrors); + } + if (expectedSignalingChanges.size() != 0) { + stillWaitingForExpectations.add( + "expectedSignalingChanges: " + expectedSignalingChanges.size()); + } + if (expectedIceConnectionChanges.size() != 0) { + stillWaitingForExpectations.add("expectedIceConnectionChanges: " + + expectedIceConnectionChanges.size()); + } + if (expectedIceGatheringChanges.size() != 0) { + stillWaitingForExpectations.add("expectedIceGatheringChanges: " + + expectedIceGatheringChanges.size()); + } + if (expectedAddStreamLabels.size() != 0) { + stillWaitingForExpectations.add( + "expectedAddStreamLabels: " + expectedAddStreamLabels.size()); + } + if (expectedRemoveStreamLabels.size() != 0) { + stillWaitingForExpectations.add( + "expectedRemoveStreamLabels: " + expectedRemoveStreamLabels.size()); + } + if (!expectedSetSizeDimensions.isEmpty()) { + stillWaitingForExpectations.add( + "expectedSetSizeDimensions: " + expectedSetSizeDimensions.size()); + } + if (expectedFramesDelivered > 0) { + stillWaitingForExpectations.add( + "expectedFramesDelivered: " + expectedFramesDelivered); + } + if (!expectedBuffers.isEmpty()) { + stillWaitingForExpectations.add( + "expectedBuffers: " + expectedBuffers.size()); + } + if (!expectedStateChanges.isEmpty()) { + stillWaitingForExpectations.add( + "expectedStateChanges: " + expectedStateChanges.size()); + } + if (!expectedRemoteDataChannelLabels.isEmpty()) { + stillWaitingForExpectations.add("expectedRemoteDataChannelLabels: " + + expectedRemoteDataChannelLabels.size()); + } + if (expectedStatsCallbacks != 0) { + stillWaitingForExpectations.add( + "expectedStatsCallbacks: " + expectedStatsCallbacks); + } + return stillWaitingForExpectations; } public void waitForAllExpectationsToBeSatisfied() { @@ -219,12 +329,27 @@ public class PeerConnectionTest extends TestCase { // stall a wait). Use callbacks to fire off dependent steps instead of // explicitly waiting, so there can be just a single wait at the end of // the test. - while (!areAllExpectationsSatisfied()) { + TreeSet prev = null; + TreeSet stillWaitingForExpectations = unsatisfiedExpectations(); + while (!stillWaitingForExpectations.isEmpty()) { + if (!stillWaitingForExpectations.equals(prev)) { + System.out.println( + name + " still waiting at\n " + + (new Throwable()).getStackTrace()[1] + + "\n for: " + + Arrays.toString(stillWaitingForExpectations.toArray())); + } try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } + prev = stillWaitingForExpectations; + stillWaitingForExpectations = unsatisfiedExpectations(); + } + if (prev == null) { + System.out.println(name + " didn't need to wait at\n " + + (new Throwable()).getStackTrace()[1]); } } } @@ -298,9 +423,9 @@ public class PeerConnectionTest extends TestCase { } private static VideoRenderer createVideoRenderer( - ObserverExpectations observer) { + VideoRenderer.Callbacks videoCallbacks) { if (!RENDER_TO_GUI) { - return new VideoRenderer(observer); + return new VideoRenderer(videoCallbacks); } ++videoWindowsMapped; assertTrue(videoWindowsMapped < 4); @@ -315,12 +440,12 @@ public class PeerConnectionTest extends TestCase { PeerConnectionFactory factory, PeerConnection pc, VideoSource videoSource, String streamLabel, String videoTrackId, String audioTrackId, - ObserverExpectations observer) { + VideoRenderer.Callbacks videoCallbacks) { MediaStream lMS = factory.createLocalMediaStream(streamLabel); VideoTrack videoTrack = factory.createVideoTrack(videoTrackId, videoSource); assertNotNull(videoTrack); - VideoRenderer videoRenderer = createVideoRenderer(observer); + VideoRenderer videoRenderer = createVideoRenderer(videoCallbacks); assertNotNull(videoRenderer); videoTrack.addRenderer(videoRenderer); lMS.addTrack(videoTrack); @@ -343,7 +468,15 @@ public class PeerConnectionTest extends TestCase { CountDownLatch testDone = new CountDownLatch(1); PeerConnectionFactory factory = new PeerConnectionFactory(); - MediaConstraints constraints = new MediaConstraints(); + MediaConstraints pcConstraints = new MediaConstraints(); + pcConstraints.mandatory.add( + new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); + pcConstraints.optional.add( + new MediaConstraints.KeyValuePair("RtpDataChannels", "true")); + // TODO(fischman): replace above with below to test SCTP channels when + // supported (https://code.google.com/p/webrtc/issues/detail?id=1408). + // pcConstraints.optional.add(new MediaConstraints.KeyValuePair( + // "internalSctpDataChannels", "true")); LinkedList iceServers = new LinkedList(); @@ -351,14 +484,16 @@ public class PeerConnectionTest extends TestCase { "stun:stun.l.google.com:19302")); iceServers.add(new PeerConnection.IceServer( "turn:fake.example.com", "fakeUsername", "fakePassword")); - ObserverExpectations offeringExpectations = new ObserverExpectations(); + ObserverExpectations offeringExpectations = + new ObserverExpectations("PCTest:offerer"); PeerConnection offeringPC = factory.createPeerConnection( - iceServers, constraints, offeringExpectations); + iceServers, pcConstraints, offeringExpectations); assertNotNull(offeringPC); - ObserverExpectations answeringExpectations = new ObserverExpectations(); + ObserverExpectations answeringExpectations = + new ObserverExpectations("PCTest:answerer"); PeerConnection answeringPC = factory.createPeerConnection( - iceServers, constraints, answeringExpectations); + iceServers, pcConstraints, answeringExpectations); assertNotNull(answeringPC); // We want to use the same camera for offerer & answerer, so create it here @@ -371,12 +506,16 @@ public class PeerConnectionTest extends TestCase { // serialized SDP, because the C++ API doesn't auto-translate. // Drop |label| params from {Audio,Video}Track-related APIs once // https://code.google.com/p/webrtc/issues/detail?id=1253 is fixed. + offeringExpectations.expectSetSize(640, 480); WeakReference oLMS = addTracksToPC( factory, offeringPC, videoSource, "oLMS", "oLMSv0", "oLMSa0", offeringExpectations); + offeringExpectations.setDataChannel(offeringPC.createDataChannel( + "offeringDC", new DataChannel.Init())); + SdpObserverLatch sdpLatch = new SdpObserverLatch(); - offeringPC.createOffer(sdpLatch, constraints); + offeringPC.createOffer(sdpLatch, new MediaConstraints()); assertTrue(sdpLatch.await()); SessionDescription offerSdp = sdpLatch.getSdp(); assertEquals(offerSdp.type, SessionDescription.Type.OFFER); @@ -386,19 +525,20 @@ public class PeerConnectionTest extends TestCase { answeringExpectations.expectSignalingChange( SignalingState.HAVE_REMOTE_OFFER); answeringExpectations.expectAddStream("oLMS"); + answeringExpectations.expectDataChannel("offeringDC"); answeringPC.setRemoteDescription(sdpLatch, offerSdp); - answeringExpectations.waitForAllExpectationsToBeSatisfied(); assertEquals( PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); assertTrue(sdpLatch.await()); assertNull(sdpLatch.getSdp()); + answeringExpectations.expectSetSize(640, 480); WeakReference aLMS = addTracksToPC( factory, answeringPC, videoSource, "aLMS", "aLMSv0", "aLMSa0", answeringExpectations); sdpLatch = new SdpObserverLatch(); - answeringPC.createAnswer(sdpLatch, constraints); + answeringPC.createAnswer(sdpLatch, new MediaConstraints()); assertTrue(sdpLatch.await()); SessionDescription answerSdp = sdpLatch.getSdp(); assertEquals(answerSdp.type, SessionDescription.Type.ANSWER); @@ -407,6 +547,9 @@ public class PeerConnectionTest extends TestCase { offeringExpectations.expectIceCandidates(2); answeringExpectations.expectIceCandidates(2); + offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); + answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); + sdpLatch = new SdpObserverLatch(); answeringExpectations.expectSignalingChange(SignalingState.STABLE); answeringPC.setLocalDescription(sdpLatch, answerSdp); @@ -434,14 +577,12 @@ public class PeerConnectionTest extends TestCase { assertEquals(answeringPC.getRemoteDescription().type, offerSdp.type); if (!RENDER_TO_GUI) { - offeringExpectations.expectSetSize(640, 480); - offeringExpectations.expectSetSize(640, 480); - answeringExpectations.expectSetSize(640, 480); - answeringExpectations.expectSetSize(640, 480); // Wait for at least some frames to be delivered at each end (number // chosen arbitrarily). offeringExpectations.expectFramesDelivered(10); answeringExpectations.expectFramesDelivered(10); + offeringExpectations.expectSetSize(640, 480); + answeringExpectations.expectSetSize(640, 480); } offeringExpectations.expectIceConnectionChange( @@ -453,8 +594,8 @@ public class PeerConnectionTest extends TestCase { answeringExpectations.expectIceConnectionChange( IceConnectionState.CONNECTED); - offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); - answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); + offeringExpectations.expectStateChange(DataChannel.State.OPEN); + answeringExpectations.expectStateChange(DataChannel.State.OPEN); for (IceCandidate candidate : offeringExpectations.gotIceCandidates) { answeringPC.addIceCandidate(candidate); @@ -473,6 +614,38 @@ public class PeerConnectionTest extends TestCase { assertEquals( PeerConnection.SignalingState.STABLE, answeringPC.signalingState()); + // Test send & receive UTF-8 text. + answeringExpectations.expectMessage( + ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); + DataChannel.Buffer buffer = new DataChannel.Buffer( + ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); + assertTrue(offeringExpectations.dataChannel.send(buffer)); + answeringExpectations.waitForAllExpectationsToBeSatisfied(); + + // TODO(fischman): add testing of binary messages when SCTP channels are + // supported (https://code.google.com/p/webrtc/issues/detail?id=1408). + // // Construct this binary message two different ways to ensure no + // // shortcuts are taken. + // ByteBuffer expectedBinaryMessage = ByteBuffer.allocateDirect(5); + // for (byte i = 1; i < 6; ++i) { + // expectedBinaryMessage.put(i); + // } + // expectedBinaryMessage.flip(); + // offeringExpectations.expectMessage(expectedBinaryMessage, true); + // assertTrue(answeringExpectations.dataChannel.send( + // new DataChannel.Buffer( + // ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5 } ), true))); + // offeringExpectations.waitForAllExpectationsToBeSatisfied(); + + offeringExpectations.expectStateChange(DataChannel.State.CLOSING); + answeringExpectations.expectStateChange(DataChannel.State.CLOSING); + answeringExpectations.dataChannel.close(); + offeringExpectations.dataChannel.close(); + // TODO(fischman): implement a new offer/answer exchange to finalize the + // closing of the channel in order to see the CLOSED state reached. + // offeringExpectations.expectStateChange(DataChannel.State.CLOSED); + // answeringExpectations.expectStateChange(DataChannel.State.CLOSED); + if (RENDER_TO_GUI) { try { Thread.sleep(3000); @@ -505,6 +678,8 @@ public class PeerConnectionTest extends TestCase { private static void shutdownPC( PeerConnection pc, ObserverExpectations expectations) { + expectations.dataChannel.unregisterObserver(); + expectations.dataChannel.dispose(); expectations.expectStatsCallback(); assertTrue(pc.getStats(expectations, null)); expectations.waitForAllExpectationsToBeSatisfied(); diff --git a/talk/app/webrtc/mediastreamsignaling_unittest.cc b/talk/app/webrtc/mediastreamsignaling_unittest.cc index 7f8745403..8b3dacb52 100644 --- a/talk/app/webrtc/mediastreamsignaling_unittest.cc +++ b/talk/app/webrtc/mediastreamsignaling_unittest.cc @@ -732,8 +732,9 @@ TEST_F(MediaStreamSignalingTest, AddRemoveTrackFromExistingRemoteMediaStream) { reference_collection_)); // Remove the extra audio and video tracks again. - CreateSessionDescriptionAndReference(1, 1, desc_ms1.use()); - signaling_->OnRemoteDescriptionChanged(desc_ms1.get()); + talk_base::scoped_ptr desc_ms2; + CreateSessionDescriptionAndReference(1, 1, desc_ms2.use()); + signaling_->OnRemoteDescriptionChanged(desc_ms2.get()); EXPECT_TRUE(CompareStreamCollections(signaling_->remote_streams(), reference_collection_)); EXPECT_TRUE(CompareStreamCollections(observer_->remote_streams(), diff --git a/talk/app/webrtc/objc/RTCPeerConnectionFactory.mm b/talk/app/webrtc/objc/RTCPeerConnectionFactory.mm index b12af9dfa..84e8c5eee 100644 --- a/talk/app/webrtc/objc/RTCPeerConnectionFactory.mm +++ b/talk/app/webrtc/objc/RTCPeerConnectionFactory.mm @@ -83,9 +83,11 @@ } webrtc::RTCPeerConnectionObserver *observer = new webrtc::RTCPeerConnectionObserver(delegate); + webrtc::DTLSIdentityServiceInterface* dummy_dtls_identity_service = NULL; talk_base::scoped_refptr peerConnection = self.nativeFactory->CreatePeerConnection( - iceServers, constraints.constraints, observer); + iceServers, constraints.constraints, dummy_dtls_identity_service, + observer); RTCPeerConnection *pc = [[RTCPeerConnection alloc] initWithPeerConnection:peerConnection observer:observer]; diff --git a/talk/app/webrtc/peerconnection_unittest.cc b/talk/app/webrtc/peerconnection_unittest.cc index 96a9c1cd4..eed693613 100644 --- a/talk/app/webrtc/peerconnection_unittest.cc +++ b/talk/app/webrtc/peerconnection_unittest.cc @@ -884,6 +884,7 @@ class P2PTestConductor : public testing::Test { if (receiving_client_) { receiving_client_->set_signaling_message_receiver(NULL); } + talk_base::CleanupSSL(); } bool CreateTestClients() { diff --git a/talk/app/webrtc/portallocatorfactory.cc b/talk/app/webrtc/portallocatorfactory.cc index 59ac9fb3f..3ded85a5f 100644 --- a/talk/app/webrtc/portallocatorfactory.cc +++ b/talk/app/webrtc/portallocatorfactory.cc @@ -31,7 +31,7 @@ #include "talk/base/network.h" #include "talk/base/thread.h" #include "talk/p2p/base/basicpacketsocketfactory.h" -#include "talk/p2p/client/httpportallocator.h" +#include "talk/p2p/client/basicportallocator.h" static const char kUserAgent[] = "PeerConnection User Agent"; diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc index 55b295076..e56e8a28e 100644 --- a/talk/app/webrtc/webrtcsession_unittest.cc +++ b/talk/app/webrtc/webrtcsession_unittest.cc @@ -470,8 +470,8 @@ class WebRtcSessionTest : public testing::Test { desc_factory_->set_secure(cricket::SEC_ENABLED); std::string identity_name = "WebRTC" + talk_base::ToString(talk_base::CreateRandomId()); - tdesc_factory_->set_identity(talk_base::SSLIdentity::Generate( - identity_name)); + identity_.reset(talk_base::SSLIdentity::Generate(identity_name)); + tdesc_factory_->set_identity(identity_.get()); tdesc_factory_->set_digest_algorithm(talk_base::DIGEST_SHA_256); tdesc_factory_->set_secure(cricket::SEC_REQUIRED); } @@ -701,8 +701,8 @@ class WebRtcSessionTest : public testing::Test { // and answer. SetLocalDescriptionWithoutError(offer); - SessionDescriptionInterface* answer = CreateRemoteAnswer( - session_->local_description()); + talk_base::scoped_ptr answer( + CreateRemoteAnswer(session_->local_description())); std::string sdp; EXPECT_TRUE(answer->ToString(&sdp)); @@ -722,11 +722,9 @@ class WebRtcSessionTest : public testing::Test { SessionDescriptionInterface* new_answer = CreateSessionDescription( JsepSessionDescription::kAnswer, sdp, NULL); - delete answer; - answer = new_answer; // SetRemoteDescription to enable rtcp mux. - SetRemoteDescriptionWithoutError(answer); + SetRemoteDescriptionWithoutError(new_answer); EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout); EXPECT_EQ(expected_candidate_num, observer_.mline_0_candidates_.size()); EXPECT_EQ(expected_candidate_num, observer_.mline_1_candidates_.size()); @@ -858,6 +856,7 @@ class WebRtcSessionTest : public testing::Test { cricket::FakeDeviceManager* device_manager_; talk_base::scoped_ptr channel_manager_; talk_base::scoped_ptr tdesc_factory_; + talk_base::scoped_ptr identity_; talk_base::scoped_ptr desc_factory_; talk_base::scoped_ptr pss_; talk_base::scoped_ptr vss_; @@ -1877,8 +1876,8 @@ TEST_F(WebRtcSessionTest, VerifyCryptoParamsInSDP) { session_->CreateOffer(NULL)); VerifyCryptoParams(offer->description()); SetRemoteDescriptionWithoutError(offer.release()); - const webrtc::SessionDescriptionInterface* answer = - session_->CreateAnswer(NULL); + scoped_ptr answer( + session_->CreateAnswer(NULL)); VerifyCryptoParams(answer->description()); } @@ -2101,7 +2100,8 @@ TEST_F(WebRtcSessionTest, TestInitiatorGIceInAnswer) { Init(); mediastream_signaling_.SendAudioVideoStream1(); SessionDescriptionInterface* offer = session_->CreateOffer(NULL); - SessionDescriptionInterface* answer = CreateRemoteAnswer(offer); + talk_base::scoped_ptr answer( + CreateRemoteAnswer(offer)); SetLocalDescriptionWithoutError(offer); std::string sdp; EXPECT_TRUE(answer->ToString(&sdp)); @@ -2137,7 +2137,8 @@ TEST_F(WebRtcSessionTest, TestReceiverGIceInOffer) { mediastream_signaling_.SendAudioVideoStream1(); SessionDescriptionInterface* offer = session_->CreateOffer(NULL); SetRemoteDescriptionWithoutError(offer); - SessionDescriptionInterface* answer = session_->CreateAnswer(NULL); + talk_base::scoped_ptr answer( + session_->CreateAnswer(NULL)); std::string sdp; EXPECT_TRUE(answer->ToString(&sdp)); // Adding ice-options to the session level. @@ -2185,15 +2186,16 @@ TEST_F(WebRtcSessionTest, TestIceOfferGIceOnlyAnswer) { SetLocalDescriptionWithoutError(ice_only_offer); std::string original_offer_sdp; EXPECT_TRUE(offer->ToString(&original_offer_sdp)); - SessionDescriptionInterface* pranswer_with_gice = + talk_base::scoped_ptr pranswer_with_gice( CreateSessionDescription(JsepSessionDescription::kPrAnswer, - original_offer_sdp, NULL); + original_offer_sdp, NULL)); SetRemoteDescriptionExpectError(kPushDownPranswerTDFailed, - pranswer_with_gice); - SessionDescriptionInterface* answer_with_gice = + pranswer_with_gice.get()); + talk_base::scoped_ptr answer_with_gice( CreateSessionDescription(JsepSessionDescription::kAnswer, - original_offer_sdp, NULL); - SetRemoteDescriptionExpectError(kPushDownAnswerTDFailed, answer_with_gice); + original_offer_sdp, NULL)); + SetRemoteDescriptionExpectError(kPushDownAnswerTDFailed, + answer_with_gice.get()); } // Verifing local offer and remote answer have matching m-lines as per RFC 3264. @@ -2207,13 +2209,13 @@ TEST_F(WebRtcSessionTest, TestIncorrectMLinesInRemoteAnswer) { cricket::SessionDescription* answer_copy = answer->description()->Copy(); answer_copy->RemoveContentByName("video"); - JsepSessionDescription* modified_answer = - new JsepSessionDescription(JsepSessionDescription::kAnswer); + talk_base::scoped_ptr modified_answer( + new JsepSessionDescription(JsepSessionDescription::kAnswer)); EXPECT_TRUE(modified_answer->Initialize(answer_copy, answer->session_id(), answer->session_version())); - SetRemoteDescriptionExpectError(kMlineMismatch, modified_answer); + SetRemoteDescriptionExpectError(kMlineMismatch, modified_answer.get()); // Modifying content names. std::string sdp; @@ -2227,9 +2229,9 @@ TEST_F(WebRtcSessionTest, TestIncorrectMLinesInRemoteAnswer) { kAudioMidReplaceStr.length(), &sdp); - SessionDescriptionInterface* modified_answer1 = - CreateSessionDescription(JsepSessionDescription::kAnswer, sdp, NULL); - SetRemoteDescriptionExpectError(kMlineMismatch, modified_answer1); + talk_base::scoped_ptr modified_answer1( + CreateSessionDescription(JsepSessionDescription::kAnswer, sdp, NULL)); + SetRemoteDescriptionExpectError(kMlineMismatch, modified_answer1.get()); SetRemoteDescriptionWithoutError(answer.release()); } @@ -2388,9 +2390,9 @@ TEST_F(WebRtcSessionTest, TestSessionContentError) { video_channel_->set_fail_set_send_codecs(true); mediastream_signaling_.SendAudioVideoStream2(); - SessionDescriptionInterface* answer = - CreateRemoteAnswer(session_->local_description()); - SetRemoteDescriptionExpectError("ERROR_CONTENT", answer); + talk_base::scoped_ptr answer( + CreateRemoteAnswer(session_->local_description())); + SetRemoteDescriptionExpectError("ERROR_CONTENT", answer.get()); } // Runs the loopback call test with BUNDLE and STUN disabled. diff --git a/talk/base/httpbase_unittest.cc b/talk/base/httpbase_unittest.cc index 73ef9491f..bd1796d1a 100644 --- a/talk/base/httpbase_unittest.cc +++ b/talk/base/httpbase_unittest.cc @@ -73,6 +73,7 @@ public: virtual void SetUp() { } virtual void TearDown() { + delete http_stream; // Avoid an ASSERT, in case a test doesn't clean up properly base.abort(HE_NONE); } diff --git a/talk/base/multipart_unittest.cc b/talk/base/multipart_unittest.cc index 18e3cf9a5..7266ed5fc 100644 --- a/talk/base/multipart_unittest.cc +++ b/talk/base/multipart_unittest.cc @@ -78,8 +78,8 @@ TEST(MultipartTest, TestAddAndRead) { EXPECT_TRUE(multipart.GetSize(&size)); EXPECT_EQ(part_size, size); - talk_base::MemoryStream* stream = - new talk_base::MemoryStream(kTestStreamContent); + talk_base::scoped_ptr stream( + new talk_base::MemoryStream(kTestStreamContent)); size_t stream_size = 0; EXPECT_TRUE(stream->GetSize(&stream_size)); part_size += diff --git a/talk/base/nat_unittest.cc b/talk/base/nat_unittest.cc index 03b1cd125..03170ca0e 100644 --- a/talk/base/nat_unittest.cc +++ b/talk/base/nat_unittest.cc @@ -247,11 +247,11 @@ void TestPhysicalInternal(const SocketAddress& int_addr) { SocketAddress(ext_addr2) }; - PhysicalSocketServer* int_pss = new PhysicalSocketServer(); - PhysicalSocketServer* ext_pss = new PhysicalSocketServer(); + scoped_ptr int_pss(new PhysicalSocketServer()); + scoped_ptr ext_pss(new PhysicalSocketServer()); - TestBindings(int_pss, int_addr, ext_pss, ext_addrs); - TestFilters(int_pss, int_addr, ext_pss, ext_addrs); + TestBindings(int_pss.get(), int_addr, ext_pss.get(), ext_addrs); + TestFilters(int_pss.get(), int_addr, ext_pss.get(), ext_addrs); } TEST(NatTest, TestPhysicalIPv4) { @@ -269,16 +269,20 @@ TEST(NatTest, TestPhysicalIPv6) { class TestVirtualSocketServer : public VirtualSocketServer { public: explicit TestVirtualSocketServer(SocketServer* ss) - : VirtualSocketServer(ss) {} + : VirtualSocketServer(ss), + ss_(ss) {} // Expose this publicly IPAddress GetNextIP(int af) { return VirtualSocketServer::GetNextIP(af); } + + private: + scoped_ptr ss_; }; void TestVirtualInternal(int family) { - TestVirtualSocketServer* int_vss = new TestVirtualSocketServer( - new PhysicalSocketServer()); - TestVirtualSocketServer* ext_vss = new TestVirtualSocketServer( - new PhysicalSocketServer()); + scoped_ptr int_vss(new TestVirtualSocketServer( + new PhysicalSocketServer())); + scoped_ptr ext_vss(new TestVirtualSocketServer( + new PhysicalSocketServer())); SocketAddress int_addr; SocketAddress ext_addrs[4]; @@ -288,8 +292,8 @@ void TestVirtualInternal(int family) { ext_addrs[2].SetIP(ext_addrs[0].ipaddr()); ext_addrs[3].SetIP(ext_addrs[1].ipaddr()); - TestBindings(int_vss, int_addr, ext_vss, ext_addrs); - TestFilters(int_vss, int_addr, ext_vss, ext_addrs); + TestBindings(int_vss.get(), int_addr, ext_vss.get(), ext_addrs); + TestFilters(int_vss.get(), int_addr, ext_vss.get(), ext_addrs); } TEST(NatTest, TestVirtualIPv4) { diff --git a/talk/base/network.cc b/talk/base/network.cc index b32bb09ef..9351b873f 100644 --- a/talk/base/network.cc +++ b/talk/base/network.cc @@ -498,6 +498,11 @@ void BasicNetworkManager::DumpNetworks(bool include_ignored) { << ((network->ignored()) ? ", Ignored" : ""); } } + // Release the network list created previously. + // Do this in a seperated for loop for better readability. + for (size_t i = 0; i < list.size(); ++i) { + delete list[i]; + } } Network::Network(const std::string& name, const std::string& desc, diff --git a/talk/base/network_unittest.cc b/talk/base/network_unittest.cc index 146b7853f..ee0f0aa09 100644 --- a/talk/base/network_unittest.cc +++ b/talk/base/network_unittest.cc @@ -128,6 +128,7 @@ TEST_F(NetworkTest, TestCreateNetworks) { close(fd); #endif } + delete (*it); } } @@ -480,6 +481,10 @@ TEST_F(NetworkTest, TestIPv6Toggle) { } } EXPECT_TRUE(ipv6_found); + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + delete (*it); + } #endif ipv6_found = false; manager.set_ipv6_enabled(false); @@ -492,6 +497,10 @@ TEST_F(NetworkTest, TestIPv6Toggle) { } } EXPECT_FALSE(ipv6_found); + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + delete (*it); + } } #if defined(POSIX) diff --git a/talk/base/physicalsocketserver_unittest.cc b/talk/base/physicalsocketserver_unittest.cc index b7f68481a..329cf5d5f 100644 --- a/talk/base/physicalsocketserver_unittest.cc +++ b/talk/base/physicalsocketserver_unittest.cc @@ -287,7 +287,8 @@ TEST_F(PosixSignalDeliveryTest, SignalOnDifferentThread) { // thread. Our implementation should safely handle it and dispatch // RecordSignal() on this thread. scoped_ptr thread(new Thread()); - thread->Start(new RaiseSigTermRunnable()); + scoped_ptr runnable(new RaiseSigTermRunnable()); + thread->Start(runnable.get()); EXPECT_TRUE(ss_->Wait(1500, true)); EXPECT_TRUE(ExpectSignal(SIGTERM)); EXPECT_EQ(Thread::Current(), signaled_thread_); diff --git a/talk/base/proxydetect.cc b/talk/base/proxydetect.cc index d3c998365..7292f3b9f 100644 --- a/talk/base/proxydetect.cc +++ b/talk/base/proxydetect.cc @@ -445,7 +445,7 @@ bool GetDefaultFirefoxProfile(Pathname* profile_path) { // Note: we are looking for the first entry with "Default=1", or the last // entry in the file path.SetFilename("profiles.ini"); - FileStream* fs = Filesystem::OpenFile(path, "r"); + scoped_ptr fs(Filesystem::OpenFile(path, "r")); if (!fs) { return false; } @@ -510,7 +510,7 @@ bool GetDefaultFirefoxProfile(Pathname* profile_path) { bool ReadFirefoxPrefs(const Pathname& filename, const char * prefix, StringMap* settings) { - FileStream* fs = Filesystem::OpenFile(filename, "r"); + scoped_ptr fs(Filesystem::OpenFile(filename, "r")); if (!fs) { LOG(LS_ERROR) << "Failed to open file: " << filename.pathname(); return false; diff --git a/talk/base/signalthread_unittest.cc b/talk/base/signalthread_unittest.cc index 4ad59613d..4458f52ef 100644 --- a/talk/base/signalthread_unittest.cc +++ b/talk/base/signalthread_unittest.cc @@ -127,6 +127,8 @@ class OwnerThread : public Thread, public sigslot::has_slots<> { signal_thread->Start(); Thread::Current()->socketserver()->Wait(100, false); signal_thread->Release(); + // Delete |signal_thread|. + signal_thread->Destroy(true); has_run_ = true; } diff --git a/talk/base/socket_unittest.cc b/talk/base/socket_unittest.cc index dd4b1e526..ad06c1e22 100644 --- a/talk/base/socket_unittest.cc +++ b/talk/base/socket_unittest.cc @@ -926,6 +926,8 @@ void SocketTest::UdpReadyToSend(const IPAddress& loopback) { new TestClient(AsyncUDPSocket::Create(ss_, empty))); int test_packet_size = 1200; talk_base::scoped_array test_packet(new char[test_packet_size]); + // Init the test packet just to avoid memcheck warning. + memset(test_packet.get(), 0, test_packet_size); // Set the send buffer size to the same size as the test packet to have a // better chance to get EWOULDBLOCK. int send_buffer_size = test_packet_size; diff --git a/talk/build/common.gypi b/talk/build/common.gypi index 28481ebea..7775e2461 100644 --- a/talk/build/common.gypi +++ b/talk/build/common.gypi @@ -102,6 +102,11 @@ 'use_nss%': 1, }, }], + ['libjingle_objc==1', { + 'defines': [ + 'CARBON_DEPRECATED=YES', + ], + }], ['os_posix==1', { 'defines': [ 'HASH_NAMESPACE=__gnu_cxx', diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java index bd17323e1..d8bc257be 100644 --- a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java @@ -42,6 +42,7 @@ import android.widget.Toast; import org.json.JSONException; import org.json.JSONObject; +import org.webrtc.DataChannel; import org.webrtc.IceCandidate; import org.webrtc.MediaConstraints; import org.webrtc.MediaStream; @@ -333,6 +334,16 @@ public class AppRTCDemoActivity extends Activity } }); } + + @Override public void onDataChannel(final DataChannel dc) { + runOnUiThread(new Runnable() { + public void run() { + throw new RuntimeException( + "AppRTC doesn't use data channels, but got: " + dc.label() + + " anyway!"); + } + }); + } } // Implementation detail: handle offer creation/signaling and answer setting, diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index 39ab84381..b1d71315e 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -84,6 +84,7 @@ 'peerconnection_java_files': [ 'app/webrtc/java/src/org/webrtc/AudioSource.java', 'app/webrtc/java/src/org/webrtc/AudioTrack.java', + 'app/webrtc/java/src/org/webrtc/DataChannel.java', 'app/webrtc/java/src/org/webrtc/IceCandidate.java', 'app/webrtc/java/src/org/webrtc/MediaConstraints.java', 'app/webrtc/java/src/org/webrtc/MediaSource.java', @@ -590,15 +591,7 @@ }], ['OS=="mac"', { 'conditions': [ - [ 'libjingle_objc != 1', { - 'sources': [ - 'base/macasyncsocket.cc', - 'base/macasyncsocket.h', - 'base/maccocoasocketserver.h', - 'base/maccocoasocketserver.mm', - 'base/macsocketserver.cc', - 'base/macsocketserver.h', - ], + ['libjingle_objc != 1', { 'link_settings' :{ 'xcode_settings': { 'OTHER_LDFLAGS': [ @@ -606,15 +599,17 @@ ], }, }, - }, { - 'defines': [ - 'CARBON_DEPRECATED=YES', - ], }], ], 'sources': [ + 'base/macasyncsocket.cc', + 'base/macasyncsocket.h', + 'base/maccocoasocketserver.h', + 'base/maccocoasocketserver.mm', 'base/macconversion.cc', 'base/macconversion.h', + 'base/macsocketserver.cc', + 'base/macsocketserver.h', 'base/macutils.cc', 'base/macutils.h', 'base/macwindowpicker.cc', @@ -967,11 +962,11 @@ 'libjingle_media', ], 'include_dirs': [ - '<(DEPTH)/third_party/gtest/include', + '<(DEPTH)/testing/gtest/include', ], 'direct_dependent_settings': { 'include_dirs': [ - '<(DEPTH)/third_party/gtest/include', + '<(DEPTH)/testing/gtest/include', ], }, 'defines': [ diff --git a/talk/libjingle_examples.gyp b/talk/libjingle_examples.gyp index 3a33740cc..192ba03ad 100755 --- a/talk/libjingle_examples.gyp +++ b/talk/libjingle_examples.gyp @@ -233,6 +233,7 @@ # (http://crbug.com/225101) 'action_name': 'build_apprtcdemo_apk', 'inputs' : [ + '<(PRODUCT_DIR)/libjingle_peerconnection.jar', '<(PRODUCT_DIR)/libjingle_peerconnection_so.so', 'examples/android/AndroidManifest.xml', 'examples/android/README', @@ -256,7 +257,7 @@ ], 'action': [ 'bash', '-ec', - 'rm -f <(_outputs) && ' + 'rm -fr <(_outputs) examples/android/{bin,libs} && ' 'mkdir -p examples/android/libs/<(android_app_abi) && ' 'cp <(PRODUCT_DIR)/libjingle_peerconnection.jar examples/android/libs/ &&' '<(android_strip) -o examples/android/libs/<(android_app_abi)/libjingle_peerconnection_so.so <(PRODUCT_DIR)/libjingle_peerconnection_so.so &&' diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp index 72184ec25..5a2f3a942 100755 --- a/talk/libjingle_tests.gyp +++ b/talk/libjingle_tests.gyp @@ -33,15 +33,15 @@ 'target_name': 'gunit', 'type': 'static_library', 'sources': [ - '<(DEPTH)/third_party/gtest/src/gtest-all.cc', + '<(DEPTH)/testing/gtest/src/gtest-all.cc', ], 'include_dirs': [ - '<(DEPTH)/third_party/gtest/include', - '<(DEPTH)/third_party/gtest', + '<(DEPTH)/testing/gtest/include', + '<(DEPTH)/testing/gtest', ], 'direct_dependent_settings': { 'include_dirs': [ - '<(DEPTH)/third_party/gtest/include', + '<(DEPTH)/testing/gtest/include', ], }, 'conditions': [ diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h index 5b7b6cb0c..886f40301 100644 --- a/talk/media/webrtc/fakewebrtcvideoengine.h +++ b/talk/media/webrtc/fakewebrtcvideoengine.h @@ -708,10 +708,6 @@ class FakeWebRtcVideoEngine WEBRTC_STUB(DeregisterDecoderObserver, (const int)); WEBRTC_STUB(SendKeyFrame, (const int)); WEBRTC_STUB(WaitForFirstKeyFrame, (const int, const bool)); -#ifdef USE_WEBRTC_DEV_BRANCH - WEBRTC_STUB(StartDebugRecording, (int, const char*)); - WEBRTC_STUB(StopDebugRecording, (int)); -#endif // webrtc::ViECapture WEBRTC_STUB(NumberOfCaptureDevices, ()); @@ -783,10 +779,12 @@ class FakeWebRtcVideoEngine // Not using WEBRTC_STUB due to bool return value virtual bool IsIPv6Enabled(int channel) { return true; } WEBRTC_STUB(SetMTU, (int, unsigned int)); +#ifndef USE_WEBRTC_DEV_BRANCH WEBRTC_STUB(SetPacketTimeoutNotification, (const int, bool, int)); WEBRTC_STUB(RegisterObserver, (const int, webrtc::ViENetworkObserver&)); WEBRTC_STUB(SetPeriodicDeadOrAliveStatus, (const int, const bool, const unsigned int)); +#endif // webrtc::ViERender WEBRTC_STUB(RegisterVideoRenderModule, (webrtc::VideoRender&)); diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h index 7202e1520..65139aa25 100644 --- a/talk/media/webrtc/fakewebrtcvoiceengine.h +++ b/talk/media/webrtc/fakewebrtcvoiceengine.h @@ -609,6 +609,7 @@ class FakeWebRtcVoiceEngine } WEBRTC_STUB(ReceivedRTCPPacket, (int channel, const void* data, unsigned int length)); +#ifndef USE_WEBRTC_DEV_BRANCH // Not using WEBRTC_STUB due to bool return value WEBRTC_STUB(SetPacketTimeoutNotification, (int channel, bool enable, int timeoutSeconds)); @@ -621,6 +622,7 @@ class FakeWebRtcVoiceEngine int& sampleTimeSeconds)); WEBRTC_STUB(SetPeriodicDeadOrAliveStatus, (int channel, bool enable, int sampleTimeSeconds)); +#endif // webrtc::VoERTP_RTCP WEBRTC_STUB(RegisterRTPObserver, (int channel, @@ -741,7 +743,11 @@ class FakeWebRtcVoiceEngine // webrtc::VoEVideoSync WEBRTC_STUB(GetPlayoutBufferSize, (int& bufferMs)); WEBRTC_STUB(GetPlayoutTimestamp, (int channel, unsigned int& timestamp)); +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_STUB(GetRtpRtcp, (int, webrtc::RtpRtcp**, webrtc::RtpReceiver**)); +#else WEBRTC_STUB(GetRtpRtcp, (int, webrtc::RtpRtcp*&)); +#endif WEBRTC_STUB(SetInitTimestamp, (int channel, unsigned int timestamp)); WEBRTC_STUB(SetInitSequenceNumber, (int channel, short sequenceNumber)); WEBRTC_STUB(SetMinimumPlayoutDelay, (int channel, int delayMs)); diff --git a/talk/session/media/channelmanager.cc b/talk/session/media/channelmanager.cc index 529ceea21..063a6c8fc 100644 --- a/talk/session/media/channelmanager.cc +++ b/talk/session/media/channelmanager.cc @@ -43,6 +43,7 @@ #include "talk/media/base/hybriddataengine.h" #include "talk/media/base/rtpdataengine.h" #include "talk/media/base/videocapturer.h" +#include "talk/media/devices/devicemanager.h" #ifdef HAVE_SCTP #include "talk/media/sctp/sctpdataengine.h" #endif @@ -595,6 +596,14 @@ bool ChannelManager::IsSameCapturer(const std::string& capturer_name, return capturer->GetId() == device.id; } +bool ChannelManager::GetVideoCaptureDevice(Device* device) { + std::string device_name; + if (!GetCaptureDevice(&device_name)) { + return false; + } + return device_manager_->GetVideoCaptureDevice(device_name, device); +} + bool ChannelManager::GetCaptureDevice(std::string* cam_name) { if (camera_device_.empty()) { // Initialize camera_device_ with default. diff --git a/talk/session/media/channelmanager.h b/talk/session/media/channelmanager.h index 0c5773791..6d53c63ea 100644 --- a/talk/session/media/channelmanager.h +++ b/talk/session/media/channelmanager.h @@ -144,7 +144,12 @@ class ChannelManager : public talk_base::MessageHandler, bool SetOutputVolume(int level); bool IsSameCapturer(const std::string& capturer_name, VideoCapturer* capturer); + // TODO(noahric): Nearly everything called "device" in this API is actually a + // device name, so this should really be GetCaptureDeviceName, and the + // next method should be GetCaptureDevice. bool GetCaptureDevice(std::string* cam_device); + // Gets the current capture Device. + bool GetVideoCaptureDevice(Device* device); // Create capturer based on what has been set in SetCaptureDevice(). VideoCapturer* CreateVideoCapturer(); bool SetCaptureDevice(const std::string& cam_device);