aboutsummaryrefslogtreecommitdiff
path: root/pc/sdp_offer_answer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'pc/sdp_offer_answer.cc')
-rw-r--r--pc/sdp_offer_answer.cc356
1 files changed, 259 insertions, 97 deletions
diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc
index c9ee82495b..2bfb61a15f 100644
--- a/pc/sdp_offer_answer.cc
+++ b/pc/sdp_offer_answer.cc
@@ -23,7 +23,6 @@
#include "api/array_view.h"
#include "api/crypto/crypto_options.h"
#include "api/dtls_transport_interface.h"
-#include "api/media_stream_proxy.h"
#include "api/rtp_parameters.h"
#include "api/rtp_receiver_interface.h"
#include "api/rtp_sender_interface.h"
@@ -41,6 +40,7 @@
#include "pc/data_channel_utils.h"
#include "pc/dtls_transport.h"
#include "pc/media_stream.h"
+#include "pc/media_stream_proxy.h"
#include "pc/peer_connection.h"
#include "pc/peer_connection_message_handler.h"
#include "pc/rtp_media_utils.h"
@@ -60,6 +60,7 @@
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
#include "rtc_base/trace_event.h"
+#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
using cricket::ContentInfo;
@@ -87,6 +88,9 @@ namespace {
typedef webrtc::PeerConnectionInterface::RTCOfferAnswerOptions
RTCOfferAnswerOptions;
+constexpr const char* kAlwaysAllowPayloadTypeDemuxingFieldTrialName =
+ "WebRTC-AlwaysAllowPayloadTypeDemuxing";
+
// Error messages
const char kInvalidSdp[] = "Invalid session description.";
const char kInvalidCandidates[] = "Description contains invalid candidates.";
@@ -164,6 +168,19 @@ void NoteKeyProtocolAndMedia(KeyExchangeProtocolType protocol_type,
}
}
+std::map<std::string, const cricket::ContentGroup*> GetBundleGroupsByMid(
+ const SessionDescription* desc) {
+ std::vector<const cricket::ContentGroup*> bundle_groups =
+ desc->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
+ std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid;
+ for (const cricket::ContentGroup* bundle_group : bundle_groups) {
+ for (const std::string& content_name : bundle_group->content_names()) {
+ bundle_groups_by_mid[content_name] = bundle_group;
+ }
+ }
+ return bundle_groups_by_mid;
+}
+
// Returns true if |new_desc| requests an ICE restart (i.e., new ufrag/pwd).
bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc,
const SessionDescriptionInterface* new_desc,
@@ -334,9 +351,10 @@ bool MediaSectionsHaveSameCount(const SessionDescription& desc1,
// needs a ufrag and pwd. Mismatches, such as replying with a DTLS fingerprint
// to SDES keys, will be caught in JsepTransport negotiation, and backstopped
// by Channel's |srtp_required| check.
-RTCError VerifyCrypto(const SessionDescription* desc, bool dtls_enabled) {
- const cricket::ContentGroup* bundle =
- desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
+RTCError VerifyCrypto(const SessionDescription* desc,
+ bool dtls_enabled,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
for (const cricket::ContentInfo& content_info : desc->contents()) {
if (content_info.rejected) {
continue;
@@ -346,8 +364,10 @@ RTCError VerifyCrypto(const SessionDescription* desc, bool dtls_enabled) {
: webrtc::kEnumCounterKeyProtocolSdes,
content_info.media_description()->type());
const std::string& mid = content_info.name;
- if (bundle && bundle->HasContentName(mid) &&
- mid != *(bundle->FirstContentName())) {
+ auto it = bundle_groups_by_mid.find(mid);
+ const cricket::ContentGroup* bundle =
+ it != bundle_groups_by_mid.end() ? it->second : nullptr;
+ if (bundle && mid != *(bundle->FirstContentName())) {
// This isn't the first media section in the BUNDLE group, so it's not
// required to have crypto attributes, since only the crypto attributes
// from the first section actually get used.
@@ -384,16 +404,19 @@ RTCError VerifyCrypto(const SessionDescription* desc, bool dtls_enabled) {
// Checks that each non-rejected content has ice-ufrag and ice-pwd set, unless
// it's in a BUNDLE group, in which case only the BUNDLE-tag section (first
// media section/description in the BUNDLE group) needs a ufrag and pwd.
-bool VerifyIceUfragPwdPresent(const SessionDescription* desc) {
- const cricket::ContentGroup* bundle =
- desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
+bool VerifyIceUfragPwdPresent(
+ const SessionDescription* desc,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
for (const cricket::ContentInfo& content_info : desc->contents()) {
if (content_info.rejected) {
continue;
}
const std::string& mid = content_info.name;
- if (bundle && bundle->HasContentName(mid) &&
- mid != *(bundle->FirstContentName())) {
+ auto it = bundle_groups_by_mid.find(mid);
+ const cricket::ContentGroup* bundle =
+ it != bundle_groups_by_mid.end() ? it->second : nullptr;
+ if (bundle && mid != *(bundle->FirstContentName())) {
// This isn't the first media section in the BUNDLE group, so it's not
// required to have ufrag/password, since only the ufrag/password from
// the first section actually get used.
@@ -525,13 +548,17 @@ RTCError UpdateSimulcastLayerStatusInSender(
}
bool SimulcastIsRejected(const ContentInfo* local_content,
- const MediaContentDescription& answer_media_desc) {
+ const MediaContentDescription& answer_media_desc,
+ bool enable_encrypted_rtp_header_extensions) {
bool simulcast_offered = local_content &&
local_content->media_description() &&
local_content->media_description()->HasSimulcast();
bool simulcast_answered = answer_media_desc.HasSimulcast();
bool rids_supported = RtpExtension::FindHeaderExtensionByUri(
- answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri);
+ answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri,
+ enable_encrypted_rtp_header_extensions
+ ? RtpExtension::Filter::kPreferEncryptedExtension
+ : RtpExtension::Filter::kDiscardEncryptedExtension);
return simulcast_offered && (!simulcast_answered || !rids_supported);
}
@@ -715,6 +742,17 @@ rtc::scoped_refptr<webrtc::DtlsTransport> LookupDtlsTransportByMid(
[controller, &mid] { return controller->LookupDtlsTransportByMid(mid); });
}
+bool ContentHasHeaderExtension(const cricket::ContentInfo& content_info,
+ absl::string_view header_extension_uri) {
+ for (const RtpExtension& rtp_header_extension :
+ content_info.media_description()->rtp_header_extensions()) {
+ if (rtp_header_extension.uri == header_extension_uri) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace
// Used by parameterless SetLocalDescription() to create an offer or answer.
@@ -1225,7 +1263,10 @@ void SdpOfferAnswerHandler::SetLocalDescription(
}
RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
- std::unique_ptr<SessionDescriptionInterface> desc) {
+ std::unique_ptr<SessionDescriptionInterface> desc,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyLocalDescription");
RTC_DCHECK_RUN_ON(signaling_thread());
RTC_DCHECK(desc);
@@ -1279,7 +1320,7 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
if (IsUnifiedPlan()) {
RTCError error = UpdateTransceiversAndDataChannels(
cricket::CS_LOCAL, *local_description(), old_local_description,
- remote_description());
+ remote_description(), bundle_groups_by_mid);
if (!error.ok()) {
return error;
}
@@ -1349,7 +1390,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
}
error = UpdateSessionState(type, cricket::CS_LOCAL,
- local_description()->description());
+ local_description()->description(),
+ bundle_groups_by_mid);
if (!error.ok()) {
return error;
}
@@ -1511,7 +1553,10 @@ void SdpOfferAnswerHandler::SetRemoteDescription(
}
RTCError SdpOfferAnswerHandler::ApplyRemoteDescription(
- std::unique_ptr<SessionDescriptionInterface> desc) {
+ std::unique_ptr<SessionDescriptionInterface> desc,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyRemoteDescription");
RTC_DCHECK_RUN_ON(signaling_thread());
RTC_DCHECK(desc);
@@ -1555,7 +1600,7 @@ RTCError SdpOfferAnswerHandler::ApplyRemoteDescription(
if (IsUnifiedPlan()) {
RTCError error = UpdateTransceiversAndDataChannels(
cricket::CS_REMOTE, *remote_description(), local_description(),
- old_remote_description);
+ old_remote_description, bundle_groups_by_mid);
if (!error.ok()) {
return error;
}
@@ -1577,7 +1622,8 @@ RTCError SdpOfferAnswerHandler::ApplyRemoteDescription(
// NOTE: Candidates allocation will be initiated only when
// SetLocalDescription is called.
error = UpdateSessionState(type, cricket::CS_REMOTE,
- remote_description()->description());
+ remote_description()->description(),
+ bundle_groups_by_mid);
if (!error.ok()) {
return error;
}
@@ -1870,7 +1916,10 @@ void SdpOfferAnswerHandler::DoSetLocalDescription(
return;
}
- RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL);
+ std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid =
+ GetBundleGroupsByMid(desc->description());
+ RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL,
+ bundle_groups_by_mid);
if (!error.ok()) {
std::string error_message = GetSetDescriptionErrorMessage(
cricket::CS_LOCAL, desc->GetType(), error);
@@ -1884,7 +1933,7 @@ void SdpOfferAnswerHandler::DoSetLocalDescription(
// which may destroy it before returning.
const SdpType type = desc->GetType();
- error = ApplyLocalDescription(std::move(desc));
+ error = ApplyLocalDescription(std::move(desc), bundle_groups_by_mid);
// |desc| may be destroyed at this point.
if (!error.ok()) {
@@ -1993,6 +2042,7 @@ void SdpOfferAnswerHandler::DoCreateOffer(
void SdpOfferAnswerHandler::CreateAnswer(
CreateSessionDescriptionObserver* observer,
const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateAnswer");
RTC_DCHECK_RUN_ON(signaling_thread());
// Chain this operation. If asynchronous operations are pending on the chain,
// this operation will be queued to be invoked, otherwise the contents of the
@@ -2130,7 +2180,10 @@ void SdpOfferAnswerHandler::DoSetRemoteDescription(
// points.
FillInMissingRemoteMids(desc->description());
- RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE);
+ std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid =
+ GetBundleGroupsByMid(desc->description());
+ RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE,
+ bundle_groups_by_mid);
if (!error.ok()) {
std::string error_message = GetSetDescriptionErrorMessage(
cricket::CS_REMOTE, desc->GetType(), error);
@@ -2144,7 +2197,7 @@ void SdpOfferAnswerHandler::DoSetRemoteDescription(
// ApplyRemoteDescription, which may destroy it before returning.
const SdpType type = desc->GetType();
- error = ApplyRemoteDescription(std::move(desc));
+ error = ApplyRemoteDescription(std::move(desc), bundle_groups_by_mid);
// |desc| may be destroyed at this point.
if (!error.ok()) {
@@ -2290,6 +2343,7 @@ AddIceCandidateResult SdpOfferAnswerHandler::AddIceCandidateInternal(
void SdpOfferAnswerHandler::AddIceCandidate(
std::unique_ptr<IceCandidateInterface> candidate,
std::function<void(RTCError)> callback) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
RTC_DCHECK_RUN_ON(signaling_thread());
// Chain this operation. If asynchronous operations are pending on the chain,
// this operation will be queued to be invoked, otherwise the contents of the
@@ -2421,6 +2475,7 @@ PeerConnectionInterface::SignalingState SdpOfferAnswerHandler::signaling_state()
void SdpOfferAnswerHandler::ChangeSignalingState(
PeerConnectionInterface::SignalingState signaling_state) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ChangeSignalingState");
RTC_DCHECK_RUN_ON(signaling_thread());
if (signaling_state_ == signaling_state) {
return;
@@ -2436,7 +2491,9 @@ void SdpOfferAnswerHandler::ChangeSignalingState(
RTCError SdpOfferAnswerHandler::UpdateSessionState(
SdpType type,
cricket::ContentSource source,
- const cricket::SessionDescription* description) {
+ const cricket::SessionDescription* description,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
RTC_DCHECK_RUN_ON(signaling_thread());
// If there's already a pending error then no state transition should happen.
@@ -2466,12 +2523,7 @@ RTCError SdpOfferAnswerHandler::UpdateSessionState(
// Update internal objects according to the session description's media
// descriptions.
- RTCError error = PushdownMediaDescription(type, source);
- if (!error.ok()) {
- return error;
- }
-
- return RTCError::OK();
+ return PushdownMediaDescription(type, source, bundle_groups_by_mid);
}
bool SdpOfferAnswerHandler::ShouldFireNegotiationNeededEvent(
@@ -2628,6 +2680,7 @@ void SdpOfferAnswerHandler::OnVideoTrackRemoved(VideoTrackInterface* track,
}
RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::Rollback");
auto state = signaling_state();
if (state != PeerConnectionInterface::kHaveLocalOffer &&
state != PeerConnectionInterface::kHaveRemoteOffer) {
@@ -2969,7 +3022,9 @@ void SdpOfferAnswerHandler::GenerateNegotiationNeededEvent() {
RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
const SessionDescriptionInterface* sdesc,
- cricket::ContentSource source) {
+ cricket::ContentSource source,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
if (session_error() != SessionError::kNone) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg());
}
@@ -2995,20 +3050,21 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
std::string crypto_error;
if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
pc_->dtls_enabled()) {
- RTCError crypto_error =
- VerifyCrypto(sdesc->description(), pc_->dtls_enabled());
+ RTCError crypto_error = VerifyCrypto(
+ sdesc->description(), pc_->dtls_enabled(), bundle_groups_by_mid);
if (!crypto_error.ok()) {
return crypto_error;
}
}
// Verify ice-ufrag and ice-pwd.
- if (!VerifyIceUfragPwdPresent(sdesc->description())) {
+ if (!VerifyIceUfragPwdPresent(sdesc->description(), bundle_groups_by_mid)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
kSdpWithoutIceUfragPwd);
}
- if (!pc_->ValidateBundleSettings(sdesc->description())) {
+ if (!pc_->ValidateBundleSettings(sdesc->description(),
+ bundle_groups_by_mid)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
kBundleWithoutRtcpMux);
}
@@ -3081,18 +3137,25 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels(
cricket::ContentSource source,
const SessionDescriptionInterface& new_session,
const SessionDescriptionInterface* old_local_description,
- const SessionDescriptionInterface* old_remote_description) {
+ const SessionDescriptionInterface* old_remote_description,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
+ TRACE_EVENT0("webrtc",
+ "SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels");
RTC_DCHECK_RUN_ON(signaling_thread());
RTC_DCHECK(IsUnifiedPlan());
- const cricket::ContentGroup* bundle_group = nullptr;
if (new_session.GetType() == SdpType::kOffer) {
- auto bundle_group_or_error =
- GetEarlyBundleGroup(*new_session.description());
- if (!bundle_group_or_error.ok()) {
- return bundle_group_or_error.MoveError();
+ // If the BUNDLE policy is max-bundle, then we know for sure that all
+ // transports will be bundled from the start. Return an error if max-bundle
+ // is specified but the session description does not have a BUNDLE group.
+ if (pc_->configuration()->bundle_policy ==
+ PeerConnectionInterface::kBundlePolicyMaxBundle &&
+ bundle_groups_by_mid.empty()) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "max-bundle configured but session description "
+ "has no BUNDLE group");
}
- bundle_group = bundle_group_or_error.MoveValue();
}
const ContentInfos& new_contents = new_session.description()->contents();
@@ -3100,6 +3163,9 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels(
const cricket::ContentInfo& new_content = new_contents[i];
cricket::MediaType media_type = new_content.media_description()->type();
mid_generator_.AddKnownId(new_content.name);
+ auto it = bundle_groups_by_mid.find(new_content.name);
+ const cricket::ContentGroup* bundle_group =
+ it != bundle_groups_by_mid.end() ? it->second : nullptr;
if (media_type == cricket::MEDIA_TYPE_AUDIO ||
media_type == cricket::MEDIA_TYPE_VIDEO) {
const cricket::ContentInfo* old_local_content = nullptr;
@@ -3162,6 +3228,7 @@ SdpOfferAnswerHandler::AssociateTransceiver(
const ContentInfo& content,
const ContentInfo* old_local_content,
const ContentInfo* old_remote_content) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AssociateTransceiver");
RTC_DCHECK(IsUnifiedPlan());
#if RTC_DCHECK_IS_ON
// If this is an offer then the m= section might be recycled. If the m=
@@ -3238,7 +3305,9 @@ SdpOfferAnswerHandler::AssociateTransceiver(
// Check if the offer indicated simulcast but the answer rejected it.
// This can happen when simulcast is not supported on the remote party.
- if (SimulcastIsRejected(old_local_content, *media_desc)) {
+ if (SimulcastIsRejected(old_local_content, *media_desc,
+ pc_->GetCryptoOptions()
+ .srtp.enable_encrypted_rtp_header_extensions)) {
RTC_HISTOGRAM_BOOLEAN(kSimulcastDisabled, true);
RTCError error =
DisableSimulcastInSender(transceiver->internal()->sender_internal());
@@ -3288,27 +3357,12 @@ SdpOfferAnswerHandler::AssociateTransceiver(
return std::move(transceiver);
}
-RTCErrorOr<const cricket::ContentGroup*>
-SdpOfferAnswerHandler::GetEarlyBundleGroup(
- const SessionDescription& desc) const {
- const cricket::ContentGroup* bundle_group = nullptr;
- if (pc_->configuration()->bundle_policy ==
- PeerConnectionInterface::kBundlePolicyMaxBundle) {
- bundle_group = desc.GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
- if (!bundle_group) {
- LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
- "max-bundle configured but session description "
- "has no BUNDLE group");
- }
- }
- return bundle_group;
-}
-
RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel(
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
transceiver,
const cricket::ContentInfo& content,
const cricket::ContentGroup* bundle_group) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateTransceiverChannel");
RTC_DCHECK(IsUnifiedPlan());
RTC_DCHECK(transceiver);
cricket::ChannelInterface* channel = transceiver->internal()->channel();
@@ -3993,6 +4047,7 @@ void SdpOfferAnswerHandler::RemoveSenders(cricket::MediaType media_type) {
void SdpOfferAnswerHandler::UpdateLocalSenders(
const std::vector<cricket::StreamParams>& streams,
cricket::MediaType media_type) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateLocalSenders");
RTC_DCHECK_RUN_ON(signaling_thread());
std::vector<RtpSenderInfo>* current_senders =
rtp_manager()->GetLocalSenderInfos(media_type);
@@ -4035,6 +4090,7 @@ void SdpOfferAnswerHandler::UpdateRemoteSendersList(
bool default_sender_needed,
cricket::MediaType media_type,
StreamCollection* new_streams) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateRemoteSendersList");
RTC_DCHECK_RUN_ON(signaling_thread());
RTC_DCHECK(!IsUnifiedPlan());
@@ -4134,10 +4190,11 @@ void SdpOfferAnswerHandler::UpdateRemoteSendersList(
}
void SdpOfferAnswerHandler::EnableSending() {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::EnableSending");
RTC_DCHECK_RUN_ON(signaling_thread());
for (const auto& transceiver : transceivers()->ListInternal()) {
cricket::ChannelInterface* channel = transceiver->channel();
- if (channel && !channel->enabled()) {
+ if (channel) {
channel->Enable(true);
}
}
@@ -4145,14 +4202,17 @@ void SdpOfferAnswerHandler::EnableSending() {
RTCError SdpOfferAnswerHandler::PushdownMediaDescription(
SdpType type,
- cricket::ContentSource source) {
+ cricket::ContentSource source,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownMediaDescription");
const SessionDescriptionInterface* sdesc =
(source == cricket::CS_LOCAL ? local_description()
: remote_description());
RTC_DCHECK_RUN_ON(signaling_thread());
RTC_DCHECK(sdesc);
- if (!UpdatePayloadTypeDemuxingState(source)) {
+ if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) {
// Note that this is never expected to fail, since RtpDemuxer doesn't return
// an error when changing payload type demux criteria, which is all this
// does.
@@ -4161,7 +4221,11 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription(
}
// Push down the new SDP media section for each audio/video transceiver.
- for (const auto& transceiver : transceivers()->ListInternal()) {
+ auto rtp_transceivers = transceivers()->ListInternal();
+ std::vector<
+ std::pair<cricket::ChannelInterface*, const MediaContentDescription*>>
+ channels;
+ for (const auto& transceiver : rtp_transceivers) {
const ContentInfo* content_info =
FindMediaSectionForTransceiver(transceiver, sdesc);
cricket::ChannelInterface* channel = transceiver->channel();
@@ -4173,12 +4237,35 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription(
if (!content_desc) {
continue;
}
- std::string error;
- bool success = (source == cricket::CS_LOCAL)
- ? channel->SetLocalContent(content_desc, type, &error)
- : channel->SetRemoteContent(content_desc, type, &error);
- if (!success) {
- LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error);
+
+ transceiver->OnNegotiationUpdate(type, content_desc);
+ channels.push_back(std::make_pair(channel, content_desc));
+ }
+
+ // This for-loop of invokes helps audio impairment during re-negotiations.
+ // One of the causes is that downstairs decoder creation is synchronous at the
+ // moment, and that a decoder is created for each codec listed in the SDP.
+ //
+ // TODO(bugs.webrtc.org/12840): consider merging the invokes again after
+ // these projects have shipped:
+ // - bugs.webrtc.org/12462
+ // - crbug.com/1157227
+ // - crbug.com/1187289
+ for (const auto& entry : channels) {
+ RTCError error =
+ pc_->worker_thread()->Invoke<RTCError>(RTC_FROM_HERE, [&]() {
+ std::string error;
+ bool success =
+ (source == cricket::CS_LOCAL)
+ ? entry.first->SetLocalContent(entry.second, type, &error)
+ : entry.first->SetRemoteContent(entry.second, type, &error);
+ if (!success) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error);
+ }
+ return RTCError::OK();
+ });
+ if (!error.ok()) {
+ return error;
}
}
@@ -4212,6 +4299,7 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription(
RTCError SdpOfferAnswerHandler::PushdownTransportDescription(
cricket::ContentSource source,
SdpType type) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownTransportDescription");
RTC_DCHECK_RUN_ON(signaling_thread());
if (source == cricket::CS_LOCAL) {
@@ -4228,6 +4316,7 @@ RTCError SdpOfferAnswerHandler::PushdownTransportDescription(
}
void SdpOfferAnswerHandler::RemoveStoppedTransceivers() {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveStoppedTransceivers");
RTC_DCHECK_RUN_ON(signaling_thread());
// 3.2.10.1: For each transceiver in the connection's set of transceivers
// run the following steps:
@@ -4447,6 +4536,7 @@ RTCErrorOr<const cricket::ContentInfo*> SdpOfferAnswerHandler::FindContentInfo(
}
RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateChannels");
// Creating the media channels. Transports should already have been created
// at this point.
RTC_DCHECK_RUN_ON(signaling_thread());
@@ -4487,6 +4577,7 @@ RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) {
// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver.
cricket::VoiceChannel* SdpOfferAnswerHandler::CreateVoiceChannel(
const std::string& mid) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateVoiceChannel");
RTC_DCHECK_RUN_ON(signaling_thread());
if (!channel_manager()->media_engine())
return nullptr;
@@ -4505,6 +4596,7 @@ cricket::VoiceChannel* SdpOfferAnswerHandler::CreateVoiceChannel(
// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver.
cricket::VideoChannel* SdpOfferAnswerHandler::CreateVideoChannel(
const std::string& mid) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateVideoChannel");
RTC_DCHECK_RUN_ON(signaling_thread());
if (!channel_manager()->media_engine())
return nullptr;
@@ -4542,6 +4634,7 @@ bool SdpOfferAnswerHandler::CreateDataChannel(const std::string& mid) {
void SdpOfferAnswerHandler::DestroyTransceiverChannel(
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
transceiver) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DestroyTransceiverChannel");
RTC_DCHECK(transceiver);
RTC_LOG_THREAD_BLOCK_COUNT();
@@ -4556,14 +4649,18 @@ void SdpOfferAnswerHandler::DestroyTransceiverChannel(
RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
if (channel) {
// TODO(tommi): VideoRtpReceiver::SetMediaChannel blocks and jumps to the
- // worker thread. When being set to nullptrpus, there are additional
+ // worker thread. When being set to nullptr, there are additional
// blocking calls to e.g. ClearRecordableEncodedFrameCallback which triggers
// another blocking call or Stop() for video channels.
+ // The channel object also needs to be de-initialized on the network thread
+ // so if ownership of the channel object lies with the transceiver, we could
+ // un-set the channel pointer and uninitialize/destruct the channel object
+ // at the same time, rather than in separate steps.
transceiver->internal()->SetChannel(nullptr);
- RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2);
// TODO(tommi): All channel objects end up getting deleted on the
- // worker thread. Can DestroyTransceiverChannel be purely posted to the
- // worker?
+ // worker thread (ideally should be on the network thread but the
+ // MediaChannel objects are tied to the worker. Can the teardown be done
+ // asynchronously across the threads rather than blocking?
DestroyChannelInterface(channel);
}
}
@@ -4586,6 +4683,7 @@ void SdpOfferAnswerHandler::DestroyDataChannelTransport() {
void SdpOfferAnswerHandler::DestroyChannelInterface(
cricket::ChannelInterface* channel) {
+ TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DestroyChannelInterface");
RTC_DCHECK_RUN_ON(signaling_thread());
RTC_DCHECK(channel_manager()->media_engine());
RTC_DCHECK(channel);
@@ -4737,7 +4835,11 @@ SdpOfferAnswerHandler::GetMediaDescriptionOptionsForRejectedData(
}
bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
- cricket::ContentSource source) {
+ cricket::ContentSource source,
+ const std::map<std::string, const cricket::ContentGroup*>&
+ bundle_groups_by_mid) {
+ TRACE_EVENT0("webrtc",
+ "SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState");
RTC_DCHECK_RUN_ON(signaling_thread());
// We may need to delete any created default streams and disable creation of
// new ones on the basis of payload type. This is needed to avoid SSRC
@@ -4750,19 +4852,27 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
const SessionDescriptionInterface* sdesc =
(source == cricket::CS_LOCAL ? local_description()
: remote_description());
- const cricket::ContentGroup* bundle_group =
- sdesc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
- std::set<int> audio_payload_types;
- std::set<int> video_payload_types;
- bool pt_demuxing_enabled_audio = true;
- bool pt_demuxing_enabled_video = true;
+ struct PayloadTypes {
+ std::set<int> audio_payload_types;
+ std::set<int> video_payload_types;
+ bool pt_demuxing_possible_audio = true;
+ bool pt_demuxing_possible_video = true;
+ };
+ std::map<const cricket::ContentGroup*, PayloadTypes> payload_types_by_bundle;
+ // If the MID is missing from *any* receiving m= section, this is set to true.
+ bool mid_header_extension_missing_audio = false;
+ bool mid_header_extension_missing_video = false;
for (auto& content_info : sdesc->description()->contents()) {
+ auto it = bundle_groups_by_mid.find(content_info.name);
+ const cricket::ContentGroup* bundle_group =
+ it != bundle_groups_by_mid.end() ? it->second : nullptr;
// If this m= section isn't bundled, it's safe to demux by payload type
// since other m= sections using the same payload type will also be using
// different transports.
- if (!bundle_group || !bundle_group->HasContentName(content_info.name)) {
+ if (!bundle_group) {
continue;
}
+ PayloadTypes* payload_types = &payload_types_by_bundle[bundle_group];
if (content_info.rejected ||
(source == cricket::ContentSource::CS_LOCAL &&
!RtpTransceiverDirectionHasRecv(
@@ -4775,28 +4885,36 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
}
switch (content_info.media_description()->type()) {
case cricket::MediaType::MEDIA_TYPE_AUDIO: {
+ if (!mid_header_extension_missing_audio) {
+ mid_header_extension_missing_audio =
+ !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
+ }
const cricket::AudioContentDescription* audio_desc =
content_info.media_description()->as_audio();
for (const cricket::AudioCodec& audio : audio_desc->codecs()) {
- if (audio_payload_types.count(audio.id)) {
+ if (payload_types->audio_payload_types.count(audio.id)) {
// Two m= sections are using the same payload type, thus demuxing
// by payload type is not possible.
- pt_demuxing_enabled_audio = false;
+ payload_types->pt_demuxing_possible_audio = false;
}
- audio_payload_types.insert(audio.id);
+ payload_types->audio_payload_types.insert(audio.id);
}
break;
}
case cricket::MediaType::MEDIA_TYPE_VIDEO: {
+ if (!mid_header_extension_missing_video) {
+ mid_header_extension_missing_video =
+ !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
+ }
const cricket::VideoContentDescription* video_desc =
content_info.media_description()->as_video();
for (const cricket::VideoCodec& video : video_desc->codecs()) {
- if (video_payload_types.count(video.id)) {
+ if (payload_types->video_payload_types.count(video.id)) {
// Two m= sections are using the same payload type, thus demuxing
// by payload type is not possible.
- pt_demuxing_enabled_video = false;
+ payload_types->pt_demuxing_possible_video = false;
}
- video_payload_types.insert(video.id);
+ payload_types->video_payload_types.insert(video.id);
}
break;
}
@@ -4828,25 +4946,69 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
if (channels_to_update.empty()) {
return true;
}
+
+ // In Unified Plan, payload type demuxing is useful for legacy endpoints that
+ // don't support the MID header extension, but it can also cause incorrrect
+ // forwarding of packets when going from one m= section to multiple m=
+ // sections in the same BUNDLE. This only happens if media arrives prior to
+ // negotiation, but this can cause missing video and unsignalled ssrc bugs
+ // severe enough to warrant disabling PT demuxing in such cases. Therefore, if
+ // a MID header extension is present on all m= sections for a given kind
+ // (audio/video) then we use that as an OK to disable payload type demuxing in
+ // BUNDLEs of that kind. However if PT demuxing was ever turned on (e.g. MID
+ // was ever removed on ANY m= section of that kind) then we continue to allow
+ // PT demuxing in order to prevent disabling it in follow-up O/A exchanges and
+ // allowing early media by PT.
+ bool bundled_pt_demux_allowed_audio = !IsUnifiedPlan() ||
+ mid_header_extension_missing_audio ||
+ pt_demuxing_has_been_used_audio_;
+ bool bundled_pt_demux_allowed_video = !IsUnifiedPlan() ||
+ mid_header_extension_missing_video ||
+ pt_demuxing_has_been_used_video_;
+ // Kill switch for the above change.
+ if (field_trial::IsEnabled(kAlwaysAllowPayloadTypeDemuxingFieldTrialName)) {
+ // TODO(https://crbug.com/webrtc/12814): If disabling PT-based demux does
+ // not trigger regressions, remove this kill switch.
+ bundled_pt_demux_allowed_audio = true;
+ bundled_pt_demux_allowed_video = true;
+ }
+
return pc_->worker_thread()->Invoke<bool>(
- RTC_FROM_HERE, [&channels_to_update, bundle_group,
- pt_demuxing_enabled_audio, pt_demuxing_enabled_video]() {
+ RTC_FROM_HERE,
+ [&channels_to_update, &bundle_groups_by_mid, &payload_types_by_bundle,
+ bundled_pt_demux_allowed_audio, bundled_pt_demux_allowed_video,
+ pt_demuxing_has_been_used_audio = &pt_demuxing_has_been_used_audio_,
+ pt_demuxing_has_been_used_video = &pt_demuxing_has_been_used_video_]() {
for (const auto& it : channels_to_update) {
RtpTransceiverDirection local_direction = it.first;
cricket::ChannelInterface* channel = it.second;
cricket::MediaType media_type = channel->media_type();
- bool in_bundle_group = (bundle_group && bundle_group->HasContentName(
- channel->content_name()));
+ auto bundle_it = bundle_groups_by_mid.find(channel->content_name());
+ const cricket::ContentGroup* bundle_group =
+ bundle_it != bundle_groups_by_mid.end() ? bundle_it->second
+ : nullptr;
if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
- if (!channel->SetPayloadTypeDemuxingEnabled(
- (!in_bundle_group || pt_demuxing_enabled_audio) &&
- RtpTransceiverDirectionHasRecv(local_direction))) {
+ bool pt_demux_enabled =
+ RtpTransceiverDirectionHasRecv(local_direction) &&
+ (!bundle_group || (bundled_pt_demux_allowed_audio &&
+ payload_types_by_bundle[bundle_group]
+ .pt_demuxing_possible_audio));
+ if (pt_demux_enabled) {
+ *pt_demuxing_has_been_used_audio = true;
+ }
+ if (!channel->SetPayloadTypeDemuxingEnabled(pt_demux_enabled)) {
return false;
}
} else if (media_type == cricket::MediaType::MEDIA_TYPE_VIDEO) {
- if (!channel->SetPayloadTypeDemuxingEnabled(
- (!in_bundle_group || pt_demuxing_enabled_video) &&
- RtpTransceiverDirectionHasRecv(local_direction))) {
+ bool pt_demux_enabled =
+ RtpTransceiverDirectionHasRecv(local_direction) &&
+ (!bundle_group || (bundled_pt_demux_allowed_video &&
+ payload_types_by_bundle[bundle_group]
+ .pt_demuxing_possible_video));
+ if (pt_demux_enabled) {
+ *pt_demuxing_has_been_used_video = true;
+ }
+ if (!channel->SetPayloadTypeDemuxingEnabled(pt_demux_enabled)) {
return false;
}
}