Add RtcpMuxPolicy support to PeerConnection.
BUG=4611 R=juberti@google.com Review URL: https://webrtc-codereview.appspot.com/46169004 Cr-Commit-Position: refs/heads/master@{#9251}
This commit is contained in:
		| @@ -93,6 +93,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { | |||||||
|   LoadClass(jni, "org/webrtc/MediaStream"); |   LoadClass(jni, "org/webrtc/MediaStream"); | ||||||
|   LoadClass(jni, "org/webrtc/MediaStreamTrack$State"); |   LoadClass(jni, "org/webrtc/MediaStreamTrack$State"); | ||||||
|   LoadClass(jni, "org/webrtc/PeerConnection$BundlePolicy"); |   LoadClass(jni, "org/webrtc/PeerConnection$BundlePolicy"); | ||||||
|  |   LoadClass(jni, "org/webrtc/PeerConnection$RtcpMuxPolicy"); | ||||||
|   LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState"); |   LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState"); | ||||||
|   LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState"); |   LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState"); | ||||||
|   LoadClass(jni, "org/webrtc/PeerConnection$IceTransportsType"); |   LoadClass(jni, "org/webrtc/PeerConnection$IceTransportsType"); | ||||||
| @@ -143,4 +144,3 @@ jclass FindClass(JNIEnv* jni, const char* name) { | |||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace webrtc_jni | }  // namespace webrtc_jni | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1216,6 +1216,22 @@ JavaBundlePolicyToNativeType(JNIEnv* jni, jobject j_bundle_policy) { | |||||||
|   return PeerConnectionInterface::kBundlePolicyBalanced; |   return PeerConnectionInterface::kBundlePolicyBalanced; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static PeerConnectionInterface::RtcpMuxPolicy | ||||||
|  | JavaRtcpMuxPolicyToNativeType(JNIEnv* jni, jobject j_rtcp_mux_policy) { | ||||||
|  |   std::string enum_name = GetJavaEnumName( | ||||||
|  |       jni, "org/webrtc/PeerConnection$RtcpMuxPolicy", | ||||||
|  |       j_rtcp_mux_policy); | ||||||
|  |  | ||||||
|  |   if (enum_name == "NEGOTIATE") | ||||||
|  |     return PeerConnectionInterface::kRtcpMuxPolicyNegotiate; | ||||||
|  |  | ||||||
|  |   if (enum_name == "REQUIRE") | ||||||
|  |     return PeerConnectionInterface::kRtcpMuxPolicyRequire; | ||||||
|  |  | ||||||
|  |   CHECK(false) << "Unexpected RtcpMuxPolicy enum_name " << enum_name; | ||||||
|  |   return PeerConnectionInterface::kRtcpMuxPolicyNegotiate; | ||||||
|  | } | ||||||
|  |  | ||||||
| static PeerConnectionInterface::TcpCandidatePolicy | static PeerConnectionInterface::TcpCandidatePolicy | ||||||
| JavaTcpCandidatePolicyToNativeType( | JavaTcpCandidatePolicyToNativeType( | ||||||
|     JNIEnv* jni, jobject j_tcp_candidate_policy) { |     JNIEnv* jni, jobject j_tcp_candidate_policy) { | ||||||
| @@ -1292,6 +1308,12 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnection)( | |||||||
|   jobject j_bundle_policy = GetObjectField( |   jobject j_bundle_policy = GetObjectField( | ||||||
|       jni, j_rtc_config, j_bundle_policy_id); |       jni, j_rtc_config, j_bundle_policy_id); | ||||||
|  |  | ||||||
|  |   jfieldID j_rtcp_mux_policy_id = GetFieldID( | ||||||
|  |       jni, j_rtc_config_class, "rtcpMuxPolicy", | ||||||
|  |       "Lorg/webrtc/PeerConnection$RtcpMuxPolicy;"); | ||||||
|  |   jobject j_rtcp_mux_policy = GetObjectField( | ||||||
|  |       jni, j_rtc_config, j_rtcp_mux_policy_id); | ||||||
|  |  | ||||||
|   jfieldID j_tcp_candidate_policy_id = GetFieldID( |   jfieldID j_tcp_candidate_policy_id = GetFieldID( | ||||||
|       jni, j_rtc_config_class, "tcpCandidatePolicy", |       jni, j_rtc_config_class, "tcpCandidatePolicy", | ||||||
|       "Lorg/webrtc/PeerConnection$TcpCandidatePolicy;"); |       "Lorg/webrtc/PeerConnection$TcpCandidatePolicy;"); | ||||||
| @@ -1311,6 +1333,8 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnection)( | |||||||
|   rtc_config.type = |   rtc_config.type = | ||||||
|       JavaIceTransportsTypeToNativeType(jni, j_ice_transports_type); |       JavaIceTransportsTypeToNativeType(jni, j_ice_transports_type); | ||||||
|   rtc_config.bundle_policy = JavaBundlePolicyToNativeType(jni, j_bundle_policy); |   rtc_config.bundle_policy = JavaBundlePolicyToNativeType(jni, j_bundle_policy); | ||||||
|  |   rtc_config.rtcp_mux_policy = | ||||||
|  |       JavaRtcpMuxPolicyToNativeType(jni, j_rtcp_mux_policy); | ||||||
|   rtc_config.tcp_candidate_policy = |   rtc_config.tcp_candidate_policy = | ||||||
|       JavaTcpCandidatePolicyToNativeType(jni, j_tcp_candidate_policy); |       JavaTcpCandidatePolicyToNativeType(jni, j_tcp_candidate_policy); | ||||||
|   JavaIceServersToJsepIceServers(jni, j_ice_servers, &rtc_config.servers); |   JavaIceServersToJsepIceServers(jni, j_ice_servers, &rtc_config.servers); | ||||||
|   | |||||||
| @@ -117,7 +117,11 @@ public class PeerConnection { | |||||||
|     BALANCED, MAXBUNDLE, MAXCOMPAT |     BALANCED, MAXBUNDLE, MAXCOMPAT | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   /** Java version of PeerConnectionInterface.BundlePolicy */ |   /** Java version of PeerConnectionInterface.RtcpMuxPolicy */ | ||||||
|  |   public enum RtcpMuxPolicy { | ||||||
|  |     NEGOTIATE, REQUIRE | ||||||
|  |   }; | ||||||
|  |   /** Java version of PeerConnectionInterface.TcpCandidatePolicy */ | ||||||
|   public enum TcpCandidatePolicy { |   public enum TcpCandidatePolicy { | ||||||
|     ENABLED, DISABLED |     ENABLED, DISABLED | ||||||
|   }; |   }; | ||||||
| @@ -127,12 +131,14 @@ public class PeerConnection { | |||||||
|     public IceTransportsType iceTransportsType; |     public IceTransportsType iceTransportsType; | ||||||
|     public List<IceServer> iceServers; |     public List<IceServer> iceServers; | ||||||
|     public BundlePolicy bundlePolicy; |     public BundlePolicy bundlePolicy; | ||||||
|  |     public RtcpMuxPolicy rtcpMuxPolicy; | ||||||
|     public TcpCandidatePolicy tcpCandidatePolicy; |     public TcpCandidatePolicy tcpCandidatePolicy; | ||||||
|     public int audioJitterBufferMaxPackets; |     public int audioJitterBufferMaxPackets; | ||||||
|  |  | ||||||
|     public RTCConfiguration(List<IceServer> iceServers) { |     public RTCConfiguration(List<IceServer> iceServers) { | ||||||
|       iceTransportsType = IceTransportsType.ALL; |       iceTransportsType = IceTransportsType.ALL; | ||||||
|       bundlePolicy = BundlePolicy.BALANCED; |       bundlePolicy = BundlePolicy.BALANCED; | ||||||
|  |       rtcpMuxPolicy = RtcpMuxPolicy.NEGOTIATE; | ||||||
|       tcpCandidatePolicy = TcpCandidatePolicy.ENABLED; |       tcpCandidatePolicy = TcpCandidatePolicy.ENABLED; | ||||||
|       this.iceServers = iceServers; |       this.iceServers = iceServers; | ||||||
|       audioJitterBufferMaxPackets = 50; |       audioJitterBufferMaxPackets = 50; | ||||||
|   | |||||||
| @@ -197,6 +197,12 @@ class PeerConnectionInterface : public rtc::RefCountInterface { | |||||||
|     kBundlePolicyMaxCompat |     kBundlePolicyMaxCompat | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-09#section-4.1.1 | ||||||
|  |   enum RtcpMuxPolicy { | ||||||
|  |     kRtcpMuxPolicyNegotiate, | ||||||
|  |     kRtcpMuxPolicyRequire, | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   enum TcpCandidatePolicy { |   enum TcpCandidatePolicy { | ||||||
|     kTcpCandidatePolicyEnabled, |     kTcpCandidatePolicyEnabled, | ||||||
|     kTcpCandidatePolicyDisabled |     kTcpCandidatePolicyDisabled | ||||||
| @@ -210,12 +216,14 @@ class PeerConnectionInterface : public rtc::RefCountInterface { | |||||||
|     // at the same time. |     // at the same time. | ||||||
|     IceServers servers; |     IceServers servers; | ||||||
|     BundlePolicy bundle_policy; |     BundlePolicy bundle_policy; | ||||||
|  |     RtcpMuxPolicy rtcp_mux_policy; | ||||||
|     TcpCandidatePolicy tcp_candidate_policy; |     TcpCandidatePolicy tcp_candidate_policy; | ||||||
|     int audio_jitter_buffer_max_packets; |     int audio_jitter_buffer_max_packets; | ||||||
|  |  | ||||||
|     RTCConfiguration() |     RTCConfiguration() | ||||||
|         : type(kAll), |         : type(kAll), | ||||||
|           bundle_policy(kBundlePolicyBalanced), |           bundle_policy(kBundlePolicyBalanced), | ||||||
|  |           rtcp_mux_policy(kRtcpMuxPolicyNegotiate), | ||||||
|           tcp_candidate_policy(kTcpCandidatePolicyEnabled), |           tcp_candidate_policy(kTcpCandidatePolicyEnabled), | ||||||
|           audio_jitter_buffer_max_packets(50) {} |           audio_jitter_buffer_max_packets(50) {} | ||||||
|   }; |   }; | ||||||
|   | |||||||
| @@ -523,6 +523,7 @@ bool WebRtcSession::Initialize( | |||||||
|     DTLSIdentityServiceInterface* dtls_identity_service, |     DTLSIdentityServiceInterface* dtls_identity_service, | ||||||
|     const PeerConnectionInterface::RTCConfiguration& rtc_configuration) { |     const PeerConnectionInterface::RTCConfiguration& rtc_configuration) { | ||||||
|   bundle_policy_ = rtc_configuration.bundle_policy; |   bundle_policy_ = rtc_configuration.bundle_policy; | ||||||
|  |   rtcp_mux_policy_ = rtc_configuration.rtcp_mux_policy; | ||||||
|  |  | ||||||
|   // TODO(perkj): Take |constraints| into consideration. Return false if not all |   // TODO(perkj): Take |constraints| into consideration. Return false if not all | ||||||
|   // mandatory constraints can be fulfilled. Note that |constraints| |   // mandatory constraints can be fulfilled. Note that |constraints| | ||||||
| @@ -1600,6 +1601,18 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire) { | ||||||
|  |     if (voice_channel()) { | ||||||
|  |       voice_channel()->ActivateRtcpMux(); | ||||||
|  |     } | ||||||
|  |     if (video_channel()) { | ||||||
|  |       video_channel()->ActivateRtcpMux(); | ||||||
|  |     } | ||||||
|  |     if (data_channel()) { | ||||||
|  |       data_channel()->ActivateRtcpMux(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Enable bundle before when kMaxBundle policy is in effect. |   // Enable bundle before when kMaxBundle policy is in effect. | ||||||
|   if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) { |   if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) { | ||||||
|     const cricket::ContentGroup* bundle_group = desc->GetGroupByName( |     const cricket::ContentGroup* bundle_group = desc->GetGroupByName( | ||||||
|   | |||||||
| @@ -413,6 +413,9 @@ class WebRtcSession : public cricket::BaseSession, | |||||||
|   // Declares the bundle policy for the WebRTCSession. |   // Declares the bundle policy for the WebRTCSession. | ||||||
|   PeerConnectionInterface::BundlePolicy bundle_policy_; |   PeerConnectionInterface::BundlePolicy bundle_policy_; | ||||||
|  |  | ||||||
|  |   // Declares the RTCP mux policy for the WebRTCSession. | ||||||
|  |   PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy_; | ||||||
|  |  | ||||||
|   DISALLOW_COPY_AND_ASSIGN(WebRtcSession); |   DISALLOW_COPY_AND_ASSIGN(WebRtcSession); | ||||||
| }; | }; | ||||||
| }  // namespace webrtc | }  // namespace webrtc | ||||||
|   | |||||||
| @@ -156,8 +156,6 @@ static const char kSdpWithRtx[] = | |||||||
|     "a=rtpmap:96 rtx/90000\r\n" |     "a=rtpmap:96 rtx/90000\r\n" | ||||||
|     "a=fmtp:96 apt=0\r\n"; |     "a=fmtp:96 apt=0\r\n"; | ||||||
|  |  | ||||||
| static const int kAudioJitterBufferMaxPackets = 50; |  | ||||||
|  |  | ||||||
| // Add some extra |newlines| to the |message| after |line|. | // Add some extra |newlines| to the |message| after |line|. | ||||||
| static void InjectAfter(const std::string& line, | static void InjectAfter(const std::string& line, | ||||||
|                         const std::string& newlines, |                         const std::string& newlines, | ||||||
| @@ -405,11 +403,6 @@ class WebRtcSessionTest : public testing::Test { | |||||||
|  |  | ||||||
|   void Init() { |   void Init() { | ||||||
|     PeerConnectionInterface::RTCConfiguration configuration; |     PeerConnectionInterface::RTCConfiguration configuration; | ||||||
|     configuration.type = PeerConnectionInterface::kAll; |  | ||||||
|     configuration.bundle_policy = |  | ||||||
|         PeerConnectionInterface::kBundlePolicyBalanced; |  | ||||||
|     configuration.audio_jitter_buffer_max_packets = |  | ||||||
|         kAudioJitterBufferMaxPackets; |  | ||||||
|     Init(NULL, configuration); |     Init(NULL, configuration); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -417,20 +410,20 @@ class WebRtcSessionTest : public testing::Test { | |||||||
|       PeerConnectionInterface::IceTransportsType ice_transport_type) { |       PeerConnectionInterface::IceTransportsType ice_transport_type) { | ||||||
|     PeerConnectionInterface::RTCConfiguration configuration; |     PeerConnectionInterface::RTCConfiguration configuration; | ||||||
|     configuration.type = ice_transport_type; |     configuration.type = ice_transport_type; | ||||||
|     configuration.bundle_policy = |  | ||||||
|         PeerConnectionInterface::kBundlePolicyBalanced; |  | ||||||
|     configuration.audio_jitter_buffer_max_packets = |  | ||||||
|         kAudioJitterBufferMaxPackets; |  | ||||||
|     Init(NULL, configuration); |     Init(NULL, configuration); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void InitWithBundlePolicy( |   void InitWithBundlePolicy( | ||||||
|       PeerConnectionInterface::BundlePolicy bundle_policy) { |       PeerConnectionInterface::BundlePolicy bundle_policy) { | ||||||
|     PeerConnectionInterface::RTCConfiguration configuration; |     PeerConnectionInterface::RTCConfiguration configuration; | ||||||
|     configuration.type = PeerConnectionInterface::kAll; |  | ||||||
|     configuration.bundle_policy = bundle_policy; |     configuration.bundle_policy = bundle_policy; | ||||||
|     configuration.audio_jitter_buffer_max_packets = |     Init(NULL, configuration); | ||||||
|         kAudioJitterBufferMaxPackets; |   } | ||||||
|  |  | ||||||
|  |   void InitWithRtcpMuxPolicy( | ||||||
|  |       PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy) { | ||||||
|  |     PeerConnectionInterface::RTCConfiguration configuration; | ||||||
|  |     configuration.rtcp_mux_policy = rtcp_mux_policy; | ||||||
|     Init(NULL, configuration); |     Init(NULL, configuration); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -438,11 +431,6 @@ class WebRtcSessionTest : public testing::Test { | |||||||
|     FakeIdentityService* identity_service = new FakeIdentityService(); |     FakeIdentityService* identity_service = new FakeIdentityService(); | ||||||
|     identity_service->set_should_fail(identity_request_should_fail); |     identity_service->set_should_fail(identity_request_should_fail); | ||||||
|     PeerConnectionInterface::RTCConfiguration configuration; |     PeerConnectionInterface::RTCConfiguration configuration; | ||||||
|     configuration.type = PeerConnectionInterface::kAll; |  | ||||||
|     configuration.bundle_policy = |  | ||||||
|         PeerConnectionInterface::kBundlePolicyBalanced; |  | ||||||
|     configuration.audio_jitter_buffer_max_packets = |  | ||||||
|         kAudioJitterBufferMaxPackets; |  | ||||||
|     Init(identity_service, configuration); |     Init(identity_service, configuration); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -2789,6 +2777,46 @@ TEST_F(WebRtcSessionTest, TestMaxBundleWithSetRemoteDescriptionFirst) { | |||||||
|             session_->GetTransportProxy("video")->impl()); |             session_->GetTransportProxy("video")->impl()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | TEST_F(WebRtcSessionTest, TestRequireRtcpMux) { | ||||||
|  |   InitWithRtcpMuxPolicy(PeerConnectionInterface::kRtcpMuxPolicyRequire); | ||||||
|  |   mediastream_signaling_.SendAudioVideoStream1(); | ||||||
|  |  | ||||||
|  |   PeerConnectionInterface::RTCOfferAnswerOptions options; | ||||||
|  |   SessionDescriptionInterface* offer = CreateOffer(options); | ||||||
|  |   SetLocalDescriptionWithoutError(offer); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); | ||||||
|  |   EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); | ||||||
|  |  | ||||||
|  |   mediastream_signaling_.SendAudioVideoStream2(); | ||||||
|  |   SessionDescriptionInterface* answer = | ||||||
|  |       CreateRemoteAnswer(session_->local_description()); | ||||||
|  |   SetRemoteDescriptionWithoutError(answer); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); | ||||||
|  |   EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) { | ||||||
|  |   InitWithRtcpMuxPolicy(PeerConnectionInterface::kRtcpMuxPolicyNegotiate); | ||||||
|  |   mediastream_signaling_.SendAudioVideoStream1(); | ||||||
|  |  | ||||||
|  |   PeerConnectionInterface::RTCOfferAnswerOptions options; | ||||||
|  |   SessionDescriptionInterface* offer = CreateOffer(options); | ||||||
|  |   SetLocalDescriptionWithoutError(offer); | ||||||
|  |  | ||||||
|  |   EXPECT_TRUE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); | ||||||
|  |   EXPECT_TRUE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); | ||||||
|  |  | ||||||
|  |   mediastream_signaling_.SendAudioVideoStream2(); | ||||||
|  |   SessionDescriptionInterface* answer = | ||||||
|  |       CreateRemoteAnswer(session_->local_description()); | ||||||
|  |   SetRemoteDescriptionWithoutError(answer); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); | ||||||
|  |   EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); | ||||||
|  | } | ||||||
|  |  | ||||||
| // This test verifies that SetLocalDescription and SetRemoteDescription fails | // This test verifies that SetLocalDescription and SetRemoteDescription fails | ||||||
| // if BUNDLE is enabled but rtcp-mux is disabled in m-lines. | // if BUNDLE is enabled but rtcp-mux is disabled in m-lines. | ||||||
| TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) { | TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) { | ||||||
|   | |||||||
| @@ -1042,6 +1042,18 @@ bool BaseChannel::SetSrtp_w(const std::vector<CryptoParams>& cryptos, | |||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void BaseChannel::ActivateRtcpMux() { | ||||||
|  |   worker_thread_->Invoke<void>(Bind( | ||||||
|  |       &BaseChannel::ActivateRtcpMux_w, this)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BaseChannel::ActivateRtcpMux_w() { | ||||||
|  |   if (!rtcp_mux_filter_.IsActive()) { | ||||||
|  |     rtcp_mux_filter_.SetActive(); | ||||||
|  |     set_rtcp_transport_channel(NULL); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| bool BaseChannel::SetRtcpMux_w(bool enable, ContentAction action, | bool BaseChannel::SetRtcpMux_w(bool enable, ContentAction action, | ||||||
|                                ContentSource src, |                                ContentSource src, | ||||||
|                                std::string* error_desc) { |                                std::string* error_desc) { | ||||||
|   | |||||||
| @@ -108,6 +108,11 @@ class BaseChannel | |||||||
|   bool writable() const { return writable_; } |   bool writable() const { return writable_; } | ||||||
|   bool IsStreamMuted(uint32 ssrc); |   bool IsStreamMuted(uint32 ssrc); | ||||||
|  |  | ||||||
|  |   // Activate RTCP mux, regardless of the state so far.  Once | ||||||
|  |   // activated, it can not be deactivated, and if the remote | ||||||
|  |   // description doesn't support RTCP mux, setting the remote | ||||||
|  |   // description will fail. | ||||||
|  |   void ActivateRtcpMux(); | ||||||
|   bool PushdownLocalDescription(const SessionDescription* local_desc, |   bool PushdownLocalDescription(const SessionDescription* local_desc, | ||||||
|                                 ContentAction action, |                                 ContentAction action, | ||||||
|                                 std::string* error_desc); |                                 std::string* error_desc); | ||||||
| @@ -350,6 +355,7 @@ class BaseChannel | |||||||
|                  ContentAction action, |                  ContentAction action, | ||||||
|                  ContentSource src, |                  ContentSource src, | ||||||
|                  std::string* error_desc); |                  std::string* error_desc); | ||||||
|  |   void ActivateRtcpMux_w(); | ||||||
|   bool SetRtcpMux_w(bool enable, |   bool SetRtcpMux_w(bool enable, | ||||||
|                     ContentAction action, |                     ContentAction action, | ||||||
|                     ContentSource src, |                     ContentSource src, | ||||||
|   | |||||||
| @@ -1140,6 +1140,89 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { | |||||||
|     EXPECT_TRUE(CheckNoRtcp2()); |     EXPECT_TRUE(CheckNoRtcp2()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Check that RTP and RTCP are transmitted ok when both sides | ||||||
|  |   // support mux and one the offerer requires mux. | ||||||
|  |   void SendRequireRtcpMuxToRtcpMux() { | ||||||
|  |     CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX); | ||||||
|  |     channel1_->ActivateRtcpMux(); | ||||||
|  |     EXPECT_TRUE(SendInitiate()); | ||||||
|  |     EXPECT_EQ(1U, GetTransport1()->channels().size()); | ||||||
|  |     EXPECT_EQ(1U, GetTransport2()->channels().size()); | ||||||
|  |     EXPECT_TRUE(SendAccept()); | ||||||
|  |     EXPECT_TRUE(SendRtp1()); | ||||||
|  |     EXPECT_TRUE(SendRtp2()); | ||||||
|  |     EXPECT_TRUE(SendRtcp1()); | ||||||
|  |     EXPECT_TRUE(SendRtcp2()); | ||||||
|  |     EXPECT_TRUE(CheckRtp1()); | ||||||
|  |     EXPECT_TRUE(CheckRtp2()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtp1()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtp2()); | ||||||
|  |     EXPECT_TRUE(CheckRtcp1()); | ||||||
|  |     EXPECT_TRUE(CheckRtcp2()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtcp1()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtcp2()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Check that RTP and RTCP are transmitted ok when both sides | ||||||
|  |   // support mux and one the answerer requires rtcp mux. | ||||||
|  |   void SendRtcpMuxToRequireRtcpMux() { | ||||||
|  |     CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX); | ||||||
|  |     channel2_->ActivateRtcpMux(); | ||||||
|  |     EXPECT_TRUE(SendInitiate()); | ||||||
|  |     EXPECT_EQ(2U, GetTransport1()->channels().size()); | ||||||
|  |     EXPECT_EQ(1U, GetTransport2()->channels().size()); | ||||||
|  |     EXPECT_TRUE(SendAccept()); | ||||||
|  |     EXPECT_EQ(1U, GetTransport1()->channels().size()); | ||||||
|  |     EXPECT_TRUE(SendRtp1()); | ||||||
|  |     EXPECT_TRUE(SendRtp2()); | ||||||
|  |     EXPECT_TRUE(SendRtcp1()); | ||||||
|  |     EXPECT_TRUE(SendRtcp2()); | ||||||
|  |     EXPECT_TRUE(CheckRtp1()); | ||||||
|  |     EXPECT_TRUE(CheckRtp2()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtp1()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtp2()); | ||||||
|  |     EXPECT_TRUE(CheckRtcp1()); | ||||||
|  |     EXPECT_TRUE(CheckRtcp2()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtcp1()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtcp2()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Check that RTP and RTCP are transmitted ok when both sides | ||||||
|  |   // require mux. | ||||||
|  |   void SendRequireRtcpMuxToRequireRtcpMux() { | ||||||
|  |     CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX); | ||||||
|  |     channel1_->ActivateRtcpMux(); | ||||||
|  |     channel2_->ActivateRtcpMux(); | ||||||
|  |     EXPECT_TRUE(SendInitiate()); | ||||||
|  |     EXPECT_EQ(1U, GetTransport1()->channels().size()); | ||||||
|  |     EXPECT_EQ(1U, GetTransport2()->channels().size()); | ||||||
|  |     EXPECT_TRUE(SendAccept()); | ||||||
|  |     EXPECT_EQ(1U, GetTransport1()->channels().size()); | ||||||
|  |     EXPECT_TRUE(SendRtp1()); | ||||||
|  |     EXPECT_TRUE(SendRtp2()); | ||||||
|  |     EXPECT_TRUE(SendRtcp1()); | ||||||
|  |     EXPECT_TRUE(SendRtcp2()); | ||||||
|  |     EXPECT_TRUE(CheckRtp1()); | ||||||
|  |     EXPECT_TRUE(CheckRtp2()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtp1()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtp2()); | ||||||
|  |     EXPECT_TRUE(CheckRtcp1()); | ||||||
|  |     EXPECT_TRUE(CheckRtcp2()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtcp1()); | ||||||
|  |     EXPECT_TRUE(CheckNoRtcp2()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Check that SendAccept fails if the answerer doesn't support mux | ||||||
|  |   // and the offerer requires it. | ||||||
|  |   void SendRequireRtcpMuxToNoRtcpMux() { | ||||||
|  |     CreateChannels(RTCP | RTCP_MUX, RTCP); | ||||||
|  |     channel1_->ActivateRtcpMux(); | ||||||
|  |     EXPECT_TRUE(SendInitiate()); | ||||||
|  |     EXPECT_EQ(1U, GetTransport1()->channels().size()); | ||||||
|  |     EXPECT_EQ(2U, GetTransport2()->channels().size()); | ||||||
|  |     EXPECT_FALSE(SendAccept()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Check that RTCP data sent by the initiator before the accept is not muxed. |   // Check that RTCP data sent by the initiator before the accept is not muxed. | ||||||
|   void SendEarlyRtcpMuxToRtcp() { |   void SendEarlyRtcpMuxToRtcp() { | ||||||
|     CreateChannels(RTCP | RTCP_MUX, RTCP); |     CreateChannels(RTCP | RTCP_MUX, RTCP); | ||||||
| @@ -2085,6 +2168,22 @@ TEST_F(VoiceChannelTest, SendRtcpMuxToRtcpMux) { | |||||||
|   Base::SendRtcpMuxToRtcpMux(); |   Base::SendRtcpMuxToRtcpMux(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | TEST_F(VoiceChannelTest, SendRequireRtcpMuxToRtcpMux) { | ||||||
|  |   Base::SendRequireRtcpMuxToRtcpMux(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(VoiceChannelTest, SendRtcpMuxToRequireRtcpMux) { | ||||||
|  |   Base::SendRtcpMuxToRequireRtcpMux(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(VoiceChannelTest, SendRequireRtcpMuxToRequireRtcpMux) { | ||||||
|  |   Base::SendRequireRtcpMuxToRequireRtcpMux(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(VoiceChannelTest, SendRequireRtcpMuxToNoRtcpMux) { | ||||||
|  |   Base::SendRequireRtcpMuxToNoRtcpMux(); | ||||||
|  | } | ||||||
|  |  | ||||||
| TEST_F(VoiceChannelTest, SendEarlyRtcpMuxToRtcp) { | TEST_F(VoiceChannelTest, SendEarlyRtcpMuxToRtcp) { | ||||||
|   Base::SendEarlyRtcpMuxToRtcp(); |   Base::SendEarlyRtcpMuxToRtcp(); | ||||||
| } | } | ||||||
| @@ -2507,6 +2606,22 @@ TEST_F(VideoChannelTest, SendRtcpMuxToRtcpMux) { | |||||||
|   Base::SendRtcpMuxToRtcpMux(); |   Base::SendRtcpMuxToRtcpMux(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | TEST_F(VideoChannelTest, SendRequireRtcpMuxToRtcpMux) { | ||||||
|  |   Base::SendRequireRtcpMuxToRtcpMux(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(VideoChannelTest, SendRtcpMuxToRequireRtcpMux) { | ||||||
|  |   Base::SendRtcpMuxToRequireRtcpMux(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(VideoChannelTest, SendRequireRtcpMuxToRequireRtcpMux) { | ||||||
|  |   Base::SendRequireRtcpMuxToRequireRtcpMux(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(VideoChannelTest, SendRequireRtcpMuxToNoRtcpMux) { | ||||||
|  |   Base::SendRequireRtcpMuxToNoRtcpMux(); | ||||||
|  | } | ||||||
|  |  | ||||||
| TEST_F(VideoChannelTest, SendEarlyRtcpMuxToRtcp) { | TEST_F(VideoChannelTest, SendEarlyRtcpMuxToRtcp) { | ||||||
|   Base::SendEarlyRtcpMuxToRtcp(); |   Base::SendEarlyRtcpMuxToRtcp(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -40,7 +40,16 @@ bool RtcpMuxFilter::IsActive() const { | |||||||
|          state_ == ST_ACTIVE; |          state_ == ST_ACTIVE; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void RtcpMuxFilter::SetActive() { | ||||||
|  |   state_ = ST_ACTIVE; | ||||||
|  | } | ||||||
|  |  | ||||||
| bool RtcpMuxFilter::SetOffer(bool offer_enable, ContentSource src) { | bool RtcpMuxFilter::SetOffer(bool offer_enable, ContentSource src) { | ||||||
|  |   if (state_ == ST_ACTIVE) { | ||||||
|  |     // Fail if we try to deactivate and no-op if we try and activate. | ||||||
|  |     return offer_enable; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (!ExpectOffer(offer_enable, src)) { |   if (!ExpectOffer(offer_enable, src)) { | ||||||
|     LOG(LS_ERROR) << "Invalid state for change of RTCP mux offer"; |     LOG(LS_ERROR) << "Invalid state for change of RTCP mux offer"; | ||||||
|     return false; |     return false; | ||||||
| @@ -53,6 +62,11 @@ bool RtcpMuxFilter::SetOffer(bool offer_enable, ContentSource src) { | |||||||
|  |  | ||||||
| bool RtcpMuxFilter::SetProvisionalAnswer(bool answer_enable, | bool RtcpMuxFilter::SetProvisionalAnswer(bool answer_enable, | ||||||
|                                          ContentSource src) { |                                          ContentSource src) { | ||||||
|  |   if (state_ == ST_ACTIVE) { | ||||||
|  |     // Fail if we try to deactivate and no-op if we try and activate. | ||||||
|  |     return answer_enable; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (!ExpectAnswer(src)) { |   if (!ExpectAnswer(src)) { | ||||||
|     LOG(LS_ERROR) << "Invalid state for RTCP mux provisional answer"; |     LOG(LS_ERROR) << "Invalid state for RTCP mux provisional answer"; | ||||||
|     return false; |     return false; | ||||||
| @@ -83,6 +97,11 @@ bool RtcpMuxFilter::SetProvisionalAnswer(bool answer_enable, | |||||||
| } | } | ||||||
|  |  | ||||||
| bool RtcpMuxFilter::SetAnswer(bool answer_enable, ContentSource src) { | bool RtcpMuxFilter::SetAnswer(bool answer_enable, ContentSource src) { | ||||||
|  |   if (state_ == ST_ACTIVE) { | ||||||
|  |     // Fail if we try to deactivate and no-op if we try and activate. | ||||||
|  |     return answer_enable; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (!ExpectAnswer(src)) { |   if (!ExpectAnswer(src)) { | ||||||
|     LOG(LS_ERROR) << "Invalid state for RTCP mux answer"; |     LOG(LS_ERROR) << "Invalid state for RTCP mux answer"; | ||||||
|     return false; |     return false; | ||||||
| @@ -100,19 +119,24 @@ bool RtcpMuxFilter::SetAnswer(bool answer_enable, ContentSource src) { | |||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool RtcpMuxFilter::DemuxRtcp(const char* data, int len) { | // Check the RTP payload type.  If 63 < payload type < 96, it's RTCP. | ||||||
|   // If we're muxing RTP/RTCP, we must inspect each packet delivered and | // For additional details, see http://tools.ietf.org/html/rfc5761. | ||||||
|   // determine whether it is RTP or RTCP. We do so by checking the packet type, | bool IsRtcp(const char* data, int len) { | ||||||
|   // and assuming RTP if type is 0-63 or 96-127. For additional details, see |   if (len < 2) { | ||||||
|   // http://tools.ietf.org/html/rfc5761. |  | ||||||
|   // Note that if we offer RTCP mux, we may receive muxed RTCP before we |  | ||||||
|   // receive the answer, so we operate in that state too. |  | ||||||
|   if (!offer_enable_ || state_ < ST_SENTOFFER) { |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |   char pt = data[1] & 0x7F; | ||||||
|  |   return (63 < pt) && (pt < 96); | ||||||
|  | } | ||||||
|  |  | ||||||
|   int type = (len >= 2) ? (static_cast<uint8>(data[1]) & 0x7F) : 0; | bool RtcpMuxFilter::DemuxRtcp(const char* data, int len) { | ||||||
|   return (type >= 64 && type < 96); |   // If we're muxing RTP/RTCP, we must inspect each packet delivered | ||||||
|  |   // and determine whether it is RTP or RTCP. We do so by looking at | ||||||
|  |   // the RTP payload type (see IsRtcp).  Note that if we offer RTCP | ||||||
|  |   // mux, we may receive muxed RTCP before we receive the answer, so | ||||||
|  |   // we operate in that state too. | ||||||
|  |   bool offered_mux = ((state_ == ST_SENTOFFER) && offer_enable_); | ||||||
|  |   return (IsActive() || offered_mux) && IsRtcp(data, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool RtcpMuxFilter::ExpectOffer(bool offer_enable, ContentSource source) { | bool RtcpMuxFilter::ExpectOffer(bool offer_enable, ContentSource source) { | ||||||
|   | |||||||
| @@ -41,6 +41,9 @@ class RtcpMuxFilter { | |||||||
|   // Whether the filter is active, i.e. has RTCP mux been properly negotiated. |   // Whether the filter is active, i.e. has RTCP mux been properly negotiated. | ||||||
|   bool IsActive() const; |   bool IsActive() const; | ||||||
|  |  | ||||||
|  |   // Make the filter active, regardless of the current state. | ||||||
|  |   void SetActive(); | ||||||
|  |  | ||||||
|   // Specifies whether the offer indicates the use of RTCP mux. |   // Specifies whether the offer indicates the use of RTCP mux. | ||||||
|   bool SetOffer(bool offer_enable, ContentSource src); |   bool SetOffer(bool offer_enable, ContentSource src); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -212,3 +212,44 @@ TEST(RtcpMuxFilterTest, KeepFilterDisabledDuringUpdate) { | |||||||
|   EXPECT_TRUE(filter.SetAnswer(false, cricket::CS_LOCAL)); |   EXPECT_TRUE(filter.SetAnswer(false, cricket::CS_LOCAL)); | ||||||
|   EXPECT_FALSE(filter.IsActive()); |   EXPECT_FALSE(filter.IsActive()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Test that we can SetActive and then can't deactivate. | ||||||
|  | TEST(RtcpMuxFilterTest, SetActiveCantDeactivate) { | ||||||
|  |   cricket::RtcpMuxFilter filter; | ||||||
|  |   const char data[] = { 0, 73, 0, 0 }; | ||||||
|  |   const int len = 4; | ||||||
|  |  | ||||||
|  |   filter.SetActive(); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |   EXPECT_TRUE(filter.DemuxRtcp(data, len)); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(filter.SetOffer(false, cricket::CS_LOCAL)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |   EXPECT_TRUE(filter.SetOffer(true, cricket::CS_LOCAL)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(filter.SetProvisionalAnswer(false, cricket::CS_REMOTE)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |   EXPECT_TRUE(filter.SetProvisionalAnswer(true, cricket::CS_REMOTE)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(filter.SetAnswer(false, cricket::CS_REMOTE)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |   EXPECT_TRUE(filter.SetAnswer(true, cricket::CS_REMOTE)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(filter.SetOffer(false, cricket::CS_REMOTE)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |   EXPECT_TRUE(filter.SetOffer(true, cricket::CS_REMOTE)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(filter.SetProvisionalAnswer(false, cricket::CS_LOCAL)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |   EXPECT_TRUE(filter.SetProvisionalAnswer(true, cricket::CS_LOCAL)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |  | ||||||
|  |   EXPECT_FALSE(filter.SetAnswer(false, cricket::CS_LOCAL)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  |   EXPECT_TRUE(filter.SetAnswer(true, cricket::CS_LOCAL)); | ||||||
|  |   EXPECT_TRUE(filter.IsActive()); | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Peter Thatcher
					Peter Thatcher