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}
diff --git a/talk/app/webrtc/java/jni/classreferenceholder.cc b/talk/app/webrtc/java/jni/classreferenceholder.cc
index 7ff48b9..f074921 100644
--- a/talk/app/webrtc/java/jni/classreferenceholder.cc
+++ b/talk/app/webrtc/java/jni/classreferenceholder.cc
@@ -93,6 +93,7 @@
LoadClass(jni, "org/webrtc/MediaStream");
LoadClass(jni, "org/webrtc/MediaStreamTrack$State");
LoadClass(jni, "org/webrtc/PeerConnection$BundlePolicy");
+ LoadClass(jni, "org/webrtc/PeerConnection$RtcpMuxPolicy");
LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState");
LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState");
LoadClass(jni, "org/webrtc/PeerConnection$IceTransportsType");
@@ -143,4 +144,3 @@
}
} // namespace webrtc_jni
-
diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc
index a6cba44..9e1ce74 100644
--- a/talk/app/webrtc/java/jni/peerconnection_jni.cc
+++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc
@@ -1216,6 +1216,22 @@
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
JavaTcpCandidatePolicyToNativeType(
JNIEnv* jni, jobject j_tcp_candidate_policy) {
@@ -1292,6 +1308,12 @@
jobject j_bundle_policy = GetObjectField(
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(
jni, j_rtc_config_class, "tcpCandidatePolicy",
"Lorg/webrtc/PeerConnection$TcpCandidatePolicy;");
@@ -1311,6 +1333,8 @@
rtc_config.type =
JavaIceTransportsTypeToNativeType(jni, j_ice_transports_type);
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 =
JavaTcpCandidatePolicyToNativeType(jni, j_tcp_candidate_policy);
JavaIceServersToJsepIceServers(jni, j_ice_servers, &rtc_config.servers);
diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java
index 80e7bfe..a229e17 100644
--- a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java
+++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java
@@ -117,7 +117,11 @@
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 {
ENABLED, DISABLED
};
@@ -127,12 +131,14 @@
public IceTransportsType iceTransportsType;
public List<IceServer> iceServers;
public BundlePolicy bundlePolicy;
+ public RtcpMuxPolicy rtcpMuxPolicy;
public TcpCandidatePolicy tcpCandidatePolicy;
public int audioJitterBufferMaxPackets;
public RTCConfiguration(List<IceServer> iceServers) {
iceTransportsType = IceTransportsType.ALL;
bundlePolicy = BundlePolicy.BALANCED;
+ rtcpMuxPolicy = RtcpMuxPolicy.NEGOTIATE;
tcpCandidatePolicy = TcpCandidatePolicy.ENABLED;
this.iceServers = iceServers;
audioJitterBufferMaxPackets = 50;
diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h
index e32676e..960e286 100644
--- a/talk/app/webrtc/peerconnectioninterface.h
+++ b/talk/app/webrtc/peerconnectioninterface.h
@@ -197,6 +197,12 @@
kBundlePolicyMaxCompat
};
+ // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-09#section-4.1.1
+ enum RtcpMuxPolicy {
+ kRtcpMuxPolicyNegotiate,
+ kRtcpMuxPolicyRequire,
+ };
+
enum TcpCandidatePolicy {
kTcpCandidatePolicyEnabled,
kTcpCandidatePolicyDisabled
@@ -210,12 +216,14 @@
// at the same time.
IceServers servers;
BundlePolicy bundle_policy;
+ RtcpMuxPolicy rtcp_mux_policy;
TcpCandidatePolicy tcp_candidate_policy;
int audio_jitter_buffer_max_packets;
RTCConfiguration()
: type(kAll),
bundle_policy(kBundlePolicyBalanced),
+ rtcp_mux_policy(kRtcpMuxPolicyNegotiate),
tcp_candidate_policy(kTcpCandidatePolicyEnabled),
audio_jitter_buffer_max_packets(50) {}
};
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index e2a9d60..e461365 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -523,6 +523,7 @@
DTLSIdentityServiceInterface* dtls_identity_service,
const PeerConnectionInterface::RTCConfiguration& rtc_configuration) {
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
// mandatory constraints can be fulfilled. Note that |constraints|
@@ -1600,6 +1601,18 @@
}
}
+ 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.
if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) {
const cricket::ContentGroup* bundle_group = desc->GetGroupByName(
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index aa1deb5..570d32f 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -413,6 +413,9 @@
// Declares the bundle policy for the WebRTCSession.
PeerConnectionInterface::BundlePolicy bundle_policy_;
+ // Declares the RTCP mux policy for the WebRTCSession.
+ PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy_;
+
DISALLOW_COPY_AND_ASSIGN(WebRtcSession);
};
} // namespace webrtc
diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc
index e4f39f8..2b6f207 100644
--- a/talk/app/webrtc/webrtcsession_unittest.cc
+++ b/talk/app/webrtc/webrtcsession_unittest.cc
@@ -156,8 +156,6 @@
"a=rtpmap:96 rtx/90000\r\n"
"a=fmtp:96 apt=0\r\n";
-static const int kAudioJitterBufferMaxPackets = 50;
-
// Add some extra |newlines| to the |message| after |line|.
static void InjectAfter(const std::string& line,
const std::string& newlines,
@@ -405,11 +403,6 @@
void Init() {
PeerConnectionInterface::RTCConfiguration configuration;
- configuration.type = PeerConnectionInterface::kAll;
- configuration.bundle_policy =
- PeerConnectionInterface::kBundlePolicyBalanced;
- configuration.audio_jitter_buffer_max_packets =
- kAudioJitterBufferMaxPackets;
Init(NULL, configuration);
}
@@ -417,20 +410,20 @@
PeerConnectionInterface::IceTransportsType ice_transport_type) {
PeerConnectionInterface::RTCConfiguration configuration;
configuration.type = ice_transport_type;
- configuration.bundle_policy =
- PeerConnectionInterface::kBundlePolicyBalanced;
- configuration.audio_jitter_buffer_max_packets =
- kAudioJitterBufferMaxPackets;
Init(NULL, configuration);
}
void InitWithBundlePolicy(
PeerConnectionInterface::BundlePolicy bundle_policy) {
PeerConnectionInterface::RTCConfiguration configuration;
- configuration.type = PeerConnectionInterface::kAll;
configuration.bundle_policy = bundle_policy;
- configuration.audio_jitter_buffer_max_packets =
- kAudioJitterBufferMaxPackets;
+ Init(NULL, configuration);
+ }
+
+ void InitWithRtcpMuxPolicy(
+ PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy) {
+ PeerConnectionInterface::RTCConfiguration configuration;
+ configuration.rtcp_mux_policy = rtcp_mux_policy;
Init(NULL, configuration);
}
@@ -438,11 +431,6 @@
FakeIdentityService* identity_service = new FakeIdentityService();
identity_service->set_should_fail(identity_request_should_fail);
PeerConnectionInterface::RTCConfiguration configuration;
- configuration.type = PeerConnectionInterface::kAll;
- configuration.bundle_policy =
- PeerConnectionInterface::kBundlePolicyBalanced;
- configuration.audio_jitter_buffer_max_packets =
- kAudioJitterBufferMaxPackets;
Init(identity_service, configuration);
}
@@ -2789,6 +2777,46 @@
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
// if BUNDLE is enabled but rtcp-mux is disabled in m-lines.
TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) {
diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc
index 0034f15..95f5bc1 100644
--- a/talk/session/media/channel.cc
+++ b/talk/session/media/channel.cc
@@ -1042,6 +1042,18 @@
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,
ContentSource src,
std::string* error_desc) {
diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h
index 441fe64..904777e 100644
--- a/talk/session/media/channel.h
+++ b/talk/session/media/channel.h
@@ -108,6 +108,11 @@
bool writable() const { return writable_; }
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,
ContentAction action,
std::string* error_desc);
@@ -350,6 +355,7 @@
ContentAction action,
ContentSource src,
std::string* error_desc);
+ void ActivateRtcpMux_w();
bool SetRtcpMux_w(bool enable,
ContentAction action,
ContentSource src,
diff --git a/talk/session/media/channel_unittest.cc b/talk/session/media/channel_unittest.cc
index a55f6e8..8128190 100644
--- a/talk/session/media/channel_unittest.cc
+++ b/talk/session/media/channel_unittest.cc
@@ -1140,6 +1140,89 @@
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.
void SendEarlyRtcpMuxToRtcp() {
CreateChannels(RTCP | RTCP_MUX, RTCP);
@@ -2085,6 +2168,22 @@
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) {
Base::SendEarlyRtcpMuxToRtcp();
}
@@ -2507,6 +2606,22 @@
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) {
Base::SendEarlyRtcpMuxToRtcp();
}
diff --git a/talk/session/media/rtcpmuxfilter.cc b/talk/session/media/rtcpmuxfilter.cc
index f951992..f21e0ee 100644
--- a/talk/session/media/rtcpmuxfilter.cc
+++ b/talk/session/media/rtcpmuxfilter.cc
@@ -40,7 +40,16 @@
state_ == ST_ACTIVE;
}
+void RtcpMuxFilter::SetActive() {
+ state_ = ST_ACTIVE;
+}
+
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)) {
LOG(LS_ERROR) << "Invalid state for change of RTCP mux offer";
return false;
@@ -53,6 +62,11 @@
bool RtcpMuxFilter::SetProvisionalAnswer(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)) {
LOG(LS_ERROR) << "Invalid state for RTCP mux provisional answer";
return false;
@@ -83,6 +97,11 @@
}
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)) {
LOG(LS_ERROR) << "Invalid state for RTCP mux answer";
return false;
@@ -100,19 +119,24 @@
return true;
}
-bool RtcpMuxFilter::DemuxRtcp(const char* data, int len) {
- // If we're muxing RTP/RTCP, we must inspect each packet delivered and
- // determine whether it is RTP or RTCP. We do so by checking the packet type,
- // and assuming RTP if type is 0-63 or 96-127. For additional details, see
- // 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) {
+// Check the RTP payload type. If 63 < payload type < 96, it's RTCP.
+// For additional details, see http://tools.ietf.org/html/rfc5761.
+bool IsRtcp(const char* data, int len) {
+ if (len < 2) {
return false;
}
+ char pt = data[1] & 0x7F;
+ return (63 < pt) && (pt < 96);
+}
- int type = (len >= 2) ? (static_cast<uint8>(data[1]) & 0x7F) : 0;
- return (type >= 64 && type < 96);
+bool RtcpMuxFilter::DemuxRtcp(const char* data, int len) {
+ // 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) {
diff --git a/talk/session/media/rtcpmuxfilter.h b/talk/session/media/rtcpmuxfilter.h
index 948b3c3..8888fd4 100644
--- a/talk/session/media/rtcpmuxfilter.h
+++ b/talk/session/media/rtcpmuxfilter.h
@@ -41,6 +41,9 @@
// Whether the filter is active, i.e. has RTCP mux been properly negotiated.
bool IsActive() const;
+ // Make the filter active, regardless of the current state.
+ void SetActive();
+
// Specifies whether the offer indicates the use of RTCP mux.
bool SetOffer(bool offer_enable, ContentSource src);
diff --git a/talk/session/media/rtcpmuxfilter_unittest.cc b/talk/session/media/rtcpmuxfilter_unittest.cc
index 1305c89..d4e6376 100644
--- a/talk/session/media/rtcpmuxfilter_unittest.cc
+++ b/talk/session/media/rtcpmuxfilter_unittest.cc
@@ -212,3 +212,44 @@
EXPECT_TRUE(filter.SetAnswer(false, cricket::CS_LOCAL));
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());
+}