Move more functions from PeerConnection to SdpOfferAnswer
These are functions that are called only from SdpOfferAnswer, or that logically belong in the SdpOfferAnswer class. Bug: webrtc:11995 Change-Id: I92136ee84e20e50957814c21b041ca152a2acca4 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/186268 Commit-Queue: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Björn Terelius <terelius@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32271}
This commit is contained in:
committed by
Commit Bot
parent
a0ad0bbf8f
commit
c06e374a55
@ -149,30 +149,6 @@ bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add options to |[audio/video]_media_description_options| from |senders|.
|
||||
void AddPlanBRtpSenderOptions(
|
||||
const std::vector<rtc::scoped_refptr<
|
||||
RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options,
|
||||
cricket::MediaDescriptionOptions* video_media_description_options,
|
||||
int num_sim_layers) {
|
||||
for (const auto& sender : senders) {
|
||||
if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
|
||||
if (audio_media_description_options) {
|
||||
audio_media_description_options->AddAudioSender(
|
||||
sender->id(), sender->internal()->stream_ids());
|
||||
}
|
||||
} else {
|
||||
RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO);
|
||||
if (video_media_description_options) {
|
||||
video_media_description_options->AddVideoSender(
|
||||
sender->id(), sender->internal()->stream_ids(), {},
|
||||
SimulcastLayerList(), num_sim_layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add options to |session_options| from |rtp_data_channels|.
|
||||
void AddRtpDataChannelOptions(
|
||||
const std::map<std::string, rtc::scoped_refptr<RtpDataChannel>>&
|
||||
@ -445,17 +421,6 @@ std::string GenerateRtcpCname() {
|
||||
return cname;
|
||||
}
|
||||
|
||||
// From |rtc_options|, fill parts of |session_options| shared by all generated
|
||||
// m= sectionss (in other words, nothing that involves a map/array).
|
||||
void ExtractSharedMediaSessionOptions(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
session_options->vad_enabled = rtc_options.voice_activity_detection;
|
||||
session_options->bundle_enabled = rtc_options.use_rtp_mux;
|
||||
session_options->raw_packetization_for_video =
|
||||
rtc_options.raw_packetization_for_video;
|
||||
}
|
||||
|
||||
PeerConnection::PeerConnection(PeerConnectionFactory* factory,
|
||||
std::unique_ptr<RtcEventLog> event_log,
|
||||
std::unique_ptr<Call> call)
|
||||
@ -1348,6 +1313,7 @@ std::vector<rtc::scoped_refptr<RtpSenderInterface>> PeerConnection::GetSenders()
|
||||
|
||||
std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
|
||||
PeerConnection::GetSendersInternal() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
|
||||
all_senders;
|
||||
for (const auto& transceiver : transceivers_.List()) {
|
||||
@ -1698,9 +1664,11 @@ void PeerConnection::RemoveStoppedTransceivers() {
|
||||
continue;
|
||||
}
|
||||
const ContentInfo* local_content =
|
||||
FindMediaSectionForTransceiver(transceiver, local_description());
|
||||
sdp_handler_.FindMediaSectionForTransceiver(transceiver,
|
||||
local_description());
|
||||
const ContentInfo* remote_content =
|
||||
FindMediaSectionForTransceiver(transceiver, remote_description());
|
||||
sdp_handler_.FindMediaSectionForTransceiver(transceiver,
|
||||
remote_description());
|
||||
if ((local_content && local_content->rejected) ||
|
||||
(remote_content && remote_content->rejected)) {
|
||||
RTC_LOG(LS_INFO) << "Dissociating transceiver"
|
||||
@ -1721,64 +1689,6 @@ void PeerConnection::RemoveStoppedTransceivers() {
|
||||
}
|
||||
}
|
||||
|
||||
// The SDP parser used to populate these values by default for the 'content
|
||||
// name' if an a=mid line was absent.
|
||||
static absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) {
|
||||
switch (media_type) {
|
||||
case cricket::MEDIA_TYPE_AUDIO:
|
||||
return cricket::CN_AUDIO;
|
||||
case cricket::MEDIA_TYPE_VIDEO:
|
||||
return cricket::CN_VIDEO;
|
||||
case cricket::MEDIA_TYPE_DATA:
|
||||
return cricket::CN_DATA;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
|
||||
void PeerConnection::FillInMissingRemoteMids(
|
||||
cricket::SessionDescription* new_remote_description) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
RTC_DCHECK(new_remote_description);
|
||||
const cricket::ContentInfos no_infos;
|
||||
const cricket::ContentInfos& local_contents =
|
||||
(local_description() ? local_description()->description()->contents()
|
||||
: no_infos);
|
||||
const cricket::ContentInfos& remote_contents =
|
||||
(remote_description() ? remote_description()->description()->contents()
|
||||
: no_infos);
|
||||
for (size_t i = 0; i < new_remote_description->contents().size(); ++i) {
|
||||
cricket::ContentInfo& content = new_remote_description->contents()[i];
|
||||
if (!content.name.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::string new_mid;
|
||||
absl::string_view source_explanation;
|
||||
if (IsUnifiedPlan()) {
|
||||
if (i < local_contents.size()) {
|
||||
new_mid = local_contents[i].name;
|
||||
source_explanation = "from the matching local media section";
|
||||
} else if (i < remote_contents.size()) {
|
||||
new_mid = remote_contents[i].name;
|
||||
source_explanation = "from the matching previous remote media section";
|
||||
} else {
|
||||
new_mid = mid_generator_();
|
||||
source_explanation = "generated just now";
|
||||
}
|
||||
} else {
|
||||
new_mid = std::string(
|
||||
GetDefaultMidForPlanB(content.media_description()->type()));
|
||||
source_explanation = "to match pre-existing behavior";
|
||||
}
|
||||
RTC_DCHECK(!new_mid.empty());
|
||||
content.name = new_mid;
|
||||
new_remote_description->transport_infos()[i].content_name = new_mid;
|
||||
RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i
|
||||
<< " is missing an a=mid line. Filling in the value '"
|
||||
<< new_mid << "' " << source_explanation << ".";
|
||||
}
|
||||
}
|
||||
|
||||
void PeerConnection::SetRemoteDescription(
|
||||
SetSessionDescriptionObserver* observer,
|
||||
SessionDescriptionInterface* desc_ptr) {
|
||||
@ -1839,48 +1749,6 @@ PeerConnection::GetTransceiverByMLineIndex(size_t mline_index) const {
|
||||
return transceivers_.FindByMLineIndex(mline_index);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
PeerConnection::FindAvailableTransceiverToReceive(
|
||||
cricket::MediaType media_type) const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
RTC_DCHECK(IsUnifiedPlan());
|
||||
// From JSEP section 5.10 (Applying a Remote Description):
|
||||
// If the m= section is sendrecv or recvonly, and there are RtpTransceivers of
|
||||
// the same type that were added to the PeerConnection by addTrack and are not
|
||||
// associated with any m= section and are not stopped, find the first such
|
||||
// RtpTransceiver.
|
||||
for (auto transceiver : transceivers_.List()) {
|
||||
if (transceiver->media_type() == media_type &&
|
||||
transceiver->internal()->created_by_addtrack() && !transceiver->mid() &&
|
||||
!transceiver->stopped()) {
|
||||
return transceiver;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const cricket::ContentInfo* PeerConnection::FindMediaSectionForTransceiver(
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
transceiver,
|
||||
const SessionDescriptionInterface* sdesc) const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
RTC_DCHECK(transceiver);
|
||||
RTC_DCHECK(sdesc);
|
||||
if (IsUnifiedPlan()) {
|
||||
if (!transceiver->internal()->mid()) {
|
||||
// This transceiver is not associated with a media section yet.
|
||||
return nullptr;
|
||||
}
|
||||
return sdesc->description()->GetContentByName(
|
||||
*transceiver->internal()->mid());
|
||||
} else {
|
||||
// Plan B only allows at most one audio and one video section, so use the
|
||||
// first media section of that type.
|
||||
return cricket::GetFirstMediaContent(sdesc->description()->contents(),
|
||||
transceiver->media_type());
|
||||
}
|
||||
}
|
||||
|
||||
PeerConnectionInterface::RTCConfiguration PeerConnection::GetConfiguration() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return configuration_;
|
||||
@ -2687,465 +2555,6 @@ void PeerConnection::PostCreateSessionDescriptionFailure(
|
||||
MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
|
||||
}
|
||||
|
||||
void PeerConnection::GetOptionsForOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
|
||||
|
||||
if (IsUnifiedPlan()) {
|
||||
GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options);
|
||||
} else {
|
||||
GetOptionsForPlanBOffer(offer_answer_options, session_options);
|
||||
}
|
||||
|
||||
// Intentionally unset the data channel type for RTP data channel with the
|
||||
// second condition. Otherwise the RTP data channels would be successfully
|
||||
// negotiated by default and the unit tests in WebRtcDataBrowserTest will fail
|
||||
// when building with chromium. We want to leave RTP data channels broken, so
|
||||
// people won't try to use them.
|
||||
if (data_channel_controller_.HasRtpDataChannels() ||
|
||||
data_channel_type() != cricket::DCT_RTP) {
|
||||
session_options->data_channel_type = data_channel_type();
|
||||
}
|
||||
|
||||
// Apply ICE restart flag and renomination flag.
|
||||
bool ice_restart =
|
||||
offer_answer_options.ice_restart || sdp_handler_.HasNewIceCredentials();
|
||||
for (auto& options : session_options->media_description_options) {
|
||||
options.transport_options.ice_restart = ice_restart;
|
||||
options.transport_options.enable_ice_renomination =
|
||||
configuration_.enable_ice_renomination;
|
||||
}
|
||||
|
||||
session_options->rtcp_cname = rtcp_cname_;
|
||||
session_options->crypto_options = GetCryptoOptions();
|
||||
session_options->pooled_ice_credentials =
|
||||
network_thread()->Invoke<std::vector<cricket::IceParameters>>(
|
||||
RTC_FROM_HERE,
|
||||
rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials,
|
||||
port_allocator_.get()));
|
||||
session_options->offer_extmap_allow_mixed =
|
||||
configuration_.offer_extmap_allow_mixed;
|
||||
|
||||
// Allow fallback for using obsolete SCTP syntax.
|
||||
// Note that the default in |session_options| is true, while
|
||||
// the default in |options| is false.
|
||||
session_options->use_obsolete_sctp_sdp =
|
||||
offer_answer_options.use_obsolete_sctp_sdp;
|
||||
}
|
||||
|
||||
void PeerConnection::GetOptionsForPlanBOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// Figure out transceiver directional preferences.
|
||||
bool send_audio = !GetAudioTransceiver()->internal()->senders().empty();
|
||||
bool send_video = !GetVideoTransceiver()->internal()->senders().empty();
|
||||
|
||||
// By default, generate sendrecv/recvonly m= sections.
|
||||
bool recv_audio = true;
|
||||
bool recv_video = true;
|
||||
|
||||
// By default, only offer a new m= section if we have media to send with it.
|
||||
bool offer_new_audio_description = send_audio;
|
||||
bool offer_new_video_description = send_video;
|
||||
bool offer_new_data_description = data_channel_controller_.HasDataChannels();
|
||||
|
||||
// The "offer_to_receive_X" options allow those defaults to be overridden.
|
||||
if (offer_answer_options.offer_to_receive_audio !=
|
||||
RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
|
||||
offer_new_audio_description =
|
||||
offer_new_audio_description ||
|
||||
(offer_answer_options.offer_to_receive_audio > 0);
|
||||
}
|
||||
if (offer_answer_options.offer_to_receive_video !=
|
||||
RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_video = (offer_answer_options.offer_to_receive_video > 0);
|
||||
offer_new_video_description =
|
||||
offer_new_video_description ||
|
||||
(offer_answer_options.offer_to_receive_video > 0);
|
||||
}
|
||||
|
||||
absl::optional<size_t> audio_index;
|
||||
absl::optional<size_t> video_index;
|
||||
absl::optional<size_t> data_index;
|
||||
// If a current description exists, generate m= sections in the same order,
|
||||
// using the first audio/video/data section that appears and rejecting
|
||||
// extraneous ones.
|
||||
if (local_description()) {
|
||||
GenerateMediaDescriptionOptions(
|
||||
local_description(),
|
||||
RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
|
||||
RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
|
||||
&audio_index, &video_index, &data_index, session_options);
|
||||
}
|
||||
|
||||
// Add audio/video/data m= sections to the end if needed.
|
||||
if (!audio_index && offer_new_audio_description) {
|
||||
cricket::MediaDescriptionOptions options(
|
||||
cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
|
||||
RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), false);
|
||||
options.header_extensions =
|
||||
channel_manager()->GetSupportedAudioRtpHeaderExtensions();
|
||||
session_options->media_description_options.push_back(options);
|
||||
audio_index = session_options->media_description_options.size() - 1;
|
||||
}
|
||||
if (!video_index && offer_new_video_description) {
|
||||
cricket::MediaDescriptionOptions options(
|
||||
cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
|
||||
RtpTransceiverDirectionFromSendRecv(send_video, recv_video), false);
|
||||
options.header_extensions =
|
||||
channel_manager()->GetSupportedVideoRtpHeaderExtensions();
|
||||
session_options->media_description_options.push_back(options);
|
||||
video_index = session_options->media_description_options.size() - 1;
|
||||
}
|
||||
if (!data_index && offer_new_data_description) {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForActiveData(cricket::CN_DATA));
|
||||
data_index = session_options->media_description_options.size() - 1;
|
||||
}
|
||||
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options =
|
||||
!audio_index ? nullptr
|
||||
: &session_options->media_description_options[*audio_index];
|
||||
cricket::MediaDescriptionOptions* video_media_description_options =
|
||||
!video_index ? nullptr
|
||||
: &session_options->media_description_options[*video_index];
|
||||
|
||||
AddPlanBRtpSenderOptions(GetSendersInternal(),
|
||||
audio_media_description_options,
|
||||
video_media_description_options,
|
||||
offer_answer_options.num_simulcast_layers);
|
||||
}
|
||||
|
||||
static cricket::MediaDescriptionOptions
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
transceiver,
|
||||
const std::string& mid,
|
||||
bool is_create_offer) {
|
||||
// NOTE: a stopping transceiver should be treated as a stopped one in
|
||||
// createOffer as specified in
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
|
||||
bool stopped =
|
||||
is_create_offer ? transceiver->stopping() : transceiver->stopped();
|
||||
cricket::MediaDescriptionOptions media_description_options(
|
||||
transceiver->media_type(), mid, transceiver->direction(), stopped);
|
||||
media_description_options.codec_preferences =
|
||||
transceiver->codec_preferences();
|
||||
media_description_options.header_extensions =
|
||||
transceiver->HeaderExtensionsToOffer();
|
||||
// This behavior is specified in JSEP. The gist is that:
|
||||
// 1. The MSID is included if the RtpTransceiver's direction is sendonly or
|
||||
// sendrecv.
|
||||
// 2. If the MSID is included, then it must be included in any subsequent
|
||||
// offer/answer exactly the same until the RtpTransceiver is stopped.
|
||||
if (stopped || (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
|
||||
!transceiver->internal()->has_ever_been_used_to_send())) {
|
||||
return media_description_options;
|
||||
}
|
||||
|
||||
cricket::SenderOptions sender_options;
|
||||
sender_options.track_id = transceiver->sender()->id();
|
||||
sender_options.stream_ids = transceiver->sender()->stream_ids();
|
||||
|
||||
// The following sets up RIDs and Simulcast.
|
||||
// RIDs are included if Simulcast is requested or if any RID was specified.
|
||||
RtpParameters send_parameters =
|
||||
transceiver->internal()->sender_internal()->GetParametersInternal();
|
||||
bool has_rids = std::any_of(send_parameters.encodings.begin(),
|
||||
send_parameters.encodings.end(),
|
||||
[](const RtpEncodingParameters& encoding) {
|
||||
return !encoding.rid.empty();
|
||||
});
|
||||
|
||||
std::vector<RidDescription> send_rids;
|
||||
SimulcastLayerList send_layers;
|
||||
for (const RtpEncodingParameters& encoding : send_parameters.encodings) {
|
||||
if (encoding.rid.empty()) {
|
||||
continue;
|
||||
}
|
||||
send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend));
|
||||
send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active));
|
||||
}
|
||||
|
||||
if (has_rids) {
|
||||
sender_options.rids = send_rids;
|
||||
}
|
||||
|
||||
sender_options.simulcast_layers = send_layers;
|
||||
// When RIDs are configured, we must set num_sim_layers to 0 to.
|
||||
// Otherwise, num_sim_layers must be 1 because either there is no
|
||||
// simulcast, or simulcast is acheived by munging the SDP.
|
||||
sender_options.num_sim_layers = has_rids ? 0 : 1;
|
||||
media_description_options.sender_options.push_back(sender_options);
|
||||
|
||||
return media_description_options;
|
||||
}
|
||||
|
||||
// Returns the ContentInfo at mline index |i|, or null if none exists.
|
||||
static const ContentInfo* GetContentByIndex(
|
||||
const SessionDescriptionInterface* sdesc,
|
||||
size_t i) {
|
||||
if (!sdesc) {
|
||||
return nullptr;
|
||||
}
|
||||
const ContentInfos& contents = sdesc->description()->contents();
|
||||
return (i < contents.size() ? &contents[i] : nullptr);
|
||||
}
|
||||
|
||||
void PeerConnection::GetOptionsForUnifiedPlanOffer(
|
||||
const RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial
|
||||
// Offers) and 5.2.2 (Subsequent Offers).
|
||||
RTC_DCHECK_EQ(session_options->media_description_options.size(), 0);
|
||||
const ContentInfos no_infos;
|
||||
const ContentInfos& local_contents =
|
||||
(local_description() ? local_description()->description()->contents()
|
||||
: no_infos);
|
||||
const ContentInfos& remote_contents =
|
||||
(remote_description() ? remote_description()->description()->contents()
|
||||
: no_infos);
|
||||
// The mline indices that can be recycled. New transceivers should reuse these
|
||||
// slots first.
|
||||
std::queue<size_t> recycleable_mline_indices;
|
||||
// First, go through each media section that exists in either the local or
|
||||
// remote description and generate a media section in this offer for the
|
||||
// associated transceiver. If a media section can be recycled, generate a
|
||||
// default, rejected media section here that can be later overwritten.
|
||||
for (size_t i = 0;
|
||||
i < std::max(local_contents.size(), remote_contents.size()); ++i) {
|
||||
// Either |local_content| or |remote_content| is non-null.
|
||||
const ContentInfo* local_content =
|
||||
(i < local_contents.size() ? &local_contents[i] : nullptr);
|
||||
const ContentInfo* current_local_content =
|
||||
GetContentByIndex(current_local_description(), i);
|
||||
const ContentInfo* remote_content =
|
||||
(i < remote_contents.size() ? &remote_contents[i] : nullptr);
|
||||
const ContentInfo* current_remote_content =
|
||||
GetContentByIndex(current_remote_description(), i);
|
||||
bool had_been_rejected =
|
||||
(current_local_content && current_local_content->rejected) ||
|
||||
(current_remote_content && current_remote_content->rejected);
|
||||
const std::string& mid =
|
||||
(local_content ? local_content->name : remote_content->name);
|
||||
cricket::MediaType media_type =
|
||||
(local_content ? local_content->media_description()->type()
|
||||
: remote_content->media_description()->type());
|
||||
if (media_type == cricket::MEDIA_TYPE_AUDIO ||
|
||||
media_type == cricket::MEDIA_TYPE_VIDEO) {
|
||||
// A media section is considered eligible for recycling if it is marked as
|
||||
// rejected in either the current local or current remote description.
|
||||
auto transceiver = GetAssociatedTransceiver(mid);
|
||||
if (!transceiver) {
|
||||
// No associated transceiver. The media section has been stopped.
|
||||
recycleable_mline_indices.push(i);
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(media_type, mid,
|
||||
RtpTransceiverDirection::kInactive,
|
||||
/*stopped=*/true));
|
||||
} else {
|
||||
// NOTE: a stopping transceiver should be treated as a stopped one in
|
||||
// createOffer as specified in
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
|
||||
if (had_been_rejected && transceiver->stopping()) {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
transceiver->media_type(), mid,
|
||||
RtpTransceiverDirection::kInactive,
|
||||
/*stopped=*/true));
|
||||
recycleable_mline_indices.push(i);
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
transceiver, mid,
|
||||
/*is_create_offer=*/true));
|
||||
// CreateOffer shouldn't really cause any state changes in
|
||||
// PeerConnection, but we need a way to match new transceivers to new
|
||||
// media sections in SetLocalDescription and JSEP specifies this is
|
||||
// done by recording the index of the media section generated for the
|
||||
// transceiver in the offer.
|
||||
transceiver->internal()->set_mline_index(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
|
||||
if (had_been_rejected) {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForRejectedData(mid));
|
||||
} else {
|
||||
RTC_CHECK(GetDataMid());
|
||||
if (mid == *GetDataMid()) {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForActiveData(mid));
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForRejectedData(mid));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next, look for transceivers that are newly added (that is, are not stopped
|
||||
// and not associated). Reuse media sections marked as recyclable first,
|
||||
// otherwise append to the end of the offer. New media sections should be
|
||||
// added in the order they were added to the PeerConnection.
|
||||
for (const auto& transceiver : transceivers_.List()) {
|
||||
if (transceiver->mid() || transceiver->stopping()) {
|
||||
continue;
|
||||
}
|
||||
size_t mline_index;
|
||||
if (!recycleable_mline_indices.empty()) {
|
||||
mline_index = recycleable_mline_indices.front();
|
||||
recycleable_mline_indices.pop();
|
||||
session_options->media_description_options[mline_index] =
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
transceiver, mid_generator_(), /*is_create_offer=*/true);
|
||||
} else {
|
||||
mline_index = session_options->media_description_options.size();
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
transceiver, mid_generator_(), /*is_create_offer=*/true));
|
||||
}
|
||||
// See comment above for why CreateOffer changes the transceiver's state.
|
||||
transceiver->internal()->set_mline_index(mline_index);
|
||||
}
|
||||
// Lastly, add a m-section if we have local data channels and an m section
|
||||
// does not already exist.
|
||||
if (!GetDataMid() && data_channel_controller_.HasDataChannels()) {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForActiveData(mid_generator_()));
|
||||
}
|
||||
}
|
||||
|
||||
void PeerConnection::GetOptionsForAnswer(
|
||||
const RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
|
||||
|
||||
if (IsUnifiedPlan()) {
|
||||
GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options);
|
||||
} else {
|
||||
GetOptionsForPlanBAnswer(offer_answer_options, session_options);
|
||||
}
|
||||
|
||||
// Intentionally unset the data channel type for RTP data channel. Otherwise
|
||||
// the RTP data channels would be successfully negotiated by default and the
|
||||
// unit tests in WebRtcDataBrowserTest will fail when building with chromium.
|
||||
// We want to leave RTP data channels broken, so people won't try to use them.
|
||||
if (data_channel_controller_.HasRtpDataChannels() ||
|
||||
data_channel_type() != cricket::DCT_RTP) {
|
||||
session_options->data_channel_type = data_channel_type();
|
||||
}
|
||||
|
||||
// Apply ICE renomination flag.
|
||||
for (auto& options : session_options->media_description_options) {
|
||||
options.transport_options.enable_ice_renomination =
|
||||
configuration_.enable_ice_renomination;
|
||||
}
|
||||
|
||||
session_options->rtcp_cname = rtcp_cname_;
|
||||
session_options->crypto_options = GetCryptoOptions();
|
||||
session_options->pooled_ice_credentials =
|
||||
network_thread()->Invoke<std::vector<cricket::IceParameters>>(
|
||||
RTC_FROM_HERE,
|
||||
rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials,
|
||||
port_allocator_.get()));
|
||||
}
|
||||
|
||||
void PeerConnection::GetOptionsForPlanBAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// Figure out transceiver directional preferences.
|
||||
bool send_audio = !GetAudioTransceiver()->internal()->senders().empty();
|
||||
bool send_video = !GetVideoTransceiver()->internal()->senders().empty();
|
||||
|
||||
// By default, generate sendrecv/recvonly m= sections. The direction is also
|
||||
// restricted by the direction in the offer.
|
||||
bool recv_audio = true;
|
||||
bool recv_video = true;
|
||||
|
||||
// The "offer_to_receive_X" options allow those defaults to be overridden.
|
||||
if (offer_answer_options.offer_to_receive_audio !=
|
||||
RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
|
||||
}
|
||||
if (offer_answer_options.offer_to_receive_video !=
|
||||
RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_video = (offer_answer_options.offer_to_receive_video > 0);
|
||||
}
|
||||
|
||||
absl::optional<size_t> audio_index;
|
||||
absl::optional<size_t> video_index;
|
||||
absl::optional<size_t> data_index;
|
||||
|
||||
// Generate m= sections that match those in the offer.
|
||||
// Note that mediasession.cc will handle intersection our preferred
|
||||
// direction with the offered direction.
|
||||
GenerateMediaDescriptionOptions(
|
||||
remote_description(),
|
||||
RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
|
||||
RtpTransceiverDirectionFromSendRecv(send_video, recv_video), &audio_index,
|
||||
&video_index, &data_index, session_options);
|
||||
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options =
|
||||
!audio_index ? nullptr
|
||||
: &session_options->media_description_options[*audio_index];
|
||||
cricket::MediaDescriptionOptions* video_media_description_options =
|
||||
!video_index ? nullptr
|
||||
: &session_options->media_description_options[*video_index];
|
||||
|
||||
AddPlanBRtpSenderOptions(GetSendersInternal(),
|
||||
audio_media_description_options,
|
||||
video_media_description_options,
|
||||
offer_answer_options.num_simulcast_layers);
|
||||
}
|
||||
|
||||
void PeerConnection::GetOptionsForUnifiedPlanAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial
|
||||
// Answers) and 5.3.2 (Subsequent Answers).
|
||||
RTC_DCHECK(remote_description());
|
||||
RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer);
|
||||
for (const ContentInfo& content :
|
||||
remote_description()->description()->contents()) {
|
||||
cricket::MediaType media_type = content.media_description()->type();
|
||||
if (media_type == cricket::MEDIA_TYPE_AUDIO ||
|
||||
media_type == cricket::MEDIA_TYPE_VIDEO) {
|
||||
auto transceiver = GetAssociatedTransceiver(content.name);
|
||||
if (transceiver) {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
transceiver, content.name,
|
||||
/*is_create_offer=*/false));
|
||||
} else {
|
||||
// This should only happen with rejected transceivers.
|
||||
RTC_DCHECK(content.rejected);
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(media_type, content.name,
|
||||
RtpTransceiverDirection::kInactive,
|
||||
/*stopped=*/true));
|
||||
}
|
||||
} else {
|
||||
RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
|
||||
// Reject all data sections if data channels are disabled.
|
||||
// Reject a data section if it has already been rejected.
|
||||
// Reject all data sections except for the first one.
|
||||
if (data_channel_type() == cricket::DCT_NONE || content.rejected ||
|
||||
content.name != *GetDataMid()) {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForRejectedData(content.name));
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForActiveData(content.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerConnection::GenerateMediaDescriptionOptions(
|
||||
const SessionDescriptionInterface* session_desc,
|
||||
@ -3155,6 +2564,7 @@ void PeerConnection::GenerateMediaDescriptionOptions(
|
||||
absl::optional<size_t>* video_index,
|
||||
absl::optional<size_t>* data_index,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
for (const cricket::ContentInfo& content :
|
||||
session_desc->description()->contents()) {
|
||||
if (IsAudioContent(&content)) {
|
||||
@ -3209,6 +2619,7 @@ void PeerConnection::GenerateMediaDescriptionOptions(
|
||||
cricket::MediaDescriptionOptions
|
||||
PeerConnection::GetMediaDescriptionOptionsForActiveData(
|
||||
const std::string& mid) const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
// Direction for data sections is meaningless, but legacy endpoints might
|
||||
// expect sendrecv.
|
||||
cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
|
||||
@ -3222,6 +2633,7 @@ PeerConnection::GetMediaDescriptionOptionsForActiveData(
|
||||
cricket::MediaDescriptionOptions
|
||||
PeerConnection::GetMediaDescriptionOptionsForRejectedData(
|
||||
const std::string& mid) const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
|
||||
RtpTransceiverDirection::kInactive,
|
||||
/*stopped=*/true);
|
||||
@ -3511,6 +2923,7 @@ void PeerConnection::OnSctpDataChannelClosed(DataChannelInterface* channel) {
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
PeerConnection::GetAudioTransceiver() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
// This method only works with Plan B SDP, where there is a single
|
||||
// audio/video transceiver.
|
||||
RTC_DCHECK(!IsUnifiedPlan());
|
||||
@ -3525,6 +2938,7 @@ PeerConnection::GetAudioTransceiver() const {
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
PeerConnection::GetVideoTransceiver() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
// This method only works with Plan B SDP, where there is a single
|
||||
// audio/video transceiver.
|
||||
RTC_DCHECK(!IsUnifiedPlan());
|
||||
@ -3839,7 +3253,7 @@ void PeerConnection::UpdatePayloadTypeDemuxingState(
|
||||
for (const auto& transceiver : transceivers_.List()) {
|
||||
cricket::ChannelInterface* channel = transceiver->internal()->channel();
|
||||
const ContentInfo* content =
|
||||
FindMediaSectionForTransceiver(transceiver, sdesc);
|
||||
sdp_handler_.FindMediaSectionForTransceiver(transceiver, sdesc);
|
||||
if (!channel || !content) {
|
||||
continue;
|
||||
}
|
||||
@ -3888,7 +3302,7 @@ RTCError PeerConnection::PushdownMediaDescription(
|
||||
// Push down the new SDP media section for each audio/video transceiver.
|
||||
for (const auto& transceiver : transceivers_.List()) {
|
||||
const ContentInfo* content_info =
|
||||
FindMediaSectionForTransceiver(transceiver, sdesc);
|
||||
sdp_handler_.FindMediaSectionForTransceiver(transceiver, sdesc);
|
||||
cricket::ChannelInterface* channel = transceiver->internal()->channel();
|
||||
if (!channel || !content_info || content_info->rejected) {
|
||||
continue;
|
||||
@ -4596,30 +4010,6 @@ bool PeerConnection::HasRtcpMuxEnabled(const cricket::ContentInfo* content) {
|
||||
return content->media_description()->rtcp_mux();
|
||||
}
|
||||
|
||||
bool PeerConnection::ExpectSetLocalDescription(SdpType type) {
|
||||
PeerConnectionInterface::SignalingState state = signaling_state();
|
||||
if (type == SdpType::kOffer) {
|
||||
return (state == PeerConnectionInterface::kStable) ||
|
||||
(state == PeerConnectionInterface::kHaveLocalOffer);
|
||||
} else {
|
||||
RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
|
||||
return (state == PeerConnectionInterface::kHaveRemoteOffer) ||
|
||||
(state == PeerConnectionInterface::kHaveLocalPrAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerConnection::ExpectSetRemoteDescription(SdpType type) {
|
||||
PeerConnectionInterface::SignalingState state = signaling_state();
|
||||
if (type == SdpType::kOffer) {
|
||||
return (state == PeerConnectionInterface::kStable) ||
|
||||
(state == PeerConnectionInterface::kHaveRemoteOffer);
|
||||
} else {
|
||||
RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
|
||||
return (state == PeerConnectionInterface::kHaveLocalOffer) ||
|
||||
(state == PeerConnectionInterface::kHaveRemotePrAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
const char* PeerConnection::SessionErrorToString(SessionError error) const {
|
||||
switch (error) {
|
||||
case SessionError::kNone:
|
||||
@ -5068,6 +4458,7 @@ PeerConnectionObserver* PeerConnection::Observer() const {
|
||||
}
|
||||
|
||||
CryptoOptions PeerConnection::GetCryptoOptions() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
// TODO(bugs.webrtc.org/9891) - Remove PeerConnectionFactory::CryptoOptions
|
||||
// after it has been removed.
|
||||
return configuration_.crypto_options.has_value()
|
||||
|
||||
@ -412,15 +412,15 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
|
||||
GetSendersInternal() const RTC_RUN_ON(signaling_thread());
|
||||
GetSendersInternal() const;
|
||||
std::vector<
|
||||
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>>
|
||||
GetReceiversInternal() const RTC_RUN_ON(signaling_thread());
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
GetAudioTransceiver() const RTC_RUN_ON(signaling_thread());
|
||||
GetAudioTransceiver() const;
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
GetVideoTransceiver() const RTC_RUN_ON(signaling_thread());
|
||||
GetVideoTransceiver() const;
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
GetFirstAudioTransceiver() const RTC_RUN_ON(signaling_thread());
|
||||
@ -560,20 +560,6 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
GetTransceiverByMLineIndex(size_t mline_index) const;
|
||||
|
||||
// Returns an RtpTransciever, if available, that can be used to receive the
|
||||
// given media type according to JSEP rules.
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
FindAvailableTransceiverToReceive(cricket::MediaType media_type) const;
|
||||
|
||||
// Returns the media section in the given session description that is
|
||||
// associated with the RtpTransceiver. Returns null if none found or this
|
||||
// RtpTransceiver is not associated. Logic varies depending on the
|
||||
// SdpSemantics specified in the configuration.
|
||||
const cricket::ContentInfo* FindMediaSectionForTransceiver(
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
transceiver,
|
||||
const SessionDescriptionInterface* sdesc) const;
|
||||
|
||||
// Runs the algorithm **process the removal of a remote track** specified in
|
||||
// the WebRTC specification.
|
||||
// This method will update the following lists:
|
||||
@ -595,21 +581,6 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
|
||||
void OnNegotiationNeeded();
|
||||
|
||||
// Returns a MediaSessionOptions struct with options decided by |options|,
|
||||
// the local MediaStreams and DataChannels.
|
||||
void GetOptionsForOffer(const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
void GetOptionsForPlanBOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void GetOptionsForUnifiedPlanOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
RTCError HandleLegacyOfferOptions(const RTCOfferAnswerOptions& options);
|
||||
void RemoveRecvDirectionFromReceivingTransceiversOfType(
|
||||
@ -620,21 +591,6 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
GetReceivingTransceiversOfType(cricket::MediaType media_type)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
// Returns a MediaSessionOptions struct with options decided by
|
||||
// |constraints|, the local MediaStreams and DataChannels.
|
||||
void GetOptionsForAnswer(const RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
void GetOptionsForPlanBAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void GetOptionsForUnifiedPlanAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
// Generates MediaDescriptionOptions for the |session_opts| based on existing
|
||||
// local description or remote description.
|
||||
void GenerateMediaDescriptionOptions(
|
||||
@ -644,18 +600,17 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
absl::optional<size_t>* audio_index,
|
||||
absl::optional<size_t>* video_index,
|
||||
absl::optional<size_t>* data_index,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
|
||||
// Generates the active MediaDescriptionOptions for the local data channel
|
||||
// given the specified MID.
|
||||
cricket::MediaDescriptionOptions GetMediaDescriptionOptionsForActiveData(
|
||||
const std::string& mid) const RTC_RUN_ON(signaling_thread());
|
||||
const std::string& mid) const;
|
||||
|
||||
// Generates the rejected MediaDescriptionOptions for the local data channel
|
||||
// given the specified MID.
|
||||
cricket::MediaDescriptionOptions GetMediaDescriptionOptionsForRejectedData(
|
||||
const std::string& mid) const RTC_RUN_ON(signaling_thread());
|
||||
const std::string& mid) const;
|
||||
|
||||
// Returns the MID for the data section associated with either the
|
||||
// RtpDataChannel or SCTP data channel, if it has been set. If no data
|
||||
@ -732,12 +687,6 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
return configuration_.sdp_semantics == SdpSemantics::kUnifiedPlan;
|
||||
}
|
||||
|
||||
// The offer/answer machinery assumes the media section MID is present and
|
||||
// unique. To support legacy end points that do not supply a=mid lines, this
|
||||
// method will modify the session description to add MIDs generated according
|
||||
// to the SDP semantics.
|
||||
void FillInMissingRemoteMids(cricket::SessionDescription* remote_description);
|
||||
|
||||
// Return the RtpSender with the given track attached.
|
||||
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>
|
||||
FindSenderForTrack(MediaStreamTrackInterface* track) const
|
||||
@ -893,12 +842,6 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
bool ValidateBundleSettings(const cricket::SessionDescription* desc);
|
||||
bool HasRtcpMuxEnabled(const cricket::ContentInfo* content);
|
||||
|
||||
// Check if a call to SetLocalDescription is acceptable with a session
|
||||
// description of the given type.
|
||||
bool ExpectSetLocalDescription(SdpType type);
|
||||
// Check if a call to SetRemoteDescription is acceptable with a session
|
||||
// description of the given type.
|
||||
bool ExpectSetRemoteDescription(SdpType type);
|
||||
// Verifies a=setup attribute as per RFC 5763.
|
||||
bool ValidateDtlsSetupAttribute(const cricket::SessionDescription* desc,
|
||||
SdpType type);
|
||||
@ -1004,7 +947,7 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
// Returns the CryptoOptions for this PeerConnection. This will always
|
||||
// return the RTCConfiguration.crypto_options if set and will only default
|
||||
// back to the PeerConnectionFactory settings if nothing was set.
|
||||
CryptoOptions GetCryptoOptions() RTC_RUN_ON(signaling_thread());
|
||||
CryptoOptions GetCryptoOptions();
|
||||
|
||||
// Returns rtp transport, result can not be nullptr.
|
||||
RtpTransportInternal* GetRtpTransport(const std::string& mid)
|
||||
|
||||
@ -10,6 +10,9 @@
|
||||
|
||||
#include "pc/sdp_offer_answer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
|
||||
#include "api/media_stream_proxy.h"
|
||||
#include "api/uma_metrics.h"
|
||||
#include "pc/media_stream.h"
|
||||
@ -40,6 +43,9 @@ namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
typedef webrtc::PeerConnectionInterface::RTCOfferAnswerOptions
|
||||
RTCOfferAnswerOptions;
|
||||
|
||||
// Error messages
|
||||
const char kInvalidSdp[] = "Invalid session description.";
|
||||
const char kInvalidCandidates[] = "Description contains invalid candidates.";
|
||||
@ -494,6 +500,132 @@ static RTCError DisableSimulcastInSender(
|
||||
return sender->DisableEncodingLayers(disabled_layers);
|
||||
}
|
||||
|
||||
// The SDP parser used to populate these values by default for the 'content
|
||||
// name' if an a=mid line was absent.
|
||||
static absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) {
|
||||
switch (media_type) {
|
||||
case cricket::MEDIA_TYPE_AUDIO:
|
||||
return cricket::CN_AUDIO;
|
||||
case cricket::MEDIA_TYPE_VIDEO:
|
||||
return cricket::CN_VIDEO;
|
||||
case cricket::MEDIA_TYPE_DATA:
|
||||
return cricket::CN_DATA;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
|
||||
// Add options to |[audio/video]_media_description_options| from |senders|.
|
||||
void AddPlanBRtpSenderOptions(
|
||||
const std::vector<rtc::scoped_refptr<
|
||||
RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options,
|
||||
cricket::MediaDescriptionOptions* video_media_description_options,
|
||||
int num_sim_layers) {
|
||||
for (const auto& sender : senders) {
|
||||
if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
|
||||
if (audio_media_description_options) {
|
||||
audio_media_description_options->AddAudioSender(
|
||||
sender->id(), sender->internal()->stream_ids());
|
||||
}
|
||||
} else {
|
||||
RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO);
|
||||
if (video_media_description_options) {
|
||||
video_media_description_options->AddVideoSender(
|
||||
sender->id(), sender->internal()->stream_ids(), {},
|
||||
SimulcastLayerList(), num_sim_layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static cricket::MediaDescriptionOptions
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
transceiver,
|
||||
const std::string& mid,
|
||||
bool is_create_offer) {
|
||||
// NOTE: a stopping transceiver should be treated as a stopped one in
|
||||
// createOffer as specified in
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
|
||||
bool stopped =
|
||||
is_create_offer ? transceiver->stopping() : transceiver->stopped();
|
||||
cricket::MediaDescriptionOptions media_description_options(
|
||||
transceiver->media_type(), mid, transceiver->direction(), stopped);
|
||||
media_description_options.codec_preferences =
|
||||
transceiver->codec_preferences();
|
||||
media_description_options.header_extensions =
|
||||
transceiver->HeaderExtensionsToOffer();
|
||||
// This behavior is specified in JSEP. The gist is that:
|
||||
// 1. The MSID is included if the RtpTransceiver's direction is sendonly or
|
||||
// sendrecv.
|
||||
// 2. If the MSID is included, then it must be included in any subsequent
|
||||
// offer/answer exactly the same until the RtpTransceiver is stopped.
|
||||
if (stopped || (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
|
||||
!transceiver->internal()->has_ever_been_used_to_send())) {
|
||||
return media_description_options;
|
||||
}
|
||||
|
||||
cricket::SenderOptions sender_options;
|
||||
sender_options.track_id = transceiver->sender()->id();
|
||||
sender_options.stream_ids = transceiver->sender()->stream_ids();
|
||||
|
||||
// The following sets up RIDs and Simulcast.
|
||||
// RIDs are included if Simulcast is requested or if any RID was specified.
|
||||
RtpParameters send_parameters =
|
||||
transceiver->internal()->sender_internal()->GetParametersInternal();
|
||||
bool has_rids = std::any_of(send_parameters.encodings.begin(),
|
||||
send_parameters.encodings.end(),
|
||||
[](const RtpEncodingParameters& encoding) {
|
||||
return !encoding.rid.empty();
|
||||
});
|
||||
|
||||
std::vector<RidDescription> send_rids;
|
||||
SimulcastLayerList send_layers;
|
||||
for (const RtpEncodingParameters& encoding : send_parameters.encodings) {
|
||||
if (encoding.rid.empty()) {
|
||||
continue;
|
||||
}
|
||||
send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend));
|
||||
send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active));
|
||||
}
|
||||
|
||||
if (has_rids) {
|
||||
sender_options.rids = send_rids;
|
||||
}
|
||||
|
||||
sender_options.simulcast_layers = send_layers;
|
||||
// When RIDs are configured, we must set num_sim_layers to 0 to.
|
||||
// Otherwise, num_sim_layers must be 1 because either there is no
|
||||
// simulcast, or simulcast is acheived by munging the SDP.
|
||||
sender_options.num_sim_layers = has_rids ? 0 : 1;
|
||||
media_description_options.sender_options.push_back(sender_options);
|
||||
|
||||
return media_description_options;
|
||||
}
|
||||
|
||||
// Returns the ContentInfo at mline index |i|, or null if none exists.
|
||||
static const ContentInfo* GetContentByIndex(
|
||||
const SessionDescriptionInterface* sdesc,
|
||||
size_t i) {
|
||||
if (!sdesc) {
|
||||
return nullptr;
|
||||
}
|
||||
const ContentInfos& contents = sdesc->description()->contents();
|
||||
return (i < contents.size() ? &contents[i] : nullptr);
|
||||
}
|
||||
|
||||
// From |rtc_options|, fill parts of |session_options| shared by all generated
|
||||
// m= sectionss (in other words, nothing that involves a map/array).
|
||||
void ExtractSharedMediaSessionOptions(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
session_options->vad_enabled = rtc_options.voice_activity_detection;
|
||||
session_options->bundle_enabled = rtc_options.use_rtp_mux;
|
||||
session_options->raw_packetization_for_video =
|
||||
rtc_options.raw_packetization_for_video;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Used by parameterless SetLocalDescription() to create an offer or answer.
|
||||
@ -964,7 +1096,7 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
|
||||
}
|
||||
|
||||
const ContentInfo* content =
|
||||
pc_->FindMediaSectionForTransceiver(transceiver, local_description());
|
||||
FindMediaSectionForTransceiver(transceiver, local_description());
|
||||
if (!content) {
|
||||
continue;
|
||||
}
|
||||
@ -1041,7 +1173,7 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
|
||||
continue;
|
||||
}
|
||||
const ContentInfo* content =
|
||||
pc_->FindMediaSectionForTransceiver(transceiver, local_description());
|
||||
FindMediaSectionForTransceiver(transceiver, local_description());
|
||||
if (!content) {
|
||||
continue;
|
||||
}
|
||||
@ -1316,8 +1448,8 @@ RTCError SdpOfferAnswerHandler::ApplyRemoteDescription(
|
||||
std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
|
||||
std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
|
||||
for (const auto& transceiver : pc_->transceivers_.List()) {
|
||||
const ContentInfo* content = pc_->FindMediaSectionForTransceiver(
|
||||
transceiver, remote_description());
|
||||
const ContentInfo* content =
|
||||
FindMediaSectionForTransceiver(transceiver, remote_description());
|
||||
if (!content) {
|
||||
continue;
|
||||
}
|
||||
@ -1668,7 +1800,7 @@ void SdpOfferAnswerHandler::DoCreateOffer(
|
||||
}
|
||||
|
||||
cricket::MediaSessionOptions session_options;
|
||||
pc_->GetOptionsForOffer(options, &session_options);
|
||||
GetOptionsForOffer(options, &session_options);
|
||||
webrtc_session_desc_factory_->CreateOffer(observer, options, session_options);
|
||||
}
|
||||
|
||||
@ -1753,7 +1885,7 @@ void SdpOfferAnswerHandler::DoCreateAnswer(
|
||||
}
|
||||
|
||||
cricket::MediaSessionOptions session_options;
|
||||
pc_->GetOptionsForAnswer(options, &session_options);
|
||||
GetOptionsForAnswer(options, &session_options);
|
||||
webrtc_session_desc_factory_->CreateAnswer(observer, session_options);
|
||||
}
|
||||
|
||||
@ -1808,7 +1940,7 @@ void SdpOfferAnswerHandler::DoSetRemoteDescription(
|
||||
|
||||
// Handle remote descriptions missing a=mid lines for interop with legacy end
|
||||
// points.
|
||||
pc_->FillInMissingRemoteMids(desc->description());
|
||||
FillInMissingRemoteMids(desc->description());
|
||||
|
||||
RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE);
|
||||
if (!error.ok()) {
|
||||
@ -2539,9 +2671,8 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
|
||||
}
|
||||
|
||||
SdpType type = sdesc->GetType();
|
||||
if ((source == cricket::CS_LOCAL && !pc_->ExpectSetLocalDescription(type)) ||
|
||||
(source == cricket::CS_REMOTE &&
|
||||
!pc_->ExpectSetRemoteDescription(type))) {
|
||||
if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) ||
|
||||
(source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::INVALID_STATE,
|
||||
"Called in wrong state: " + GetSignalingStateString(signaling_state()));
|
||||
@ -2759,7 +2890,7 @@ SdpOfferAnswerHandler::AssociateTransceiver(
|
||||
if (!transceiver &&
|
||||
RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
|
||||
!media_desc->HasSimulcast()) {
|
||||
transceiver = pc_->FindAvailableTransceiverToReceive(media_desc->type());
|
||||
transceiver = FindAvailableTransceiverToReceive(media_desc->type());
|
||||
}
|
||||
// If no RtpTransceiver was found in the previous step, create one with a
|
||||
// recvonly direction.
|
||||
@ -2918,4 +3049,501 @@ RTCError SdpOfferAnswerHandler::UpdateDataChannel(
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
bool SdpOfferAnswerHandler::ExpectSetLocalDescription(SdpType type) {
|
||||
PeerConnectionInterface::SignalingState state = signaling_state();
|
||||
if (type == SdpType::kOffer) {
|
||||
return (state == PeerConnectionInterface::kStable) ||
|
||||
(state == PeerConnectionInterface::kHaveLocalOffer);
|
||||
} else {
|
||||
RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
|
||||
return (state == PeerConnectionInterface::kHaveRemoteOffer) ||
|
||||
(state == PeerConnectionInterface::kHaveLocalPrAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
bool SdpOfferAnswerHandler::ExpectSetRemoteDescription(SdpType type) {
|
||||
PeerConnectionInterface::SignalingState state = signaling_state();
|
||||
if (type == SdpType::kOffer) {
|
||||
return (state == PeerConnectionInterface::kStable) ||
|
||||
(state == PeerConnectionInterface::kHaveRemoteOffer);
|
||||
} else {
|
||||
RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
|
||||
return (state == PeerConnectionInterface::kHaveLocalOffer) ||
|
||||
(state == PeerConnectionInterface::kHaveRemotePrAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
void SdpOfferAnswerHandler::FillInMissingRemoteMids(
|
||||
cricket::SessionDescription* new_remote_description) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
RTC_DCHECK(new_remote_description);
|
||||
const cricket::ContentInfos no_infos;
|
||||
const cricket::ContentInfos& local_contents =
|
||||
(local_description() ? local_description()->description()->contents()
|
||||
: no_infos);
|
||||
const cricket::ContentInfos& remote_contents =
|
||||
(remote_description() ? remote_description()->description()->contents()
|
||||
: no_infos);
|
||||
for (size_t i = 0; i < new_remote_description->contents().size(); ++i) {
|
||||
cricket::ContentInfo& content = new_remote_description->contents()[i];
|
||||
if (!content.name.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::string new_mid;
|
||||
absl::string_view source_explanation;
|
||||
if (IsUnifiedPlan()) {
|
||||
if (i < local_contents.size()) {
|
||||
new_mid = local_contents[i].name;
|
||||
source_explanation = "from the matching local media section";
|
||||
} else if (i < remote_contents.size()) {
|
||||
new_mid = remote_contents[i].name;
|
||||
source_explanation = "from the matching previous remote media section";
|
||||
} else {
|
||||
new_mid = pc_->mid_generator()->GenerateString();
|
||||
source_explanation = "generated just now";
|
||||
}
|
||||
} else {
|
||||
new_mid = std::string(
|
||||
GetDefaultMidForPlanB(content.media_description()->type()));
|
||||
source_explanation = "to match pre-existing behavior";
|
||||
}
|
||||
RTC_DCHECK(!new_mid.empty());
|
||||
content.name = new_mid;
|
||||
new_remote_description->transport_infos()[i].content_name = new_mid;
|
||||
RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i
|
||||
<< " is missing an a=mid line. Filling in the value '"
|
||||
<< new_mid << "' " << source_explanation << ".";
|
||||
}
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
SdpOfferAnswerHandler::FindAvailableTransceiverToReceive(
|
||||
cricket::MediaType media_type) const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
RTC_DCHECK(IsUnifiedPlan());
|
||||
// From JSEP section 5.10 (Applying a Remote Description):
|
||||
// If the m= section is sendrecv or recvonly, and there are RtpTransceivers of
|
||||
// the same type that were added to the PeerConnection by addTrack and are not
|
||||
// associated with any m= section and are not stopped, find the first such
|
||||
// RtpTransceiver.
|
||||
for (auto transceiver : pc_->transceivers_.List()) {
|
||||
if (transceiver->media_type() == media_type &&
|
||||
transceiver->internal()->created_by_addtrack() && !transceiver->mid() &&
|
||||
!transceiver->stopped()) {
|
||||
return transceiver;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const cricket::ContentInfo*
|
||||
SdpOfferAnswerHandler::FindMediaSectionForTransceiver(
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
transceiver,
|
||||
const SessionDescriptionInterface* sdesc) const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
RTC_DCHECK(transceiver);
|
||||
RTC_DCHECK(sdesc);
|
||||
if (IsUnifiedPlan()) {
|
||||
if (!transceiver->internal()->mid()) {
|
||||
// This transceiver is not associated with a media section yet.
|
||||
return nullptr;
|
||||
}
|
||||
return sdesc->description()->GetContentByName(
|
||||
*transceiver->internal()->mid());
|
||||
} else {
|
||||
// Plan B only allows at most one audio and one video section, so use the
|
||||
// first media section of that type.
|
||||
return cricket::GetFirstMediaContent(sdesc->description()->contents(),
|
||||
transceiver->media_type());
|
||||
}
|
||||
}
|
||||
|
||||
void SdpOfferAnswerHandler::GetOptionsForOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
|
||||
|
||||
if (IsUnifiedPlan()) {
|
||||
GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options);
|
||||
} else {
|
||||
GetOptionsForPlanBOffer(offer_answer_options, session_options);
|
||||
}
|
||||
|
||||
// Intentionally unset the data channel type for RTP data channel with the
|
||||
// second condition. Otherwise the RTP data channels would be successfully
|
||||
// negotiated by default and the unit tests in WebRtcDataBrowserTest will fail
|
||||
// when building with chromium. We want to leave RTP data channels broken, so
|
||||
// people won't try to use them.
|
||||
if (pc_->data_channel_controller()->HasRtpDataChannels() ||
|
||||
pc_->data_channel_type() != cricket::DCT_RTP) {
|
||||
session_options->data_channel_type = pc_->data_channel_type();
|
||||
}
|
||||
|
||||
// Apply ICE restart flag and renomination flag.
|
||||
bool ice_restart = offer_answer_options.ice_restart || HasNewIceCredentials();
|
||||
for (auto& options : session_options->media_description_options) {
|
||||
options.transport_options.ice_restart = ice_restart;
|
||||
options.transport_options.enable_ice_renomination =
|
||||
pc_->configuration()->enable_ice_renomination;
|
||||
}
|
||||
|
||||
session_options->rtcp_cname = pc_->rtcp_cname_;
|
||||
session_options->crypto_options = pc_->GetCryptoOptions();
|
||||
session_options->pooled_ice_credentials =
|
||||
pc_->network_thread()->Invoke<std::vector<cricket::IceParameters>>(
|
||||
RTC_FROM_HERE,
|
||||
rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials,
|
||||
pc_->port_allocator_.get()));
|
||||
session_options->offer_extmap_allow_mixed =
|
||||
pc_->configuration()->offer_extmap_allow_mixed;
|
||||
|
||||
// Allow fallback for using obsolete SCTP syntax.
|
||||
// Note that the default in |session_options| is true, while
|
||||
// the default in |options| is false.
|
||||
session_options->use_obsolete_sctp_sdp =
|
||||
offer_answer_options.use_obsolete_sctp_sdp;
|
||||
}
|
||||
|
||||
void SdpOfferAnswerHandler::GetOptionsForPlanBOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// Figure out transceiver directional preferences.
|
||||
bool send_audio = !pc_->GetAudioTransceiver()->internal()->senders().empty();
|
||||
bool send_video = !pc_->GetVideoTransceiver()->internal()->senders().empty();
|
||||
|
||||
// By default, generate sendrecv/recvonly m= sections.
|
||||
bool recv_audio = true;
|
||||
bool recv_video = true;
|
||||
|
||||
// By default, only offer a new m= section if we have media to send with it.
|
||||
bool offer_new_audio_description = send_audio;
|
||||
bool offer_new_video_description = send_video;
|
||||
bool offer_new_data_description =
|
||||
pc_->data_channel_controller()->HasDataChannels();
|
||||
|
||||
// The "offer_to_receive_X" options allow those defaults to be overridden.
|
||||
if (offer_answer_options.offer_to_receive_audio !=
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
|
||||
offer_new_audio_description =
|
||||
offer_new_audio_description ||
|
||||
(offer_answer_options.offer_to_receive_audio > 0);
|
||||
}
|
||||
if (offer_answer_options.offer_to_receive_video !=
|
||||
RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_video = (offer_answer_options.offer_to_receive_video > 0);
|
||||
offer_new_video_description =
|
||||
offer_new_video_description ||
|
||||
(offer_answer_options.offer_to_receive_video > 0);
|
||||
}
|
||||
|
||||
absl::optional<size_t> audio_index;
|
||||
absl::optional<size_t> video_index;
|
||||
absl::optional<size_t> data_index;
|
||||
// If a current description exists, generate m= sections in the same order,
|
||||
// using the first audio/video/data section that appears and rejecting
|
||||
// extraneous ones.
|
||||
if (local_description()) {
|
||||
pc_->GenerateMediaDescriptionOptions(
|
||||
local_description(),
|
||||
RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
|
||||
RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
|
||||
&audio_index, &video_index, &data_index, session_options);
|
||||
}
|
||||
|
||||
// Add audio/video/data m= sections to the end if needed.
|
||||
if (!audio_index && offer_new_audio_description) {
|
||||
cricket::MediaDescriptionOptions options(
|
||||
cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
|
||||
RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), false);
|
||||
options.header_extensions =
|
||||
pc_->channel_manager()->GetSupportedAudioRtpHeaderExtensions();
|
||||
session_options->media_description_options.push_back(options);
|
||||
audio_index = session_options->media_description_options.size() - 1;
|
||||
}
|
||||
if (!video_index && offer_new_video_description) {
|
||||
cricket::MediaDescriptionOptions options(
|
||||
cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
|
||||
RtpTransceiverDirectionFromSendRecv(send_video, recv_video), false);
|
||||
options.header_extensions =
|
||||
pc_->channel_manager()->GetSupportedVideoRtpHeaderExtensions();
|
||||
session_options->media_description_options.push_back(options);
|
||||
video_index = session_options->media_description_options.size() - 1;
|
||||
}
|
||||
if (!data_index && offer_new_data_description) {
|
||||
session_options->media_description_options.push_back(
|
||||
pc_->GetMediaDescriptionOptionsForActiveData(cricket::CN_DATA));
|
||||
data_index = session_options->media_description_options.size() - 1;
|
||||
}
|
||||
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options =
|
||||
!audio_index ? nullptr
|
||||
: &session_options->media_description_options[*audio_index];
|
||||
cricket::MediaDescriptionOptions* video_media_description_options =
|
||||
!video_index ? nullptr
|
||||
: &session_options->media_description_options[*video_index];
|
||||
|
||||
AddPlanBRtpSenderOptions(pc_->GetSendersInternal(),
|
||||
audio_media_description_options,
|
||||
video_media_description_options,
|
||||
offer_answer_options.num_simulcast_layers);
|
||||
}
|
||||
|
||||
void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanOffer(
|
||||
const RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial
|
||||
// Offers) and 5.2.2 (Subsequent Offers).
|
||||
RTC_DCHECK_EQ(session_options->media_description_options.size(), 0);
|
||||
const ContentInfos no_infos;
|
||||
const ContentInfos& local_contents =
|
||||
(local_description() ? local_description()->description()->contents()
|
||||
: no_infos);
|
||||
const ContentInfos& remote_contents =
|
||||
(remote_description() ? remote_description()->description()->contents()
|
||||
: no_infos);
|
||||
// The mline indices that can be recycled. New transceivers should reuse these
|
||||
// slots first.
|
||||
std::queue<size_t> recycleable_mline_indices;
|
||||
// First, go through each media section that exists in either the local or
|
||||
// remote description and generate a media section in this offer for the
|
||||
// associated transceiver. If a media section can be recycled, generate a
|
||||
// default, rejected media section here that can be later overwritten.
|
||||
for (size_t i = 0;
|
||||
i < std::max(local_contents.size(), remote_contents.size()); ++i) {
|
||||
// Either |local_content| or |remote_content| is non-null.
|
||||
const ContentInfo* local_content =
|
||||
(i < local_contents.size() ? &local_contents[i] : nullptr);
|
||||
const ContentInfo* current_local_content =
|
||||
GetContentByIndex(current_local_description(), i);
|
||||
const ContentInfo* remote_content =
|
||||
(i < remote_contents.size() ? &remote_contents[i] : nullptr);
|
||||
const ContentInfo* current_remote_content =
|
||||
GetContentByIndex(current_remote_description(), i);
|
||||
bool had_been_rejected =
|
||||
(current_local_content && current_local_content->rejected) ||
|
||||
(current_remote_content && current_remote_content->rejected);
|
||||
const std::string& mid =
|
||||
(local_content ? local_content->name : remote_content->name);
|
||||
cricket::MediaType media_type =
|
||||
(local_content ? local_content->media_description()->type()
|
||||
: remote_content->media_description()->type());
|
||||
if (media_type == cricket::MEDIA_TYPE_AUDIO ||
|
||||
media_type == cricket::MEDIA_TYPE_VIDEO) {
|
||||
// A media section is considered eligible for recycling if it is marked as
|
||||
// rejected in either the current local or current remote description.
|
||||
auto transceiver = pc_->GetAssociatedTransceiver(mid);
|
||||
if (!transceiver) {
|
||||
// No associated transceiver. The media section has been stopped.
|
||||
recycleable_mline_indices.push(i);
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(media_type, mid,
|
||||
RtpTransceiverDirection::kInactive,
|
||||
/*stopped=*/true));
|
||||
} else {
|
||||
// NOTE: a stopping transceiver should be treated as a stopped one in
|
||||
// createOffer as specified in
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
|
||||
if (had_been_rejected && transceiver->stopping()) {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
transceiver->media_type(), mid,
|
||||
RtpTransceiverDirection::kInactive,
|
||||
/*stopped=*/true));
|
||||
recycleable_mline_indices.push(i);
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
transceiver, mid,
|
||||
/*is_create_offer=*/true));
|
||||
// CreateOffer shouldn't really cause any state changes in
|
||||
// PeerConnection, but we need a way to match new transceivers to new
|
||||
// media sections in SetLocalDescription and JSEP specifies this is
|
||||
// done by recording the index of the media section generated for the
|
||||
// transceiver in the offer.
|
||||
transceiver->internal()->set_mline_index(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
|
||||
if (had_been_rejected) {
|
||||
session_options->media_description_options.push_back(
|
||||
pc_->GetMediaDescriptionOptionsForRejectedData(mid));
|
||||
} else {
|
||||
RTC_CHECK(pc_->GetDataMid());
|
||||
if (mid == *(pc_->GetDataMid())) {
|
||||
session_options->media_description_options.push_back(
|
||||
pc_->GetMediaDescriptionOptionsForActiveData(mid));
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
pc_->GetMediaDescriptionOptionsForRejectedData(mid));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next, look for transceivers that are newly added (that is, are not stopped
|
||||
// and not associated). Reuse media sections marked as recyclable first,
|
||||
// otherwise append to the end of the offer. New media sections should be
|
||||
// added in the order they were added to the PeerConnection.
|
||||
for (const auto& transceiver : pc_->transceivers_.List()) {
|
||||
if (transceiver->mid() || transceiver->stopping()) {
|
||||
continue;
|
||||
}
|
||||
size_t mline_index;
|
||||
if (!recycleable_mline_indices.empty()) {
|
||||
mline_index = recycleable_mline_indices.front();
|
||||
recycleable_mline_indices.pop();
|
||||
session_options->media_description_options[mline_index] =
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
transceiver, pc_->mid_generator()->GenerateString(),
|
||||
/*is_create_offer=*/true);
|
||||
} else {
|
||||
mline_index = session_options->media_description_options.size();
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
transceiver, pc_->mid_generator()->GenerateString(),
|
||||
/*is_create_offer=*/true));
|
||||
}
|
||||
// See comment above for why CreateOffer changes the transceiver's state.
|
||||
transceiver->internal()->set_mline_index(mline_index);
|
||||
}
|
||||
// Lastly, add a m-section if we have local data channels and an m section
|
||||
// does not already exist.
|
||||
if (!pc_->GetDataMid() && pc_->data_channel_controller()->HasDataChannels()) {
|
||||
session_options->media_description_options.push_back(
|
||||
pc_->GetMediaDescriptionOptionsForActiveData(
|
||||
pc_->mid_generator()->GenerateString()));
|
||||
}
|
||||
}
|
||||
|
||||
void SdpOfferAnswerHandler::GetOptionsForAnswer(
|
||||
const RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
|
||||
|
||||
if (IsUnifiedPlan()) {
|
||||
GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options);
|
||||
} else {
|
||||
GetOptionsForPlanBAnswer(offer_answer_options, session_options);
|
||||
}
|
||||
|
||||
// Intentionally unset the data channel type for RTP data channel. Otherwise
|
||||
// the RTP data channels would be successfully negotiated by default and the
|
||||
// unit tests in WebRtcDataBrowserTest will fail when building with chromium.
|
||||
// We want to leave RTP data channels broken, so people won't try to use them.
|
||||
if (pc_->data_channel_controller()->HasRtpDataChannels() ||
|
||||
pc_->data_channel_type() != cricket::DCT_RTP) {
|
||||
session_options->data_channel_type = pc_->data_channel_type();
|
||||
}
|
||||
|
||||
// Apply ICE renomination flag.
|
||||
for (auto& options : session_options->media_description_options) {
|
||||
options.transport_options.enable_ice_renomination =
|
||||
pc_->configuration()->enable_ice_renomination;
|
||||
}
|
||||
|
||||
session_options->rtcp_cname = pc_->rtcp_cname_;
|
||||
session_options->crypto_options = pc_->GetCryptoOptions();
|
||||
session_options->pooled_ice_credentials =
|
||||
pc_->network_thread()->Invoke<std::vector<cricket::IceParameters>>(
|
||||
RTC_FROM_HERE,
|
||||
rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials,
|
||||
pc_->port_allocator_.get()));
|
||||
}
|
||||
|
||||
void SdpOfferAnswerHandler::GetOptionsForPlanBAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// Figure out transceiver directional preferences.
|
||||
bool send_audio = !pc_->GetAudioTransceiver()->internal()->senders().empty();
|
||||
bool send_video = !pc_->GetVideoTransceiver()->internal()->senders().empty();
|
||||
|
||||
// By default, generate sendrecv/recvonly m= sections. The direction is also
|
||||
// restricted by the direction in the offer.
|
||||
bool recv_audio = true;
|
||||
bool recv_video = true;
|
||||
|
||||
// The "offer_to_receive_X" options allow those defaults to be overridden.
|
||||
if (offer_answer_options.offer_to_receive_audio !=
|
||||
RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
|
||||
}
|
||||
if (offer_answer_options.offer_to_receive_video !=
|
||||
RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_video = (offer_answer_options.offer_to_receive_video > 0);
|
||||
}
|
||||
|
||||
absl::optional<size_t> audio_index;
|
||||
absl::optional<size_t> video_index;
|
||||
absl::optional<size_t> data_index;
|
||||
|
||||
// Generate m= sections that match those in the offer.
|
||||
// Note that mediasession.cc will handle intersection our preferred
|
||||
// direction with the offered direction.
|
||||
pc_->GenerateMediaDescriptionOptions(
|
||||
remote_description(),
|
||||
RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
|
||||
RtpTransceiverDirectionFromSendRecv(send_video, recv_video), &audio_index,
|
||||
&video_index, &data_index, session_options);
|
||||
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options =
|
||||
!audio_index ? nullptr
|
||||
: &session_options->media_description_options[*audio_index];
|
||||
cricket::MediaDescriptionOptions* video_media_description_options =
|
||||
!video_index ? nullptr
|
||||
: &session_options->media_description_options[*video_index];
|
||||
|
||||
AddPlanBRtpSenderOptions(pc_->GetSendersInternal(),
|
||||
audio_media_description_options,
|
||||
video_media_description_options,
|
||||
offer_answer_options.num_simulcast_layers);
|
||||
}
|
||||
|
||||
void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial
|
||||
// Answers) and 5.3.2 (Subsequent Answers).
|
||||
RTC_DCHECK(remote_description());
|
||||
RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer);
|
||||
for (const ContentInfo& content :
|
||||
remote_description()->description()->contents()) {
|
||||
cricket::MediaType media_type = content.media_description()->type();
|
||||
if (media_type == cricket::MEDIA_TYPE_AUDIO ||
|
||||
media_type == cricket::MEDIA_TYPE_VIDEO) {
|
||||
auto transceiver = pc_->GetAssociatedTransceiver(content.name);
|
||||
if (transceiver) {
|
||||
session_options->media_description_options.push_back(
|
||||
GetMediaDescriptionOptionsForTransceiver(
|
||||
transceiver, content.name,
|
||||
/*is_create_offer=*/false));
|
||||
} else {
|
||||
// This should only happen with rejected transceivers.
|
||||
RTC_DCHECK(content.rejected);
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(media_type, content.name,
|
||||
RtpTransceiverDirection::kInactive,
|
||||
/*stopped=*/true));
|
||||
}
|
||||
} else {
|
||||
RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
|
||||
// Reject all data sections if data channels are disabled.
|
||||
// Reject a data section if it has already been rejected.
|
||||
// Reject all data sections except for the first one.
|
||||
if (pc_->data_channel_type() == cricket::DCT_NONE || content.rejected ||
|
||||
content.name != *(pc_->GetDataMid())) {
|
||||
session_options->media_description_options.push_back(
|
||||
pc_->GetMediaDescriptionOptionsForRejectedData(content.name));
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
pc_->GetMediaDescriptionOptionsForActiveData(content.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -131,6 +131,15 @@ class SdpOfferAnswerHandler {
|
||||
bool IceRestartPending(const std::string& content_name) const;
|
||||
void UpdateNegotiationNeeded();
|
||||
|
||||
// Returns the media section in the given session description that is
|
||||
// associated with the RtpTransceiver. Returns null if none found or this
|
||||
// RtpTransceiver is not associated. Logic varies depending on the
|
||||
// SdpSemantics specified in the configuration.
|
||||
const cricket::ContentInfo* FindMediaSectionForTransceiver(
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
transceiver,
|
||||
const SessionDescriptionInterface* sdesc) const;
|
||||
|
||||
private:
|
||||
class ImplicitCreateSessionDescriptionObserver;
|
||||
friend class ImplicitCreateSessionDescriptionObserver;
|
||||
@ -253,6 +262,55 @@ class SdpOfferAnswerHandler {
|
||||
const cricket::ContentInfo& content,
|
||||
const cricket::ContentGroup* bundle_group)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
// Check if a call to SetLocalDescription is acceptable with a session
|
||||
// description of the given type.
|
||||
bool ExpectSetLocalDescription(SdpType type);
|
||||
// Check if a call to SetRemoteDescription is acceptable with a session
|
||||
// description of the given type.
|
||||
bool ExpectSetRemoteDescription(SdpType type);
|
||||
|
||||
// The offer/answer machinery assumes the media section MID is present and
|
||||
// unique. To support legacy end points that do not supply a=mid lines, this
|
||||
// method will modify the session description to add MIDs generated according
|
||||
// to the SDP semantics.
|
||||
void FillInMissingRemoteMids(cricket::SessionDescription* remote_description);
|
||||
|
||||
// Returns an RtpTransciever, if available, that can be used to receive the
|
||||
// given media type according to JSEP rules.
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
FindAvailableTransceiverToReceive(cricket::MediaType media_type) const;
|
||||
|
||||
// Returns a MediaSessionOptions struct with options decided by |options|,
|
||||
// the local MediaStreams and DataChannels.
|
||||
void GetOptionsForOffer(const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
void GetOptionsForPlanBOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void GetOptionsForUnifiedPlanOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
// Returns a MediaSessionOptions struct with options decided by
|
||||
// |constraints|, the local MediaStreams and DataChannels.
|
||||
void GetOptionsForAnswer(const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
void GetOptionsForPlanBAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void GetOptionsForUnifiedPlanAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions&
|
||||
offer_answer_options,
|
||||
cricket::MediaSessionOptions* session_options)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
// ===================================================================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user