Adding support for Unified Plan offer/answer negotiation to the mediasession layer.
This layer takes in a simplified "options" struct and the current local description,
and generates a new offer/answer. Previously the options struct assumed there would
only be one media description per media type (audio/video), but it now supports
N number of audio/video descriptions.
The |add_legacy_stream| options is removed from the mediasession.cc/.h
in this CL.
The next step is to add the ability for PeerConnection/WebRtcSession to create
"options" to represent multiple RtpTransceivers, and apply the Unified Plan
descriptions correctly. Right now, only Plan B descriptions will be
generated in unit tests.
BUG=chromium:465349
Review-Url: https://codereview.webrtc.org/2991693002
Cr-Commit-Position: refs/heads/master@{#19343}
diff --git a/webrtc/pc/mediasession.cc b/webrtc/pc/mediasession.cc
index de8f72c..4d4744a 100644
--- a/webrtc/pc/mediasession.cc
+++ b/webrtc/pc/mediasession.cc
@@ -188,11 +188,14 @@
return true;
}
-const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
- if (!media) {
- return NULL;
+const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
+ if (!content) {
+ return nullptr;
}
- return &media->cryptos();
+
+ RTC_DCHECK(IsMediaContent(content));
+ return &(static_cast<const MediaContentDescription*>(content->description)
+ ->cryptos());
}
bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
@@ -428,15 +431,15 @@
private:
};
-// Adds a StreamParams for each Stream in Streams with media type
-// media_type to content_description.
+// Adds a StreamParams for each SenderOptions in |sender_options| to
+// content_description.
// |current_params| - All currently known StreamParams of any media type.
template <class C>
-static bool AddStreamParams(MediaType media_type,
- const MediaSessionOptions& options,
- StreamParamsVec* current_streams,
- MediaContentDescriptionImpl<C>* content_description,
- const bool add_legacy_stream) {
+static bool AddStreamParams(
+ const std::vector<SenderOptions>& sender_options,
+ const std::string& rtcp_cname,
+ StreamParamsVec* current_streams,
+ MediaContentDescriptionImpl<C>* content_description) {
// SCTP streams are not negotiated using SDP/ContentDescriptions.
if (IsSctp(content_description->protocol())) {
return true;
@@ -445,44 +448,26 @@
const bool include_rtx_streams =
ContainsRtxCodec(content_description->codecs());
- const MediaSessionOptions::Streams& streams = options.streams;
- if (streams.empty() && add_legacy_stream) {
- // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
- std::vector<uint32_t> ssrcs;
- int num_ssrcs = include_rtx_streams ? 2 : 1;
- GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
- if (include_rtx_streams) {
- content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
- content_description->set_multistream(true);
- } else {
- content_description->AddLegacyStream(ssrcs[0]);
- }
- return true;
- }
const bool include_flexfec_stream =
ContainsFlexfecCodec(content_description->codecs());
- MediaSessionOptions::Streams::const_iterator stream_it;
- for (stream_it = streams.begin();
- stream_it != streams.end(); ++stream_it) {
- if (stream_it->type != media_type)
- continue; // Wrong media type.
-
- StreamParams* param = GetStreamByIds(*current_streams, "", stream_it->id);
+ for (const SenderOptions& sender : sender_options) {
// groupid is empty for StreamParams generated using
// MediaSessionDescriptionFactory.
+ StreamParams* param =
+ GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
if (!param) {
- // This is a new stream.
+ // This is a new sender.
std::vector<uint32_t> ssrcs;
- GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
+ GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs);
StreamParams stream_param;
- stream_param.id = stream_it->id;
+ stream_param.id = sender.track_id;
// Add the generated ssrc.
for (size_t i = 0; i < ssrcs.size(); ++i) {
stream_param.ssrcs.push_back(ssrcs[i]);
}
- if (stream_it->num_sim_layers > 1) {
+ if (sender.num_sim_layers > 1) {
SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
stream_param.ssrc_groups.push_back(group);
}
@@ -512,8 +497,8 @@
<< "media streams however, so no FlexFEC SSRC will be generated.";
}
}
- stream_param.cname = options.rtcp_cname;
- stream_param.sync_label = stream_it->sync_label;
+ stream_param.cname = rtcp_cname;
+ stream_param.sync_label = sender.stream_id;
content_description->AddStream(stream_param);
// Store the new StreamParams in current_streams.
@@ -523,7 +508,7 @@
// Use existing generated SSRCs/groups, but update the sync_label if
// necessary. This may be needed if a MediaStreamTrack was moved from one
// MediaStream to another.
- param->sync_label = stream_it->sync_label;
+ param->sync_label = sender.stream_id;
content_description->AddStream(*param);
}
}
@@ -735,46 +720,34 @@
return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
}
-static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
- const std::string& content_name) {
- TransportOptions transport_options;
- auto it = options.transport_options.find(content_name);
- if (it != options.transport_options.end()) {
- transport_options = it->second;
- }
- transport_options.enable_ice_renomination = options.enable_ice_renomination;
- return transport_options;
-}
-
-// Create a media content to be offered in a session-initiate,
-// according to the given options.rtcp_mux, options.is_muc,
-// options.streams, codecs, secure_transport, crypto, and streams. If we don't
-// currently have crypto (in current_cryptos) and it is enabled (in
-// secure_policy), crypto is created (according to crypto_suites). If
-// add_legacy_stream is true, and current_streams is empty, a legacy
-// stream is created. The created content is added to the offer.
+// Create a media content to be offered for the given |sender_options|,
+// according to the given options.rtcp_mux, session_options.is_muc, codecs,
+// secure_transport, crypto, and current_streams. If we don't currently have
+// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
+// created (according to crypto_suites). The created content is added to the
+// offer.
template <class C>
static bool CreateMediaContentOffer(
- const MediaSessionOptions& options,
+ const std::vector<SenderOptions>& sender_options,
+ const MediaSessionOptions& session_options,
const std::vector<C>& codecs,
const SecurePolicy& secure_policy,
const CryptoParamsVec* current_cryptos,
const std::vector<std::string>& crypto_suites,
const RtpHeaderExtensions& rtp_extensions,
- bool add_legacy_stream,
StreamParamsVec* current_streams,
MediaContentDescriptionImpl<C>* offer) {
offer->AddCodecs(codecs);
- offer->set_rtcp_mux(options.rtcp_mux_enabled);
+ offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
offer->set_rtcp_reduced_size(true);
}
- offer->set_multistream(options.is_muc);
+ offer->set_multistream(session_options.is_muc);
offer->set_rtp_header_extensions(rtp_extensions);
- if (!AddStreamParams(offer->type(), options, current_streams, offer,
- add_legacy_stream)) {
+ if (!AddStreamParams(sender_options, session_options.rtcp_cname,
+ current_streams, offer)) {
return false;
}
@@ -882,15 +855,42 @@
return false;
}
-// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
+// Find the codec in |codec_list| that |rtx_codec| is associated with.
+template <class C>
+static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
+ const C& rtx_codec) {
+ std::string associated_pt_str;
+ if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
+ &associated_pt_str)) {
+ LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
+ << " is missing an associated payload type.";
+ return nullptr;
+ }
+
+ int associated_pt;
+ if (!rtc::FromString(associated_pt_str, &associated_pt)) {
+ LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
+ << " of RTX codec " << rtx_codec.name << " to an integer.";
+ return nullptr;
+ }
+
+ // Find the associated reference codec for the reference RTX codec.
+ const C* associated_codec = FindCodecById(codec_list, associated_pt);
+ if (!associated_codec) {
+ LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
+ << associated_pt << " for RTX codec " << rtx_codec.name
+ << ".";
+ }
+ return associated_codec;
+}
+
+// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
// already exist in |offered_codecs| and ensure the payload types don't
// collide.
template <class C>
-static void FindCodecsToOffer(
- const std::vector<C>& reference_codecs,
- std::vector<C>* offered_codecs,
- UsedPayloadTypes* used_pltypes) {
-
+static void MergeCodecs(const std::vector<C>& reference_codecs,
+ std::vector<C>* offered_codecs,
+ UsedPayloadTypes* used_pltypes) {
// Add all new codecs that are not RTX codecs.
for (const C& reference_codec : reference_codecs) {
if (!IsRtxCodec(reference_codec) &&
@@ -908,33 +908,11 @@
!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
reference_codec, nullptr)) {
C rtx_codec = reference_codec;
-
- std::string associated_pt_str;
- if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
- &associated_pt_str)) {
- LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
- << " is missing an associated payload type.";
- continue;
- }
-
- int associated_pt;
- if (!rtc::FromString(associated_pt_str, &associated_pt)) {
- LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
- << " of RTX codec " << rtx_codec.name
- << " to an integer.";
- continue;
- }
-
- // Find the associated reference codec for the reference RTX codec.
const C* associated_codec =
- FindCodecById(reference_codecs, associated_pt);
+ GetAssociatedCodec(reference_codecs, rtx_codec);
if (!associated_codec) {
- LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
- << associated_pt << " for RTX codec " << rtx_codec.name
- << ".";
continue;
}
-
// Find a codec in the offered list that matches the reference codec.
// Its payload type may be different than the reference codec.
C matching_codec;
@@ -953,6 +931,22 @@
}
}
+static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
+ const webrtc::RtpExtension& ext_to_match,
+ webrtc::RtpExtension* found_extension) {
+ for (RtpHeaderExtensions::const_iterator it = extensions.begin();
+ it != extensions.end(); ++it) {
+ // We assume that all URIs are given in a canonical format.
+ if (it->uri == ext_to_match.uri && it->encrypt == ext_to_match.encrypt) {
+ if (found_extension) {
+ *found_extension = *it;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
static bool FindByUri(const RtpHeaderExtensions& extensions,
const webrtc::RtpExtension& ext_to_match,
webrtc::RtpExtension* found_extension) {
@@ -996,50 +990,41 @@
return false;
}
-// Iterates through |offered_extensions|, adding each one to
-// |regular_extensions| (or |encrypted_extensions| if encrypted) and |used_ids|,
-// and resolving ID conflicts.
-// If an offered extension has the same URI as one in |regular_extensions| or
-// |encrypted_extensions|, it will re-use the same ID and won't be treated as
-// a conflict.
-static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
- RtpHeaderExtensions* regular_extensions,
- RtpHeaderExtensions* encrypted_extensions,
- UsedRtpHeaderExtensionIds* used_ids) {
- for (auto& extension : *offered_extensions) {
- webrtc::RtpExtension existing;
- if ((extension.encrypt &&
- FindByUri(*encrypted_extensions, extension, &existing)) ||
- (!extension.encrypt &&
- FindByUri(*regular_extensions, extension, &existing))) {
- extension.id = existing.id;
- } else {
- used_ids->FindAndSetIdUsed(&extension);
- if (extension.encrypt) {
- encrypted_extensions->push_back(extension);
- } else {
- regular_extensions->push_back(extension);
- }
- }
- }
-}
-
-// Adds |reference_extensions| to |offered_extensions|, while updating
-// |all_extensions| and |used_ids|.
-static void FindRtpHdrExtsToOffer(
- const RtpHeaderExtensions& reference_extensions,
- RtpHeaderExtensions* offered_extensions,
- RtpHeaderExtensions* all_extensions,
- UsedRtpHeaderExtensionIds* used_ids) {
+// Adds all extensions from |reference_extensions| to |offered_extensions| that
+// don't already exist in |offered_extensions| and ensure the IDs don't
+// collide. If an extension is added, it's also added to |regular_extensions| or
+// |encrypted_extensions|, and if the extension is in |regular_extensions| or
+// |encrypted_extensions|, its ID is marked as used in |used_ids|.
+// |offered_extensions| is for either audio or video while |regular_extensions|
+// and |encrypted_extensions| are used for both audio and video. There could be
+// overlap between audio extensions and video extensions.
+static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
+ RtpHeaderExtensions* offered_extensions,
+ RtpHeaderExtensions* regular_extensions,
+ RtpHeaderExtensions* encrypted_extensions,
+ UsedRtpHeaderExtensionIds* used_ids) {
for (auto reference_extension : reference_extensions) {
- if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
+ if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
+ nullptr)) {
webrtc::RtpExtension existing;
- if (FindByUri(*all_extensions, reference_extension, &existing)) {
- offered_extensions->push_back(existing);
+ if (reference_extension.encrypt) {
+ if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
+ &existing)) {
+ offered_extensions->push_back(existing);
+ } else {
+ used_ids->FindAndSetIdUsed(&reference_extension);
+ encrypted_extensions->push_back(reference_extension);
+ offered_extensions->push_back(reference_extension);
+ }
} else {
- used_ids->FindAndSetIdUsed(&reference_extension);
- all_extensions->push_back(reference_extension);
- offered_extensions->push_back(reference_extension);
+ if (FindByUriAndEncryption(*regular_extensions, reference_extension,
+ &existing)) {
+ offered_extensions->push_back(existing);
+ } else {
+ used_ids->FindAndSetIdUsed(&reference_extension);
+ regular_extensions->push_back(reference_extension);
+ offered_extensions->push_back(reference_extension);
+ }
}
}
}
@@ -1103,26 +1088,24 @@
}
}
-// Create a media content to be answered in a session-accept,
-// according to the given options.rtcp_mux, options.streams, codecs,
-// crypto, and streams. If we don't currently have crypto (in
-// current_cryptos) and it is enabled (in secure_policy), crypto is
-// created (according to crypto_suites). If add_legacy_stream is
-// true, and current_streams is empty, a legacy stream is created.
-// The codecs, rtcp_mux, and crypto are all negotiated with the offer
-// from the incoming session-initiate. If the negotiation fails, this
-// method returns false. The created content is added to the offer.
+// Create a media content to be answered for the given |sender_options|
+// according to the given session_options.rtcp_mux, session_options.streams,
+// codecs, crypto, and current_streams. If we don't currently have crypto (in
+// current_cryptos) and it is enabled (in secure_policy), crypto is created
+// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
+// negotiated with the offer. If the negotiation fails, this method returns
+// false. The created content is added to the offer.
template <class C>
static bool CreateMediaContentAnswer(
const MediaContentDescriptionImpl<C>* offer,
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
const std::vector<C>& local_codecs,
const SecurePolicy& sdes_policy,
const CryptoParamsVec* current_cryptos,
const RtpHeaderExtensions& local_rtp_extenstions,
bool enable_encrypted_rtp_header_extensions,
StreamParamsVec* current_streams,
- bool add_legacy_stream,
bool bundle_enabled,
MediaContentDescriptionImpl<C>* answer) {
std::vector<C> negotiated_codecs;
@@ -1136,14 +1119,15 @@
&negotiated_rtp_extensions);
answer->set_rtp_header_extensions(negotiated_rtp_extensions);
- answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
+ answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
}
if (sdes_policy != SEC_DISABLED) {
CryptoParams crypto;
- if (SelectCrypto(offer, bundle_enabled, options.crypto_options, &crypto)) {
+ if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
+ &crypto)) {
if (current_cryptos) {
FindMatchingCrypto(*current_cryptos, crypto, &crypto);
}
@@ -1155,29 +1139,17 @@
return false;
}
- if (!AddStreamParams(answer->type(), options, current_streams, answer,
- add_legacy_stream)) {
+ if (!AddStreamParams(media_description_options.sender_options,
+ session_options.rtcp_cname, current_streams, answer)) {
return false; // Something went seriously wrong.
}
- // Make sure the answer media content direction is per default set as
- // described in RFC3264 section 6.1.
- const bool is_data = !IsRtpProtocol(answer->protocol());
- const bool has_send_streams = !answer->streams().empty();
- const bool wants_send = has_send_streams || is_data;
- const bool recv_audio =
- answer->type() == cricket::MEDIA_TYPE_AUDIO && options.recv_audio;
- const bool recv_video =
- answer->type() == cricket::MEDIA_TYPE_VIDEO && options.recv_video;
- const bool recv_data =
- answer->type() == cricket::MEDIA_TYPE_DATA;
- const bool wants_receive = recv_audio || recv_video || recv_data;
-
auto offer_rtd =
RtpTransceiverDirection::FromMediaContentDirection(offer->direction());
- auto wants_rtd = RtpTransceiverDirection(wants_send, wants_receive);
- answer->set_direction(NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd)
- .ToMediaContentDirection());
+
+ answer->set_direction(NegotiateRtpTransceiverDirection(
+ offer_rtd, media_description_options.direction)
+ .ToMediaContentDirection());
return true;
}
@@ -1239,23 +1211,20 @@
}
// Gets the current DTLS state from the transport description.
-static bool IsDtlsActive(
- const std::string& content_name,
- const SessionDescription* current_description) {
- if (!current_description)
+static bool IsDtlsActive(const ContentInfo* content,
+ const SessionDescription* current_description) {
+ if (!content) {
return false;
+ }
- const ContentInfo* content =
- current_description->GetContentByName(content_name);
- if (!content)
+ size_t msection_index = content - ¤t_description->contents()[0];
+
+ if (current_description->transport_infos().size() <= msection_index) {
return false;
+ }
- const TransportDescription* current_tdesc =
- GetTransportDescription(content_name, current_description);
- if (!current_tdesc)
- return false;
-
- return current_tdesc->secure();
+ return current_description->transport_infos()[msection_index]
+ .description.secure();
}
std::string MediaContentDirectionToString(MediaContentDirection direction) {
@@ -1281,75 +1250,54 @@
return dir_str;
}
-void MediaSessionOptions::AddSendStream(MediaType type,
- const std::string& id,
- const std::string& sync_label) {
- AddSendStreamInternal(type, id, sync_label, 1);
+void MediaDescriptionOptions::AddAudioSender(const std::string& track_id,
+ const std::string& stream_id) {
+ RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
+ AddSenderInternal(track_id, stream_id, 1);
}
-void MediaSessionOptions::AddSendVideoStream(
- const std::string& id,
- const std::string& sync_label,
- int num_sim_layers) {
- AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
+void MediaDescriptionOptions::AddVideoSender(const std::string& track_id,
+ const std::string& stream_id,
+ int num_sim_layers) {
+ RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
+ AddSenderInternal(track_id, stream_id, num_sim_layers);
}
-void MediaSessionOptions::AddSendStreamInternal(
- MediaType type,
- const std::string& id,
- const std::string& sync_label,
- int num_sim_layers) {
- streams.push_back(Stream(type, id, sync_label, num_sim_layers));
-
- // If we haven't already set the data_channel_type, and we add a
- // stream, we assume it's an RTP data stream.
- if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
- data_channel_type = DCT_RTP;
+void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
+ const std::string& stream_id) {
+ RTC_DCHECK(type == MEDIA_TYPE_DATA);
+ AddSenderInternal(track_id, stream_id, 1);
}
-void MediaSessionOptions::RemoveSendStream(MediaType type,
- const std::string& id) {
- Streams::iterator stream_it = streams.begin();
- for (; stream_it != streams.end(); ++stream_it) {
- if (stream_it->type == type && stream_it->id == id) {
- streams.erase(stream_it);
- return;
- }
- }
- RTC_NOTREACHED();
+void MediaDescriptionOptions::AddSenderInternal(const std::string& track_id,
+ const std::string& stream_id,
+ int num_sim_layers) {
+ sender_options.push_back(SenderOptions{track_id, stream_id, num_sim_layers});
}
-bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
- Streams::const_iterator stream_it = streams.begin();
- for (; stream_it != streams.end(); ++stream_it) {
- if (stream_it->type == type) {
- return true;
- }
- }
- return false;
+bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
+ return std::find_if(media_description_options.begin(),
+ media_description_options.end(),
+ [type](const MediaDescriptionOptions& t) {
+ return t.type == type;
+ }) != media_description_options.end();
}
MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
const TransportDescriptionFactory* transport_desc_factory)
- : secure_(SEC_DISABLED),
- add_legacy_(true),
- transport_desc_factory_(transport_desc_factory) {
-}
+ : transport_desc_factory_(transport_desc_factory) {}
MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
ChannelManager* channel_manager,
const TransportDescriptionFactory* transport_desc_factory)
- : secure_(SEC_DISABLED),
- add_legacy_(true),
- transport_desc_factory_(transport_desc_factory) {
+ : transport_desc_factory_(transport_desc_factory) {
channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
channel_manager->GetSupportedVideoCodecs(&video_codecs_);
channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
channel_manager->GetSupportedDataCodecs(&data_codecs_);
- NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
- &audio_sendrecv_codecs_);
+ ComputeAudioCodecsIntersectionAndUnion();
}
const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
@@ -1369,129 +1317,114 @@
const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
audio_send_codecs_ = send_codecs;
audio_recv_codecs_ = recv_codecs;
- audio_sendrecv_codecs_.clear();
- // Use NegotiateCodecs to merge our codec lists, since the operation is
- // essentially the same. Put send_codecs as the offered_codecs, which is the
- // order we'd like to follow. The reasoning is that encoding is usually more
- // expensive than decoding, and prioritizing a codec in the send list probably
- // means it's a codec we can handle efficiently.
- NegotiateCodecs(recv_codecs, send_codecs, &audio_sendrecv_codecs_);
+ ComputeAudioCodecsIntersectionAndUnion();
}
SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
- const MediaSessionOptions& options,
+ const MediaSessionOptions& session_options,
const SessionDescription* current_description) const {
std::unique_ptr<SessionDescription> offer(new SessionDescription());
StreamParamsVec current_streams;
GetCurrentStreamParams(current_description, ¤t_streams);
- const bool wants_send =
- options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
- const AudioCodecs& supported_audio_codecs =
- GetAudioCodecsForOffer({wants_send, options.recv_audio});
+ AudioCodecs offer_audio_codecs;
+ VideoCodecs offer_video_codecs;
+ DataCodecs offer_data_codecs;
+ GetCodecsForOffer(current_description, &offer_audio_codecs,
+ &offer_video_codecs, &offer_data_codecs);
- AudioCodecs audio_codecs;
- VideoCodecs video_codecs;
- DataCodecs data_codecs;
- GetCodecsToOffer(current_description, supported_audio_codecs,
- video_codecs_, data_codecs_,
- &audio_codecs, &video_codecs, &data_codecs);
-
- if (!options.vad_enabled) {
+ if (!session_options.vad_enabled) {
// If application doesn't want CN codecs in offer.
- StripCNCodecs(&audio_codecs);
+ StripCNCodecs(&offer_audio_codecs);
}
+ FilterDataCodecs(&offer_data_codecs,
+ session_options.data_channel_type == DCT_SCTP);
RtpHeaderExtensions audio_rtp_extensions;
RtpHeaderExtensions video_rtp_extensions;
GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
&video_rtp_extensions);
- bool audio_added = false;
- bool video_added = false;
- bool data_added = false;
-
- // Iterate through the contents of |current_description| to maintain the order
- // of the m-lines in the new offer.
+ // Must have options for each existing section.
if (current_description) {
- ContentInfos::const_iterator it = current_description->contents().begin();
- for (; it != current_description->contents().end(); ++it) {
- if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
- if (!AddAudioContentForOffer(options, current_description,
- audio_rtp_extensions, audio_codecs,
- ¤t_streams, offer.get())) {
- return NULL;
- }
- audio_added = true;
- } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
- if (!AddVideoContentForOffer(options, current_description,
- video_rtp_extensions, video_codecs,
- ¤t_streams, offer.get())) {
- return NULL;
- }
- video_added = true;
- } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
- MediaSessionOptions options_copy(options);
- if (IsSctp(static_cast<const MediaContentDescription*>(it->description)
- ->protocol())) {
- options_copy.data_channel_type = DCT_SCTP;
- }
- if (!AddDataContentForOffer(options_copy, current_description,
- &data_codecs, ¤t_streams,
- offer.get())) {
- return NULL;
- }
- data_added = true;
- } else {
- RTC_NOTREACHED();
- }
- }
+ RTC_DCHECK(current_description->contents().size() <=
+ session_options.media_description_options.size());
}
- // Append contents that are not in |current_description|.
- if (!audio_added && options.has_audio() &&
- !AddAudioContentForOffer(options, current_description,
- audio_rtp_extensions, audio_codecs,
- ¤t_streams, offer.get())) {
- return NULL;
- }
- if (!video_added && options.has_video() &&
- !AddVideoContentForOffer(options, current_description,
- video_rtp_extensions, video_codecs,
- ¤t_streams, offer.get())) {
- return NULL;
- }
- if (!data_added && options.has_data() &&
- !AddDataContentForOffer(options, current_description, &data_codecs,
- ¤t_streams, offer.get())) {
- return NULL;
+ // Iterate through the media description options, matching with existing media
+ // descriptions in |current_description|.
+ int msection_index = 0;
+ for (const MediaDescriptionOptions& media_description_options :
+ session_options.media_description_options) {
+ const ContentInfo* current_content = nullptr;
+ if (current_description &&
+ msection_index <
+ static_cast<int>(current_description->contents().size())) {
+ current_content = ¤t_description->contents()[msection_index];
+ // Media type must match.
+ RTC_DCHECK(IsMediaContentOfType(current_content,
+ media_description_options.type));
+ }
+ switch (media_description_options.type) {
+ case MEDIA_TYPE_AUDIO:
+ if (!AddAudioContentForOffer(media_description_options, session_options,
+ current_content, current_description,
+ audio_rtp_extensions, offer_audio_codecs,
+ ¤t_streams, offer.get())) {
+ return nullptr;
+ }
+ break;
+ case MEDIA_TYPE_VIDEO:
+ if (!AddVideoContentForOffer(media_description_options, session_options,
+ current_content, current_description,
+ video_rtp_extensions, offer_video_codecs,
+ ¤t_streams, offer.get())) {
+ return nullptr;
+ }
+ break;
+ case MEDIA_TYPE_DATA:
+ if (!AddDataContentForOffer(media_description_options, session_options,
+ current_content, current_description,
+ offer_data_codecs, ¤t_streams,
+ offer.get())) {
+ return nullptr;
+ }
+ break;
+ default:
+ RTC_NOTREACHED();
+ }
+ ++msection_index;
}
// Bundle the contents together, if we've been asked to do so, and update any
// parameters that need to be tweaked for BUNDLE.
- if (options.bundle_enabled) {
+ if (session_options.bundle_enabled && offer->contents().size() > 0u) {
ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
- for (ContentInfos::const_iterator content = offer->contents().begin();
- content != offer->contents().end(); ++content) {
- offer_bundle.AddContentName(content->name);
+ for (const ContentInfo& content : offer->contents()) {
+ // TODO(deadbeef): There are conditions that make bundling two media
+ // descriptions together illegal. For example, they use the same payload
+ // type to represent different codecs, or same IDs for different header
+ // extensions. We need to detect this and not try to bundle those media
+ // descriptions together.
+ offer_bundle.AddContentName(content.name);
}
offer->AddGroup(offer_bundle);
if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
- return NULL;
+ return nullptr;
}
if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
- return NULL;
+ return nullptr;
}
}
-
return offer.release();
}
SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
- const SessionDescription* offer, const MediaSessionOptions& options,
+ const SessionDescription* offer,
+ const MediaSessionOptions& session_options,
const SessionDescription* current_description) const {
if (!offer) {
return nullptr;
@@ -1511,32 +1444,81 @@
// Transport info shared by the bundle group.
std::unique_ptr<TransportInfo> bundle_transport;
- ContentInfos::const_iterator it = offer->contents().begin();
- for (; it != offer->contents().end(); ++it) {
- if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
- if (!AddAudioContentForAnswer(offer, options, current_description,
- bundle_transport.get(), ¤t_streams,
- answer.get())) {
- return NULL;
- }
- } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
- if (!AddVideoContentForAnswer(offer, options, current_description,
- bundle_transport.get(), ¤t_streams,
- answer.get())) {
- return NULL;
- }
- } else {
- RTC_DCHECK(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
- if (!AddDataContentForAnswer(offer, options, current_description,
- bundle_transport.get(), ¤t_streams,
- answer.get())) {
- return NULL;
- }
+ // Get list of all possible codecs that respects existing payload type
+ // mappings and uses a single payload type space.
+ //
+ // Note that these lists may be further filtered for each m= section; this
+ // step is done just to establish the payload type mappings shared by all
+ // sections.
+ AudioCodecs answer_audio_codecs;
+ VideoCodecs answer_video_codecs;
+ DataCodecs answer_data_codecs;
+ GetCodecsForAnswer(current_description, offer, &answer_audio_codecs,
+ &answer_video_codecs, &answer_data_codecs);
+
+ if (!session_options.vad_enabled) {
+ // If application doesn't want CN codecs in answer.
+ StripCNCodecs(&answer_audio_codecs);
+ }
+ FilterDataCodecs(&answer_data_codecs,
+ session_options.data_channel_type == DCT_SCTP);
+
+ // Must have options for exactly as many sections as in the offer.
+ RTC_DCHECK(offer->contents().size() ==
+ session_options.media_description_options.size());
+ // Iterate through the media description options, matching with existing
+ // media descriptions in |current_description|.
+ int msection_index = 0;
+ for (const MediaDescriptionOptions& media_description_options :
+ session_options.media_description_options) {
+ const ContentInfo* offer_content = &offer->contents()[msection_index];
+ // Media types and MIDs must match between the remote offer and the
+ // MediaDescriptionOptions.
+ RTC_DCHECK(
+ IsMediaContentOfType(offer_content, media_description_options.type));
+ RTC_DCHECK(media_description_options.mid == offer_content->name);
+ const ContentInfo* current_content = nullptr;
+ if (current_description &&
+ msection_index <
+ static_cast<int>(current_description->contents().size())) {
+ current_content = ¤t_description->contents()[msection_index];
}
+ switch (media_description_options.type) {
+ case MEDIA_TYPE_AUDIO:
+ if (!AddAudioContentForAnswer(
+ media_description_options, session_options, offer_content,
+ offer, current_content, current_description,
+ bundle_transport.get(), answer_audio_codecs, ¤t_streams,
+ answer.get())) {
+ return nullptr;
+ }
+ break;
+ case MEDIA_TYPE_VIDEO:
+ if (!AddVideoContentForAnswer(
+ media_description_options, session_options, offer_content,
+ offer, current_content, current_description,
+ bundle_transport.get(), answer_video_codecs, ¤t_streams,
+ answer.get())) {
+ return nullptr;
+ }
+ break;
+ case MEDIA_TYPE_DATA:
+ if (!AddDataContentForAnswer(media_description_options, session_options,
+ offer_content, offer, current_content,
+ current_description,
+ bundle_transport.get(), answer_data_codecs,
+ ¤t_streams, answer.get())) {
+ return nullptr;
+ }
+ break;
+ default:
+ RTC_NOTREACHED();
+ }
+ ++msection_index;
// See if we can add the newly generated m= section to the BUNDLE group in
// the answer.
ContentInfo& added = answer->contents().back();
- if (!added.rejected && options.bundle_enabled && offer_bundle &&
+ if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
offer_bundle->HasContentName(added.name)) {
answer_bundle.AddContentName(added.name);
bundle_transport.reset(
@@ -1596,11 +1578,37 @@
}
}
-void MediaSessionDescriptionFactory::GetCodecsToOffer(
+void MergeCodecsFromDescription(const SessionDescription* description,
+ AudioCodecs* audio_codecs,
+ VideoCodecs* video_codecs,
+ DataCodecs* data_codecs,
+ UsedPayloadTypes* used_pltypes) {
+ RTC_DCHECK(description);
+ for (const ContentInfo& content : description->contents()) {
+ if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
+ const AudioContentDescription* audio =
+ static_cast<AudioContentDescription*>(content.description);
+ MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
+ } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
+ const VideoContentDescription* video =
+ static_cast<VideoContentDescription*>(content.description);
+ MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
+ } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
+ const DataContentDescription* data =
+ static_cast<DataContentDescription*>(content.description);
+ MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
+ }
+ }
+}
+
+// Getting codecs for an offer involves these steps:
+//
+// 1. Construct payload type -> codec mappings for current description.
+// 2. Add any reference codecs that weren't already present
+// 3. For each individual media description (m= section), filter codecs based
+// on the directional attribute (happens in another method).
+void MediaSessionDescriptionFactory::GetCodecsForOffer(
const SessionDescription* current_description,
- const AudioCodecs& supported_audio_codecs,
- const VideoCodecs& supported_video_codecs,
- const DataCodecs& supported_data_codecs,
AudioCodecs* audio_codecs,
VideoCodecs* video_codecs,
DataCodecs* data_codecs) const {
@@ -1609,87 +1617,150 @@
video_codecs->clear();
data_codecs->clear();
-
// First - get all codecs from the current description if the media type
- // is used.
- // Add them to |used_pltypes| so the payloadtype is not reused if a new media
- // type is added.
+ // is used. Add them to |used_pltypes| so the payload type is not reused if a
+ // new media type is added.
if (current_description) {
- const AudioContentDescription* audio =
- GetFirstAudioContentDescription(current_description);
- if (audio) {
- *audio_codecs = audio->codecs();
- used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
- }
- const VideoContentDescription* video =
- GetFirstVideoContentDescription(current_description);
- if (video) {
- *video_codecs = video->codecs();
- used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
- }
- const DataContentDescription* data =
- GetFirstDataContentDescription(current_description);
- if (data) {
- *data_codecs = data->codecs();
- used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
- }
+ MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
+ data_codecs, &used_pltypes);
}
// Add our codecs that are not in |current_description|.
- FindCodecsToOffer<AudioCodec>(supported_audio_codecs, audio_codecs,
- &used_pltypes);
- FindCodecsToOffer<VideoCodec>(supported_video_codecs, video_codecs,
- &used_pltypes);
- FindCodecsToOffer<DataCodec>(supported_data_codecs, data_codecs,
- &used_pltypes);
+ MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
+ MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
+ MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
+}
+
+// Getting codecs for an answer involves these steps:
+//
+// 1. Construct payload type -> codec mappings for current description.
+// 2. Add any codecs from the offer that weren't already present.
+// 3. Add any remaining codecs that weren't already present.
+// 4. For each individual media description (m= section), filter codecs based
+// on the directional attribute (happens in another method).
+void MediaSessionDescriptionFactory::GetCodecsForAnswer(
+ const SessionDescription* current_description,
+ const SessionDescription* remote_offer,
+ AudioCodecs* audio_codecs,
+ VideoCodecs* video_codecs,
+ DataCodecs* data_codecs) const {
+ UsedPayloadTypes used_pltypes;
+ audio_codecs->clear();
+ video_codecs->clear();
+ data_codecs->clear();
+
+ // First - get all codecs from the current description if the media type
+ // is used. Add them to |used_pltypes| so the payload type is not reused if a
+ // new media type is added.
+ if (current_description) {
+ MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
+ data_codecs, &used_pltypes);
+ }
+
+ // Second - filter out codecs that we don't support at all and should ignore.
+ AudioCodecs filtered_offered_audio_codecs;
+ VideoCodecs filtered_offered_video_codecs;
+ DataCodecs filtered_offered_data_codecs;
+ for (const ContentInfo& content : remote_offer->contents()) {
+ if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
+ const AudioContentDescription* audio =
+ static_cast<AudioContentDescription*>(content.description);
+ for (const AudioCodec& offered_audio_codec : audio->codecs()) {
+ if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
+ filtered_offered_audio_codecs,
+ offered_audio_codec, nullptr) &&
+ FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
+ offered_audio_codec, nullptr)) {
+ filtered_offered_audio_codecs.push_back(offered_audio_codec);
+ }
+ }
+ } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
+ const VideoContentDescription* video =
+ static_cast<VideoContentDescription*>(content.description);
+ for (const VideoCodec& offered_video_codec : video->codecs()) {
+ if (!FindMatchingCodec<VideoCodec>(video->codecs(),
+ filtered_offered_video_codecs,
+ offered_video_codec, nullptr) &&
+ FindMatchingCodec<VideoCodec>(video->codecs(), video_codecs_,
+ offered_video_codec, nullptr)) {
+ filtered_offered_video_codecs.push_back(offered_video_codec);
+ }
+ }
+ } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
+ const DataContentDescription* data =
+ static_cast<DataContentDescription*>(content.description);
+ for (const DataCodec& offered_data_codec : data->codecs()) {
+ if (!FindMatchingCodec<DataCodec>(data->codecs(),
+ filtered_offered_data_codecs,
+ offered_data_codec, nullptr) &&
+ FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
+ offered_data_codec, nullptr)) {
+ filtered_offered_data_codecs.push_back(offered_data_codec);
+ }
+ }
+ }
+ }
+
+ // Add codecs that are not in |current_description| but were in
+ // |remote_offer|.
+ MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
+ &used_pltypes);
+ MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
+ &used_pltypes);
+ MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
+ &used_pltypes);
}
void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
const SessionDescription* current_description,
- RtpHeaderExtensions* audio_extensions,
- RtpHeaderExtensions* video_extensions) const {
+ RtpHeaderExtensions* offer_audio_extensions,
+ RtpHeaderExtensions* offer_video_extensions) const {
// All header extensions allocated from the same range to avoid potential
// issues when using BUNDLE.
UsedRtpHeaderExtensionIds used_ids;
RtpHeaderExtensions all_regular_extensions;
RtpHeaderExtensions all_encrypted_extensions;
- audio_extensions->clear();
- video_extensions->clear();
+ offer_audio_extensions->clear();
+ offer_video_extensions->clear();
// First - get all extensions from the current description if the media type
// is used.
// Add them to |used_ids| so the local ids are not reused if a new media
// type is added.
if (current_description) {
- const AudioContentDescription* audio =
- GetFirstAudioContentDescription(current_description);
- if (audio) {
- *audio_extensions = audio->rtp_header_extensions();
- FindAndSetRtpHdrExtUsed(audio_extensions, &all_regular_extensions,
- &all_encrypted_extensions, &used_ids);
- }
- const VideoContentDescription* video =
- GetFirstVideoContentDescription(current_description);
- if (video) {
- *video_extensions = video->rtp_header_extensions();
- FindAndSetRtpHdrExtUsed(video_extensions, &all_regular_extensions,
- &all_encrypted_extensions, &used_ids);
+ for (const ContentInfo& content : current_description->contents()) {
+ if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
+ const AudioContentDescription* audio =
+ static_cast<const AudioContentDescription*>(content.description);
+ MergeRtpHdrExts(audio->rtp_header_extensions(), offer_audio_extensions,
+ &all_regular_extensions, &all_encrypted_extensions,
+ &used_ids);
+ } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
+ const VideoContentDescription* video =
+ static_cast<const VideoContentDescription*>(content.description);
+ MergeRtpHdrExts(video->rtp_header_extensions(), offer_video_extensions,
+ &all_regular_extensions, &all_encrypted_extensions,
+ &used_ids);
+ }
}
}
// Add our default RTP header extensions that are not in
// |current_description|.
- FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
- &all_regular_extensions, &used_ids);
- FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
- &all_regular_extensions, &used_ids);
+ MergeRtpHdrExts(audio_rtp_header_extensions(), offer_audio_extensions,
+ &all_regular_extensions, &all_encrypted_extensions,
+ &used_ids);
+ MergeRtpHdrExts(video_rtp_header_extensions(), offer_video_extensions,
+ &all_regular_extensions, &all_encrypted_extensions,
+ &used_ids);
+
// TODO(jbauch): Support adding encrypted header extensions to existing
// sessions.
if (enable_encrypted_rtp_header_extensions_ && !current_description) {
- AddEncryptedVersionsOfHdrExts(audio_extensions, &all_encrypted_extensions,
- &used_ids);
- AddEncryptedVersionsOfHdrExts(video_extensions, &all_encrypted_extensions,
- &used_ids);
+ AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
+ &all_encrypted_extensions, &used_ids);
+ AddEncryptedVersionsOfHdrExts(offer_video_extensions,
+ &all_encrypted_extensions, &used_ids);
}
}
@@ -1743,35 +1814,71 @@
return true;
}
+// |audio_codecs| = set of all possible codecs that can be used, with correct
+// payload type mappings
+//
+// |supported_audio_codecs| = set of codecs that are supported for the direction
+// of this m= section
+//
+// acd->codecs() = set of previously negotiated codecs for this m= section
+//
+// The payload types should come from audio_codecs, but the order should come
+// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
+// change existing codec priority, and that new codecs are added with the right
+// priority.
bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
const RtpHeaderExtensions& audio_rtp_extensions,
const AudioCodecs& audio_codecs,
StreamParamsVec* current_streams,
SessionDescription* desc) const {
- const ContentInfo* current_audio_content =
- GetFirstAudioContent(current_description);
- std::string content_name =
- current_audio_content ? current_audio_content->name : CN_AUDIO;
+ // Filter audio_codecs (which includes all codecs, with correctly remapped
+ // payload types) based on transceiver direction.
+ const AudioCodecs& supported_audio_codecs =
+ GetAudioCodecsForOffer(media_description_options.direction);
+
+ AudioCodecs filtered_codecs;
+ // Add the codecs from current content if exists.
+ if (current_content) {
+ RTC_DCHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
+ const AudioContentDescription* acd =
+ static_cast<const AudioContentDescription*>(
+ current_content->description);
+ for (const AudioCodec& codec : acd->codecs()) {
+ if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
+ codec, nullptr)) {
+ filtered_codecs.push_back(codec);
+ }
+ }
+ }
+ // Add other supported audio codecs.
+ AudioCodec found_codec;
+ for (const AudioCodec& codec : supported_audio_codecs) {
+ if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
+ codec, &found_codec) &&
+ !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
+ codec, nullptr)) {
+ // Use the |found_codec| from |audio_codecs| because it has the correctly
+ // mapped payload type.
+ filtered_codecs.push_back(found_codec);
+ }
+ }
cricket::SecurePolicy sdes_policy =
- IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
- : secure();
+ IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
+ : secure();
std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
std::vector<std::string> crypto_suites;
- GetSupportedAudioSdesCryptoSuiteNames(options.crypto_options, &crypto_suites);
+ GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
+ &crypto_suites);
if (!CreateMediaContentOffer(
- options,
- audio_codecs,
- sdes_policy,
- GetCryptos(GetFirstAudioContentDescription(current_description)),
- crypto_suites,
- audio_rtp_extensions,
- add_legacy_,
- current_streams,
- audio.get())) {
+ media_description_options.sender_options, session_options,
+ filtered_codecs, sdes_policy, GetCryptos(current_content),
+ crypto_suites, audio_rtp_extensions, current_streams, audio.get())) {
return false;
}
audio->set_lang(lang_);
@@ -1779,13 +1886,13 @@
bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
SetMediaProtocol(secure_transport, audio.get());
- auto offer_rtd =
- RtpTransceiverDirection(!audio->streams().empty(), options.recv_audio);
- audio->set_direction(offer_rtd.ToMediaContentDirection());
+ audio->set_direction(
+ media_description_options.direction.ToMediaContentDirection());
- desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
- if (!AddTransportOffer(content_name,
- GetTransportOptions(options, content_name),
+ desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
+ media_description_options.stopped, audio.release());
+ if (!AddTransportOffer(media_description_options.mid,
+ media_description_options.transport_options,
current_description, desc)) {
return false;
}
@@ -1794,87 +1901,98 @@
}
bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
const RtpHeaderExtensions& video_rtp_extensions,
const VideoCodecs& video_codecs,
StreamParamsVec* current_streams,
SessionDescription* desc) const {
- const ContentInfo* current_video_content =
- GetFirstVideoContent(current_description);
- std::string content_name =
- current_video_content ? current_video_content->name : CN_VIDEO;
-
cricket::SecurePolicy sdes_policy =
- IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
- : secure();
+ IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
+ : secure();
std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
std::vector<std::string> crypto_suites;
- GetSupportedVideoSdesCryptoSuiteNames(options.crypto_options, &crypto_suites);
+ GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
+ &crypto_suites);
+
+ VideoCodecs filtered_codecs;
+ // Add the codecs from current content if exists.
+ if (current_content) {
+ RTC_DCHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
+ const VideoContentDescription* vcd =
+ static_cast<const VideoContentDescription*>(
+ current_content->description);
+ for (const VideoCodec& codec : vcd->codecs()) {
+ if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
+ nullptr)) {
+ filtered_codecs.push_back(codec);
+ }
+ }
+ }
+ // Add other supported video codecs.
+ VideoCodec found_codec;
+ for (const VideoCodec& codec : video_codecs_) {
+ if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
+ &found_codec) &&
+ !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
+ nullptr)) {
+ // Use the |found_codec| from |video_codecs| because it has the correctly
+ // mapped payload type.
+ filtered_codecs.push_back(found_codec);
+ }
+ }
+
if (!CreateMediaContentOffer(
- options,
- video_codecs,
- sdes_policy,
- GetCryptos(GetFirstVideoContentDescription(current_description)),
- crypto_suites,
- video_rtp_extensions,
- add_legacy_,
- current_streams,
- video.get())) {
+ media_description_options.sender_options, session_options,
+ filtered_codecs, sdes_policy, GetCryptos(current_content),
+ crypto_suites, video_rtp_extensions, current_streams, video.get())) {
return false;
}
- video->set_bandwidth(options.video_bandwidth);
+ video->set_bandwidth(kAutoBandwidth);
bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
SetMediaProtocol(secure_transport, video.get());
- if (!video->streams().empty()) {
- if (options.recv_video) {
- video->set_direction(MD_SENDRECV);
- } else {
- video->set_direction(MD_SENDONLY);
- }
- } else {
- if (options.recv_video) {
- video->set_direction(MD_RECVONLY);
- } else {
- video->set_direction(MD_INACTIVE);
- }
- }
+ video->set_direction(
+ media_description_options.direction.ToMediaContentDirection());
- desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
- if (!AddTransportOffer(content_name,
- GetTransportOptions(options, content_name),
+ desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
+ media_description_options.stopped, video.release());
+ if (!AddTransportOffer(media_description_options.mid,
+ media_description_options.transport_options,
current_description, desc)) {
return false;
}
-
return true;
}
bool MediaSessionDescriptionFactory::AddDataContentForOffer(
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
- DataCodecs* data_codecs,
+ const DataCodecs& data_codecs,
StreamParamsVec* current_streams,
SessionDescription* desc) const {
bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
std::unique_ptr<DataContentDescription> data(new DataContentDescription());
- bool is_sctp = (options.data_channel_type == DCT_SCTP);
-
- FilterDataCodecs(data_codecs, is_sctp);
-
- const ContentInfo* current_data_content =
- GetFirstDataContent(current_description);
- std::string content_name =
- current_data_content ? current_data_content->name : CN_DATA;
+ bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
+ // If the DataChannel type is not specified, use the DataChannel type in
+ // the current description.
+ if (session_options.data_channel_type == DCT_NONE && current_content) {
+ is_sctp = (static_cast<const DataContentDescription*>(
+ current_content->description)
+ ->protocol() == kMediaProtocolSctp);
+ }
cricket::SecurePolicy sdes_policy =
- IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
- : secure();
+ IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
+ : secure();
std::vector<std::string> crypto_suites;
if (is_sctp) {
// SDES doesn't make sense for SCTP, so we disable it, and we only
@@ -1890,227 +2008,279 @@
data->set_protocol(
secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
} else {
- GetSupportedDataSdesCryptoSuiteNames(options.crypto_options,
+ GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
&crypto_suites);
}
+ // Even SCTP uses a "codec".
if (!CreateMediaContentOffer(
- options,
- *data_codecs,
- sdes_policy,
- GetCryptos(GetFirstDataContentDescription(current_description)),
- crypto_suites,
- RtpHeaderExtensions(),
- add_legacy_,
- current_streams,
- data.get())) {
+ media_description_options.sender_options, session_options,
+ data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites,
+ RtpHeaderExtensions(), current_streams, data.get())) {
return false;
}
if (is_sctp) {
- desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
+ desc->AddContent(media_description_options.mid, NS_JINGLE_DRAFT_SCTP,
+ data.release());
} else {
- data->set_bandwidth(options.data_bandwidth);
+ data->set_bandwidth(kDataMaxBandwidth);
SetMediaProtocol(secure_transport, data.get());
- desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
+ desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
+ media_description_options.stopped, data.release());
}
- if (!AddTransportOffer(content_name,
- GetTransportOptions(options, content_name),
+ if (!AddTransportOffer(media_description_options.mid,
+ media_description_options.transport_options,
current_description, desc)) {
return false;
}
return true;
}
+// |audio_codecs| = set of all possible codecs that can be used, with correct
+// payload type mappings
+//
+// |supported_audio_codecs| = set of codecs that are supported for the direction
+// of this m= section
+//
+// acd->codecs() = set of previously negotiated codecs for this m= section
+//
+// The payload types should come from audio_codecs, but the order should come
+// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
+// change existing codec priority, and that new codecs are added with the right
+// priority.
bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
- const SessionDescription* offer,
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* offer_content,
+ const SessionDescription* offer_description,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
const TransportInfo* bundle_transport,
+ const AudioCodecs& audio_codecs,
StreamParamsVec* current_streams,
SessionDescription* answer) const {
- const ContentInfo* audio_content = GetFirstAudioContent(offer);
- const AudioContentDescription* offer_audio =
- static_cast<const AudioContentDescription*>(audio_content->description);
+ const AudioContentDescription* offer_audio_description =
+ static_cast<const AudioContentDescription*>(offer_content->description);
std::unique_ptr<TransportDescription> audio_transport(
- CreateTransportAnswer(audio_content->name, offer,
- GetTransportOptions(options, audio_content->name),
+ CreateTransportAnswer(media_description_options.mid, offer_description,
+ media_description_options.transport_options,
current_description, bundle_transport != nullptr));
if (!audio_transport) {
return false;
}
- // Pick codecs based on the requested communications direction in the offer.
- const bool wants_send =
- options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
- auto wants_rtd = RtpTransceiverDirection(wants_send, options.recv_audio);
- auto offer_rtd =
- RtpTransceiverDirection::FromMediaContentDirection(
- offer_audio->direction());
+ // Pick codecs based on the requested communications direction in the offer
+ // and the selected direction in the answer.
+ // Note these will be filtered one final time in CreateMediaContentAnswer.
+ auto wants_rtd = media_description_options.direction;
+ auto offer_rtd = RtpTransceiverDirection::FromMediaContentDirection(
+ offer_audio_description->direction());
auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
- AudioCodecs audio_codecs = GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
- if (!options.vad_enabled) {
- StripCNCodecs(&audio_codecs);
+ AudioCodecs supported_audio_codecs =
+ GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
+
+ AudioCodecs filtered_codecs;
+ // Add the codecs from current content if exists.
+ if (current_content) {
+ RTC_DCHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
+ const AudioContentDescription* acd =
+ static_cast<const AudioContentDescription*>(
+ current_content->description);
+ for (const AudioCodec& codec : acd->codecs()) {
+ if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
+ codec, nullptr)) {
+ filtered_codecs.push_back(codec);
+ }
+ }
+ }
+ // Add other supported audio codecs.
+ AudioCodec found_codec;
+ for (const AudioCodec& codec : supported_audio_codecs) {
+ if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
+ codec, &found_codec) &&
+ !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
+ codec, nullptr)) {
+ // Use the |found_codec| from |audio_codecs| because it has the correctly
+ // mapped payload type.
+ filtered_codecs.push_back(found_codec);
+ }
}
- bool bundle_enabled =
- offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
+ bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
+ session_options.bundle_enabled;
std::unique_ptr<AudioContentDescription> audio_answer(
new AudioContentDescription());
// Do not require or create SDES cryptos if DTLS is used.
cricket::SecurePolicy sdes_policy =
audio_transport->secure() ? cricket::SEC_DISABLED : secure();
if (!CreateMediaContentAnswer(
- offer_audio,
- options,
- audio_codecs,
- sdes_policy,
- GetCryptos(GetFirstAudioContentDescription(current_description)),
- audio_rtp_extensions_,
- enable_encrypted_rtp_header_extensions_,
- current_streams,
- add_legacy_,
- bundle_enabled,
- audio_answer.get())) {
+ offer_audio_description, media_description_options, session_options,
+ filtered_codecs, sdes_policy, GetCryptos(current_content),
+ audio_rtp_extensions_, enable_encrypted_rtp_header_extensions_,
+ current_streams, bundle_enabled, audio_answer.get())) {
return false; // Fails the session setup.
}
bool secure = bundle_transport ? bundle_transport->description.secure()
: audio_transport->secure();
- bool rejected = !options.has_audio() || audio_content->rejected ||
+ bool rejected = media_description_options.stopped ||
+ offer_content->rejected ||
!IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
audio_answer->protocol(), secure);
if (!rejected) {
- AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
+ AddTransportAnswer(media_description_options.mid, *(audio_transport.get()),
+ answer);
} else {
- // RFC 3264
- // The answer MUST contain the same number of m-lines as the offer.
- LOG(LS_INFO) << "Audio is not supported in the answer.";
+ LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
+ << "' being rejected in answer.";
}
- answer->AddContent(audio_content->name, audio_content->type, rejected,
- audio_answer.release());
+ answer->AddContent(media_description_options.mid, offer_content->type,
+ rejected, audio_answer.release());
return true;
}
bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
- const SessionDescription* offer,
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* offer_content,
+ const SessionDescription* offer_description,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
const TransportInfo* bundle_transport,
+ const VideoCodecs& video_codecs,
StreamParamsVec* current_streams,
SessionDescription* answer) const {
- const ContentInfo* video_content = GetFirstVideoContent(offer);
+ const VideoContentDescription* offer_video_description =
+ static_cast<const VideoContentDescription*>(offer_content->description);
+
std::unique_ptr<TransportDescription> video_transport(
- CreateTransportAnswer(video_content->name, offer,
- GetTransportOptions(options, video_content->name),
+ CreateTransportAnswer(media_description_options.mid, offer_description,
+ media_description_options.transport_options,
current_description, bundle_transport != nullptr));
if (!video_transport) {
return false;
}
+ VideoCodecs filtered_codecs;
+ // Add the codecs from current content if exists.
+ if (current_content) {
+ RTC_DCHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
+ const VideoContentDescription* vcd =
+ static_cast<const VideoContentDescription*>(
+ current_content->description);
+ for (const VideoCodec& codec : vcd->codecs()) {
+ if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
+ nullptr)) {
+ filtered_codecs.push_back(codec);
+ }
+ }
+ }
+ // Add other supported video codecs.
+ VideoCodec found_codec;
+ for (const VideoCodec& codec : video_codecs_) {
+ if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
+ &found_codec) &&
+ !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
+ nullptr)) {
+ // Use the |found_codec| from |video_codecs| because it has the correctly
+ // mapped payload type.
+ filtered_codecs.push_back(found_codec);
+ }
+ }
+
+ bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
+ session_options.bundle_enabled;
+
std::unique_ptr<VideoContentDescription> video_answer(
new VideoContentDescription());
// Do not require or create SDES cryptos if DTLS is used.
cricket::SecurePolicy sdes_policy =
video_transport->secure() ? cricket::SEC_DISABLED : secure();
- bool bundle_enabled =
- offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
if (!CreateMediaContentAnswer(
- static_cast<const VideoContentDescription*>(
- video_content->description),
- options,
- video_codecs_,
- sdes_policy,
- GetCryptos(GetFirstVideoContentDescription(current_description)),
- video_rtp_extensions_,
- enable_encrypted_rtp_header_extensions_,
- current_streams,
- add_legacy_,
- bundle_enabled,
- video_answer.get())) {
- return false;
+ offer_video_description, media_description_options, session_options,
+ filtered_codecs, sdes_policy, GetCryptos(current_content),
+ video_rtp_extensions_, enable_encrypted_rtp_header_extensions_,
+ current_streams, bundle_enabled, video_answer.get())) {
+ return false; // Failed the sessin setup.
}
bool secure = bundle_transport ? bundle_transport->description.secure()
: video_transport->secure();
- bool rejected = !options.has_video() || video_content->rejected ||
+ bool rejected = media_description_options.stopped ||
+ offer_content->rejected ||
!IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
video_answer->protocol(), secure);
if (!rejected) {
- if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
- answer)) {
+ if (!AddTransportAnswer(media_description_options.mid,
+ *(video_transport.get()), answer)) {
return false;
}
- video_answer->set_bandwidth(options.video_bandwidth);
+ video_answer->set_bandwidth(kAutoBandwidth);
} else {
- // RFC 3264
- // The answer MUST contain the same number of m-lines as the offer.
- LOG(LS_INFO) << "Video is not supported in the answer.";
+ LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
+ << "' being rejected in answer.";
}
- answer->AddContent(video_content->name, video_content->type, rejected,
- video_answer.release());
+ answer->AddContent(media_description_options.mid, offer_content->type,
+ rejected, video_answer.release());
return true;
}
bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
- const SessionDescription* offer,
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* offer_content,
+ const SessionDescription* offer_description,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
const TransportInfo* bundle_transport,
+ const DataCodecs& data_codecs,
StreamParamsVec* current_streams,
SessionDescription* answer) const {
- const ContentInfo* data_content = GetFirstDataContent(offer);
std::unique_ptr<TransportDescription> data_transport(
- CreateTransportAnswer(data_content->name, offer,
- GetTransportOptions(options, data_content->name),
+ CreateTransportAnswer(media_description_options.mid, offer_description,
+ media_description_options.transport_options,
current_description, bundle_transport != nullptr));
if (!data_transport) {
return false;
}
- bool is_sctp = (options.data_channel_type == DCT_SCTP);
- std::vector<DataCodec> data_codecs(data_codecs_);
- FilterDataCodecs(&data_codecs, is_sctp);
std::unique_ptr<DataContentDescription> data_answer(
new DataContentDescription());
// Do not require or create SDES cryptos if DTLS is used.
cricket::SecurePolicy sdes_policy =
data_transport->secure() ? cricket::SEC_DISABLED : secure();
- bool bundle_enabled =
- offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
+ bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
+ session_options.bundle_enabled;
if (!CreateMediaContentAnswer(
static_cast<const DataContentDescription*>(
- data_content->description),
- options,
- data_codecs_,
- sdes_policy,
- GetCryptos(GetFirstDataContentDescription(current_description)),
- RtpHeaderExtensions(),
- enable_encrypted_rtp_header_extensions_,
- current_streams,
- add_legacy_,
- bundle_enabled,
- data_answer.get())) {
+ offer_content->description),
+ media_description_options, session_options, data_codecs, sdes_policy,
+ GetCryptos(current_content), RtpHeaderExtensions(),
+ enable_encrypted_rtp_header_extensions_, current_streams,
+ bundle_enabled, data_answer.get())) {
return false; // Fails the session setup.
}
// Respond with sctpmap if the offer uses sctpmap.
const DataContentDescription* offer_data_description =
- static_cast<const DataContentDescription*>(data_content->description);
+ static_cast<const DataContentDescription*>(offer_content->description);
bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
data_answer->set_use_sctpmap(offer_uses_sctpmap);
bool secure = bundle_transport ? bundle_transport->description.secure()
: data_transport->secure();
- bool rejected = !options.has_data() || data_content->rejected ||
+ bool rejected = session_options.data_channel_type == DCT_NONE ||
+ media_description_options.stopped ||
+ offer_content->rejected ||
!IsMediaProtocolSupported(MEDIA_TYPE_DATA,
data_answer->protocol(), secure);
if (!rejected) {
- data_answer->set_bandwidth(options.data_bandwidth);
- if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
- answer)) {
+ data_answer->set_bandwidth(kDataMaxBandwidth);
+ if (!AddTransportAnswer(media_description_options.mid,
+ *(data_transport.get()), answer)) {
return false;
}
} else {
@@ -2118,11 +2288,39 @@
// The answer MUST contain the same number of m-lines as the offer.
LOG(LS_INFO) << "Data is not supported in the answer.";
}
- answer->AddContent(data_content->name, data_content->type, rejected,
- data_answer.release());
+ answer->AddContent(media_description_options.mid, offer_content->type,
+ rejected, data_answer.release());
return true;
}
+void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
+ audio_sendrecv_codecs_.clear();
+ all_audio_codecs_.clear();
+ // Compute the audio codecs union.
+ for (const AudioCodec& send : audio_send_codecs_) {
+ all_audio_codecs_.push_back(send);
+ if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
+ send, nullptr)) {
+ // It doesn't make sense to have an RTX codec we support sending but not
+ // receiving.
+ RTC_DCHECK(!IsRtxCodec(send));
+ }
+ }
+ for (const AudioCodec& recv : audio_recv_codecs_) {
+ if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
+ recv, nullptr)) {
+ all_audio_codecs_.push_back(recv);
+ }
+ }
+ // Use NegotiateCodecs to merge our codec lists, since the operation is
+ // essentially the same. Put send_codecs as the offered_codecs, which is the
+ // order we'd like to follow. The reasoning is that encoding is usually more
+ // expensive than decoding, and prioritizing a codec in the send list probably
+ // means it's a codec we can handle efficiently.
+ NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
+ &audio_sendrecv_codecs_);
+}
+
bool IsMediaContent(const ContentInfo* content) {
return (content &&
(content->type == NS_JINGLE_RTP ||
diff --git a/webrtc/pc/mediasession.h b/webrtc/pc/mediasession.h
index 596bd18..35bd444 100644
--- a/webrtc/pc/mediasession.h
+++ b/webrtc/pc/mediasession.h
@@ -102,83 +102,72 @@
NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
RtpTransceiverDirection wants);
-struct MediaSessionOptions {
- MediaSessionOptions()
- : recv_audio(true),
- recv_video(false),
- data_channel_type(DCT_NONE),
- is_muc(false),
- vad_enabled(true), // When disabled, removes all CN codecs from SDP.
- rtcp_mux_enabled(true),
- bundle_enabled(false),
- video_bandwidth(kAutoBandwidth),
- data_bandwidth(kDataMaxBandwidth),
- rtcp_cname(kDefaultRtcpCname) {}
+// Options for an RtpSender contained with an media description/"m=" section.
+struct SenderOptions {
+ std::string track_id;
+ std::string stream_id;
+ int num_sim_layers;
+};
- bool has_audio() const {
- return recv_audio || HasSendMediaStream(MEDIA_TYPE_AUDIO);
- }
- bool has_video() const {
- return recv_video || HasSendMediaStream(MEDIA_TYPE_VIDEO);
- }
- bool has_data() const { return data_channel_type != DCT_NONE; }
+// Options for an individual media description/"m=" section.
+struct MediaDescriptionOptions {
+ MediaDescriptionOptions(MediaType type,
+ const std::string& mid,
+ RtpTransceiverDirection direction,
+ bool stopped)
+ : type(type), mid(mid), direction(direction), stopped(stopped) {}
- // Add a stream with MediaType type and id.
- // All streams with the same sync_label will get the same CNAME.
- // All ids must be unique.
- void AddSendStream(MediaType type,
- const std::string& id,
- const std::string& sync_label);
- void AddSendVideoStream(const std::string& id,
- const std::string& sync_label,
+ // TODO(deadbeef): When we don't support Plan B, there will only be one
+ // sender per media description and this can be simplified.
+ void AddAudioSender(const std::string& track_id,
+ const std::string& stream_id);
+ void AddVideoSender(const std::string& track_id,
+ const std::string& stream_id,
int num_sim_layers);
- void RemoveSendStream(MediaType type, const std::string& id);
+ // Internally just uses sender_options.
+ void AddRtpDataChannel(const std::string& track_id,
+ const std::string& stream_id);
- // Helper function.
- void AddSendStreamInternal(MediaType type,
- const std::string& id,
- const std::string& sync_label,
+ MediaType type;
+ std::string mid;
+ RtpTransceiverDirection direction;
+ bool stopped;
+ TransportOptions transport_options;
+ // Note: There's no equivalent "RtpReceiverOptions" because only send
+ // stream information goes in the local descriptions.
+ std::vector<SenderOptions> sender_options;
+
+ private:
+ // Doesn't DCHECK on |type|.
+ void AddSenderInternal(const std::string& track_id,
+ const std::string& stream_id,
int num_sim_layers);
+};
- bool HasSendMediaStream(MediaType type) const;
+// Provides a mechanism for describing how m= sections should be generated.
+// The m= section with index X will use media_description_options[X]. There
+// must be an option for each existing section if creating an answer, or a
+// subsequent offer.
+struct MediaSessionOptions {
+ MediaSessionOptions() {}
- // TODO(deadbeef): Put all the audio/video/data-specific options into a map
- // structure (content name -> options).
- // MediaSessionDescriptionFactory assumes there will never be more than one
- // audio/video/data content, but this will change with unified plan.
- bool recv_audio;
- bool recv_video;
- DataChannelType data_channel_type;
- bool is_muc;
- bool vad_enabled;
- bool rtcp_mux_enabled;
- bool bundle_enabled;
- // bps. -1 == auto.
- int video_bandwidth;
- int data_bandwidth;
- bool enable_ice_renomination = false;
- // content name ("mid") => options.
- std::map<std::string, TransportOptions> transport_options;
- std::string rtcp_cname;
+ bool has_audio() const { return HasMediaDescription(MEDIA_TYPE_AUDIO); }
+ bool has_video() const { return HasMediaDescription(MEDIA_TYPE_VIDEO); }
+ bool has_data() const { return HasMediaDescription(MEDIA_TYPE_DATA); }
+
+ bool HasMediaDescription(MediaType type) const;
+
+ DataChannelType data_channel_type = DCT_NONE;
+ bool is_muc = false;
+ bool vad_enabled = true; // When disabled, removes all CN codecs from SDP.
+ bool rtcp_mux_enabled = true;
+ bool bundle_enabled = false;
+ std::string rtcp_cname = kDefaultRtcpCname;
rtc::CryptoOptions crypto_options;
-
- struct Stream {
- Stream(MediaType type,
- const std::string& id,
- const std::string& sync_label,
- int num_sim_layers)
- : type(type), id(id), sync_label(sync_label),
- num_sim_layers(num_sim_layers) {
- }
- MediaType type;
- std::string id;
- std::string sync_label;
- int num_sim_layers;
- };
-
- typedef std::vector<Stream> Streams;
- Streams streams;
+ // List of media description options in the same order that the media
+ // descriptions will be generated.
+ std::vector<MediaDescriptionOptions> media_description_options;
};
// "content" (as used in XEP-0166) descriptions for voice and video.
@@ -277,7 +266,6 @@
streams_.push_back(sp);
}
// Sets the CNAME of all StreamParams if it have not been set.
- // This can be used to set the CNAME of legacy streams.
void SetCnameIfEmpty(const std::string& cname) {
for (cricket::StreamParamsVec::iterator it = streams_.begin();
it != streams_.end(); ++it) {
@@ -469,11 +457,6 @@
void set_data_codecs(const DataCodecs& codecs) { data_codecs_ = codecs; }
SecurePolicy secure() const { return secure_; }
void set_secure(SecurePolicy s) { secure_ = s; }
- // Decides if a StreamParams shall be added to the audio and video media
- // content in SessionDescription when CreateOffer and CreateAnswer is called
- // even if |options| don't include a Stream. This is needed to support legacy
- // applications. |add_legacy_| is true per default.
- void set_add_legacy_streams(bool add_legacy) { add_legacy_ = add_legacy; }
void set_enable_encrypted_rtp_header_extensions(bool enable) {
enable_encrypted_rtp_header_extensions_ = enable;
@@ -493,13 +476,15 @@
const AudioCodecs& GetAudioCodecsForAnswer(
const RtpTransceiverDirection& offer,
const RtpTransceiverDirection& answer) const;
- void GetCodecsToOffer(const SessionDescription* current_description,
- const AudioCodecs& supported_audio_codecs,
- const VideoCodecs& supported_video_codecs,
- const DataCodecs& supported_data_codecs,
- AudioCodecs* audio_codecs,
- VideoCodecs* video_codecs,
- DataCodecs* data_codecs) const;
+ void GetCodecsForOffer(const SessionDescription* current_description,
+ AudioCodecs* audio_codecs,
+ VideoCodecs* video_codecs,
+ DataCodecs* data_codecs) const;
+ void GetCodecsForAnswer(const SessionDescription* current_description,
+ const SessionDescription* remote_offer,
+ AudioCodecs* audio_codecs,
+ VideoCodecs* video_codecs,
+ DataCodecs* data_codecs) const;
void GetRtpHdrExtsToOffer(const SessionDescription* current_description,
RtpHeaderExtensions* audio_extensions,
RtpHeaderExtensions* video_extensions) const;
@@ -526,7 +511,9 @@
// error.
bool AddAudioContentForOffer(
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
const RtpHeaderExtensions& audio_rtp_extensions,
const AudioCodecs& audio_codecs,
@@ -534,7 +521,9 @@
SessionDescription* desc) const;
bool AddVideoContentForOffer(
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
const RtpHeaderExtensions& video_rtp_extensions,
const VideoCodecs& video_codecs,
@@ -542,43 +531,66 @@
SessionDescription* desc) const;
bool AddDataContentForOffer(
- const MediaSessionOptions& options,
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* current_content,
const SessionDescription* current_description,
- DataCodecs* data_codecs,
+ const DataCodecs& data_codecs,
StreamParamsVec* current_streams,
SessionDescription* desc) const;
- bool AddAudioContentForAnswer(const SessionDescription* offer,
- const MediaSessionOptions& options,
- const SessionDescription* current_description,
- const TransportInfo* bundle_transport,
- StreamParamsVec* current_streams,
- SessionDescription* answer) const;
+ bool AddAudioContentForAnswer(
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* offer_content,
+ const SessionDescription* offer_description,
+ const ContentInfo* current_content,
+ const SessionDescription* current_description,
+ const TransportInfo* bundle_transport,
+ const AudioCodecs& audio_codecs,
+ StreamParamsVec* current_streams,
+ SessionDescription* answer) const;
- bool AddVideoContentForAnswer(const SessionDescription* offer,
- const MediaSessionOptions& options,
- const SessionDescription* current_description,
- const TransportInfo* bundle_transport,
- StreamParamsVec* current_streams,
- SessionDescription* answer) const;
+ bool AddVideoContentForAnswer(
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* offer_content,
+ const SessionDescription* offer_description,
+ const ContentInfo* current_content,
+ const SessionDescription* current_description,
+ const TransportInfo* bundle_transport,
+ const VideoCodecs& video_codecs,
+ StreamParamsVec* current_streams,
+ SessionDescription* answer) const;
- bool AddDataContentForAnswer(const SessionDescription* offer,
- const MediaSessionOptions& options,
- const SessionDescription* current_description,
- const TransportInfo* bundle_transport,
- StreamParamsVec* current_streams,
- SessionDescription* answer) const;
+ bool AddDataContentForAnswer(
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* offer_content,
+ const SessionDescription* offer_description,
+ const ContentInfo* current_content,
+ const SessionDescription* current_description,
+ const TransportInfo* bundle_transport,
+ const DataCodecs& data_codecs,
+ StreamParamsVec* current_streams,
+ SessionDescription* answer) const;
+
+ void ComputeAudioCodecsIntersectionAndUnion();
AudioCodecs audio_send_codecs_;
AudioCodecs audio_recv_codecs_;
+ // Intersection of send and recv.
AudioCodecs audio_sendrecv_codecs_;
+ // Union of send and recv.
+ AudioCodecs all_audio_codecs_;
RtpHeaderExtensions audio_rtp_extensions_;
VideoCodecs video_codecs_;
RtpHeaderExtensions video_rtp_extensions_;
DataCodecs data_codecs_;
- SecurePolicy secure_;
- bool add_legacy_;
bool enable_encrypted_rtp_header_extensions_ = false;
+ // TODO(zhihuang): Rename secure_ to sdec_policy_; rename the related getter
+ // and setter.
+ SecurePolicy secure_ = SEC_DISABLED;
std::string lang_;
const TransportDescriptionFactory* transport_desc_factory_;
};
diff --git a/webrtc/pc/mediasession_unittest.cc b/webrtc/pc/mediasession_unittest.cc
index ae8d139..f87dda1 100644
--- a/webrtc/pc/mediasession_unittest.cc
+++ b/webrtc/pc/mediasession_unittest.cc
@@ -34,6 +34,7 @@
using cricket::MediaContentDescription;
using cricket::MediaSessionDescriptionFactory;
using cricket::MediaContentDirection;
+using cricket::MediaDescriptionOptions;
using cricket::MediaSessionOptions;
using cricket::MediaType;
using cricket::SessionDescription;
@@ -65,6 +66,7 @@
using cricket::SEC_DISABLED;
using cricket::SEC_ENABLED;
using cricket::SEC_REQUIRED;
+using cricket::RtpTransceiverDirection;
using rtc::CS_AES_CM_128_HMAC_SHA1_32;
using rtc::CS_AES_CM_128_HMAC_SHA1_80;
using rtc::CS_AEAD_AES_128_GCM;
@@ -93,6 +95,9 @@
static const VideoCodec kVideoCodecs1[] = {VideoCodec(96, "H264-SVC"),
VideoCodec(97, "H264")};
+static const VideoCodec kVideoCodecs1Reverse[] = {VideoCodec(97, "H264"),
+ VideoCodec(96, "H264-SVC")};
+
static const VideoCodec kVideoCodecs2[] = {VideoCodec(126, "H264"),
VideoCodec(127, "H263")};
@@ -208,6 +213,11 @@
"TCP/TLS/RTP/SAVPF", "TCP/TLS/RTP/SAVP", "UDP/TLS/RTP/SAVPF",
"UDP/TLS/RTP/SAVP"};
+// These constants are used to make the code using "AddMediaSection" more
+// readable.
+static constexpr bool kStopped = true;
+static constexpr bool kActive = false;
+
static bool IsMediaContentOfType(const ContentInfo* content,
MediaType media_type) {
const MediaContentDescription* mdesc =
@@ -237,11 +247,95 @@
return codec_names;
}
+// This is used for test only. MIDs are not the identification of the
+// MediaDescriptionOptions since some end points may not support MID and the SDP
+// may not contain 'mid'.
+std::vector<MediaDescriptionOptions>::iterator FindFirstMediaDescriptionByMid(
+ const std::string& mid,
+ MediaSessionOptions* opts) {
+ return std::find_if(
+ opts->media_description_options.begin(),
+ opts->media_description_options.end(),
+ [mid](const MediaDescriptionOptions& t) { return t.mid == mid; });
+}
+
+// Add a media section to the |session_options|.
+static void AddMediaSection(MediaType type,
+ const std::string& mid,
+ MediaContentDirection direction,
+ bool stopped,
+ MediaSessionOptions* opts) {
+ opts->media_description_options.push_back(MediaDescriptionOptions(
+ type, mid,
+ cricket::RtpTransceiverDirection::FromMediaContentDirection(direction),
+ stopped));
+}
+
+static void AddAudioVideoSections(MediaContentDirection direction,
+ MediaSessionOptions* opts) {
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", direction, kActive, opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", direction, kActive, opts);
+}
+
+static void AddDataSection(cricket::DataChannelType dct,
+ MediaContentDirection direction,
+ MediaSessionOptions* opts) {
+ opts->data_channel_type = dct;
+ AddMediaSection(MEDIA_TYPE_DATA, "data", direction, kActive, opts);
+}
+
+static void AttachSenderToMediaSection(const std::string& mid,
+ MediaType type,
+ const std::string& track_id,
+ const std::string& stream_id,
+ int num_sim_layer,
+ MediaSessionOptions* session_options) {
+ auto it = FindFirstMediaDescriptionByMid(mid, session_options);
+ switch (type) {
+ case MEDIA_TYPE_AUDIO:
+ it->AddAudioSender(track_id, stream_id);
+ break;
+ case MEDIA_TYPE_VIDEO:
+ it->AddVideoSender(track_id, stream_id, num_sim_layer);
+ break;
+ case MEDIA_TYPE_DATA:
+ it->AddRtpDataChannel(track_id, stream_id);
+ break;
+ default:
+ RTC_NOTREACHED();
+ }
+}
+
+static void DetachSenderFromMediaSection(const std::string& mid,
+ const std::string& track_id,
+ MediaSessionOptions* session_options) {
+ auto it = FindFirstMediaDescriptionByMid(mid, session_options);
+ auto sender_it = it->sender_options.begin();
+ for (; sender_it != it->sender_options.end(); ++sender_it) {
+ if (sender_it->track_id == track_id) {
+ it->sender_options.erase(sender_it);
+ return;
+ }
+ }
+ RTC_NOTREACHED();
+}
+
+// Helper function used to create a default MediaSessionOptions for Plan B SDP.
+// (https://tools.ietf.org/html/draft-uberti-rtcweb-plan-00).
+static MediaSessionOptions CreatePlanBMediaSessionOptions() {
+ MediaSessionOptions session_options;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &session_options);
+ return session_options;
+}
+
+// TODO(zhihuang): Most of these tests were written while MediaSessionOptions
+// was designed for Plan B SDP, where only one audio "m=" section and one video
+// "m=" section could be generated, and ordering couldn't be controlled. Many of
+// these tests may be obsolete as a result, and should be refactored or removed.
class MediaSessionDescriptionFactoryTest : public testing::Test {
public:
- MediaSessionDescriptionFactoryTest()
- : f1_(&tdf1_),
- f2_(&tdf2_) {
+ MediaSessionDescriptionFactoryTest() : f1_(&tdf1_), f2_(&tdf2_) {
f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
MAKE_VECTOR(kAudioCodecs1));
f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
@@ -305,7 +399,8 @@
return iter != ice_options.end();
}
- void TestTransportInfo(bool offer, const MediaSessionOptions& options,
+ void TestTransportInfo(bool offer,
+ MediaSessionOptions& options,
bool has_current_desc) {
const std::string current_audio_ufrag = "current_audio_ufrag";
const std::string current_audio_pwd = "current_audio_pwd";
@@ -350,7 +445,11 @@
EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
ti_audio->description.ice_pwd.size());
}
- EXPECT_EQ(options.enable_ice_renomination, GetIceRenomination(ti_audio));
+ auto media_desc_options_it =
+ FindFirstMediaDescriptionByMid("audio", &options);
+ EXPECT_EQ(
+ media_desc_options_it->transport_options.enable_ice_renomination,
+ GetIceRenomination(ti_audio));
} else {
EXPECT_TRUE(ti_audio == NULL);
@@ -374,7 +473,11 @@
ti_video->description.ice_pwd.size());
}
}
- EXPECT_EQ(options.enable_ice_renomination, GetIceRenomination(ti_video));
+ auto media_desc_options_it =
+ FindFirstMediaDescriptionByMid("video", &options);
+ EXPECT_EQ(
+ media_desc_options_it->transport_options.enable_ice_renomination,
+ GetIceRenomination(ti_video));
} else {
EXPECT_TRUE(ti_video == NULL);
}
@@ -397,7 +500,11 @@
ti_data->description.ice_pwd.size());
}
}
- EXPECT_EQ(options.enable_ice_renomination, GetIceRenomination(ti_data));
+ auto media_desc_options_it =
+ FindFirstMediaDescriptionByMid("data", &options);
+ EXPECT_EQ(
+ media_desc_options_it->transport_options.enable_ice_renomination,
+ GetIceRenomination(ti_data));
} else {
EXPECT_TRUE(ti_video == NULL);
@@ -407,9 +514,8 @@
void TestCryptoWithBundle(bool offer) {
f1_.set_secure(SEC_ENABLED);
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
std::unique_ptr<SessionDescription> ref_desc;
std::unique_ptr<SessionDescription> desc;
if (offer) {
@@ -455,27 +561,25 @@
// This test that the audio and video media direction is set to
// |expected_direction_in_answer| in an answer if the offer direction is set
- // to |direction_in_offer|.
+ // to |direction_in_offer| and the answer is willing to both send and receive.
void TestMediaDirectionInAnswer(
cricket::MediaContentDirection direction_in_offer,
cricket::MediaContentDirection expected_direction_in_answer) {
- MediaSessionOptions opts;
- opts.recv_video = true;
- std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
+ MediaSessionOptions offer_opts;
+ AddAudioVideoSections(direction_in_offer, &offer_opts);
+
+ std::unique_ptr<SessionDescription> offer(
+ f1_.CreateOffer(offer_opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
ContentInfo* ac_offer = offer->GetContentByName("audio");
ASSERT_TRUE(ac_offer != NULL);
- AudioContentDescription* acd_offer =
- static_cast<AudioContentDescription*>(ac_offer->description);
- acd_offer->set_direction(direction_in_offer);
ContentInfo* vc_offer = offer->GetContentByName("video");
ASSERT_TRUE(vc_offer != NULL);
- VideoContentDescription* vcd_offer =
- static_cast<VideoContentDescription*>(vc_offer->description);
- vcd_offer->set_direction(direction_in_offer);
+ MediaSessionOptions answer_opts;
+ AddAudioVideoSections(cricket::MD_SENDRECV, &answer_opts);
std::unique_ptr<SessionDescription> answer(
- f2_.CreateAnswer(offer.get(), opts, NULL));
+ f2_.CreateAnswer(offer.get(), answer_opts, NULL));
const AudioContentDescription* acd_answer =
GetFirstAudioContentDescription(answer.get());
EXPECT_EQ(expected_direction_in_answer, acd_answer->direction());
@@ -499,11 +603,13 @@
void TestVideoGcmCipher(bool gcm_offer, bool gcm_answer) {
MediaSessionOptions offer_opts;
- offer_opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &offer_opts);
offer_opts.crypto_options.enable_gcm_crypto_suites = gcm_offer;
+
MediaSessionOptions answer_opts;
- answer_opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &answer_opts);
answer_opts.crypto_options.enable_gcm_crypto_suites = gcm_answer;
+
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(
@@ -524,7 +630,7 @@
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
if (gcm_offer && gcm_answer) {
ASSERT_CRYPTO(acd, 1U, CS_AEAD_AES_256_GCM);
@@ -533,7 +639,7 @@
}
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
EXPECT_EQ(MAKE_VECTOR(kVideoCodecsAnswer), vcd->codecs());
- EXPECT_NE(0U, vcd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
EXPECT_TRUE(vcd->rtcp_mux()); // negotiated rtcp-mux
if (gcm_offer && gcm_answer) {
ASSERT_CRYPTO(vcd, 1U, CS_AEAD_AES_256_GCM);
@@ -554,7 +660,7 @@
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOffer) {
f1_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(
- f1_.CreateOffer(MediaSessionOptions(), NULL));
+ f1_.CreateOffer(CreatePlanBMediaSessionOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
const ContentInfo* ac = offer->GetContentByName("audio");
const ContentInfo* vc = offer->GetContentByName("video");
@@ -565,7 +671,7 @@
static_cast<const AudioContentDescription*>(ac->description);
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached.
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
ASSERT_CRYPTO(acd, 2U, CS_AES_CM_128_HMAC_SHA1_32);
@@ -575,7 +681,7 @@
// Create a typical video offer, and ensure it matches what we expect.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoOffer) {
MediaSessionOptions opts;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f1_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
@@ -591,14 +697,14 @@
static_cast<const VideoContentDescription*>(vc->description);
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
ASSERT_CRYPTO(acd, 2U, CS_AES_CM_128_HMAC_SHA1_32);
EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol());
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
EXPECT_EQ(f1_.video_codecs(), vcd->codecs());
- EXPECT_NE(0U, vcd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto)
EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on
ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80);
@@ -616,9 +722,8 @@
ASSERT_EQ(offered_video_codec.id, offered_data_codec.id);
MediaSessionOptions opts;
- opts.recv_audio = true;
- opts.recv_video = true;
- opts.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &opts);
opts.bundle_enabled = true;
std::unique_ptr<SessionDescription> offer(f2_.CreateOffer(opts, NULL));
const VideoContentDescription* vcd =
@@ -638,15 +743,17 @@
EXPECT_EQ(dcd->codecs()[0].name, offered_data_codec.name);
}
-// Test creating an updated offer with with bundle, audio, video and data
+// Test creating an updated offer with bundle, audio, video and data
// after an audio only session has been negotiated.
TEST_F(MediaSessionDescriptionFactoryTest,
TestCreateUpdatedVideoOfferWithBundle) {
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
MediaSessionOptions opts;
- opts.recv_audio = true;
- opts.recv_video = false;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_INACTIVE, kStopped,
+ &opts);
opts.data_channel_type = cricket::DCT_NONE;
opts.bundle_enabled = true;
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
@@ -654,9 +761,8 @@
f2_.CreateAnswer(offer.get(), opts, NULL));
MediaSessionOptions updated_opts;
- updated_opts.recv_audio = true;
- updated_opts.recv_video = true;
- updated_opts.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &updated_opts);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &updated_opts);
updated_opts.bundle_enabled = true;
std::unique_ptr<SessionDescription> updated_offer(
f1_.CreateOffer(updated_opts, answer.get()));
@@ -682,7 +788,8 @@
// Create a RTP data offer, and ensure it matches what we expect.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateRtpDataOffer) {
MediaSessionOptions opts;
- opts.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &opts);
f1_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
@@ -698,14 +805,14 @@
static_cast<const DataContentDescription*>(dc->description);
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attched.
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
ASSERT_CRYPTO(acd, 2U, CS_AES_CM_128_HMAC_SHA1_32);
EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol());
EXPECT_EQ(MEDIA_TYPE_DATA, dcd->type());
EXPECT_EQ(f1_.data_codecs(), dcd->codecs());
- EXPECT_NE(0U, dcd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, dcd->first_ssrc()); // no sender is attached.
EXPECT_EQ(cricket::kDataMaxBandwidth,
dcd->bandwidth()); // default bandwidth (auto)
EXPECT_TRUE(dcd->rtcp_mux()); // rtcp-mux defaults on
@@ -716,9 +823,8 @@
// Create an SCTP data offer with bundle without error.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSctpDataOffer) {
MediaSessionOptions opts;
- opts.recv_audio = false;
opts.bundle_enabled = true;
- opts.data_channel_type = cricket::DCT_SCTP;
+ AddDataSection(cricket::DCT_SCTP, cricket::MD_SENDRECV, &opts);
f1_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
EXPECT_TRUE(offer.get() != NULL);
@@ -728,9 +834,8 @@
// Test creating an sctp data channel from an already generated offer.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateImplicitSctpDataOffer) {
MediaSessionOptions opts;
- opts.recv_audio = false;
opts.bundle_enabled = true;
- opts.data_channel_type = cricket::DCT_SCTP;
+ AddDataSection(cricket::DCT_SCTP, cricket::MD_SENDRECV, &opts);
f1_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer1(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer1.get() != NULL);
@@ -756,8 +861,7 @@
TEST_F(MediaSessionDescriptionFactoryTest,
TestCreateOfferWithoutLegacyStreams) {
MediaSessionOptions opts;
- opts.recv_video = true;
- f1_.set_add_legacy_streams(false);
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
const ContentInfo* ac = offer->GetContentByName("audio");
@@ -775,13 +879,14 @@
// Creates an audio+video sendonly offer.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSendOnlyOffer) {
- MediaSessionOptions options;
- options.recv_audio = false;
- options.recv_video = false;
- options.AddSendStream(MEDIA_TYPE_VIDEO, kVideoTrack1, kMediaStream1);
- options.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack1, kMediaStream1);
+ MediaSessionOptions opts;
+ AddAudioVideoSections(cricket::MD_SENDONLY, &opts);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
+ kMediaStream1, 1, &opts);
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
+ kMediaStream1, 1, &opts);
- std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(options, NULL));
+ std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
EXPECT_EQ(2u, offer->contents().size());
EXPECT_TRUE(IsMediaContentOfType(&offer->contents()[0], MEDIA_TYPE_AUDIO));
@@ -795,16 +900,15 @@
// SessionDescription is preserved in the new SessionDescription.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateOfferContentOrder) {
MediaSessionOptions opts;
- opts.recv_audio = false;
- opts.recv_video = false;
- opts.data_channel_type = cricket::DCT_SCTP;
+ AddDataSection(cricket::DCT_SCTP, cricket::MD_SENDRECV, &opts);
std::unique_ptr<SessionDescription> offer1(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer1.get() != NULL);
EXPECT_EQ(1u, offer1->contents().size());
EXPECT_TRUE(IsMediaContentOfType(&offer1->contents()[0], MEDIA_TYPE_DATA));
- opts.recv_video = true;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &opts);
std::unique_ptr<SessionDescription> offer2(
f1_.CreateOffer(opts, offer1.get()));
ASSERT_TRUE(offer2.get() != NULL);
@@ -812,7 +916,8 @@
EXPECT_TRUE(IsMediaContentOfType(&offer2->contents()[0], MEDIA_TYPE_DATA));
EXPECT_TRUE(IsMediaContentOfType(&offer2->contents()[1], MEDIA_TYPE_VIDEO));
- opts.recv_audio = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &opts);
std::unique_ptr<SessionDescription> offer3(
f1_.CreateOffer(opts, offer2.get()));
ASSERT_TRUE(offer3.get() != NULL);
@@ -820,15 +925,6 @@
EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[0], MEDIA_TYPE_DATA));
EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[1], MEDIA_TYPE_VIDEO));
EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[2], MEDIA_TYPE_AUDIO));
-
- // Verifies the default order is audio-video-data, so that the previous checks
- // didn't pass by accident.
- std::unique_ptr<SessionDescription> offer4(f1_.CreateOffer(opts, NULL));
- ASSERT_TRUE(offer4.get() != NULL);
- EXPECT_EQ(3u, offer4->contents().size());
- EXPECT_TRUE(IsMediaContentOfType(&offer4->contents()[0], MEDIA_TYPE_AUDIO));
- EXPECT_TRUE(IsMediaContentOfType(&offer4->contents()[1], MEDIA_TYPE_VIDEO));
- EXPECT_TRUE(IsMediaContentOfType(&offer4->contents()[2], MEDIA_TYPE_DATA));
}
// Create a typical audio answer, and ensure it matches what we expect.
@@ -836,10 +932,10 @@
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(
- f1_.CreateOffer(MediaSessionOptions(), NULL));
+ f1_.CreateOffer(CreatePlanBMediaSessionOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
std::unique_ptr<SessionDescription> answer(
- f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL));
+ f2_.CreateAnswer(offer.get(), CreatePlanBMediaSessionOptions(), NULL));
const ContentInfo* ac = answer->GetContentByName("audio");
const ContentInfo* vc = answer->GetContentByName("video");
ASSERT_TRUE(ac != NULL);
@@ -849,7 +945,7 @@
static_cast<const AudioContentDescription*>(ac->description);
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_32);
@@ -861,13 +957,12 @@
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerGcm) {
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
- MediaSessionOptions options;
- options.crypto_options.enable_gcm_crypto_suites = true;
- std::unique_ptr<SessionDescription> offer(
- f1_.CreateOffer(options, NULL));
+ MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
+ opts.crypto_options.enable_gcm_crypto_suites = true;
+ std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
std::unique_ptr<SessionDescription> answer(
- f2_.CreateAnswer(offer.get(), options, NULL));
+ f2_.CreateAnswer(offer.get(), opts, NULL));
const ContentInfo* ac = answer->GetContentByName("audio");
const ContentInfo* vc = answer->GetContentByName("video");
ASSERT_TRUE(ac != NULL);
@@ -877,7 +972,7 @@
static_cast<const AudioContentDescription*>(ac->description);
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(acd, 1U, CS_AEAD_AES_256_GCM);
@@ -887,7 +982,7 @@
// Create a typical video answer, and ensure it matches what we expect.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) {
MediaSessionOptions opts;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
@@ -907,12 +1002,12 @@
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_32);
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
EXPECT_EQ(MAKE_VECTOR(kVideoCodecsAnswer), vcd->codecs());
- EXPECT_NE(0U, vcd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
EXPECT_TRUE(vcd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80);
EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol());
@@ -937,8 +1032,8 @@
}
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswer) {
- MediaSessionOptions opts;
- opts.data_channel_type = cricket::DCT_RTP;
+ MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &opts);
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
@@ -958,20 +1053,20 @@
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_32);
EXPECT_EQ(MEDIA_TYPE_DATA, dcd->type());
EXPECT_EQ(MAKE_VECTOR(kDataCodecsAnswer), dcd->codecs());
- EXPECT_NE(0U, dcd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, dcd->first_ssrc()); // no sender is attached
EXPECT_TRUE(dcd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80);
EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), dcd->protocol());
}
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerGcm) {
- MediaSessionOptions opts;
- opts.data_channel_type = cricket::DCT_RTP;
+ MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &opts);
opts.crypto_options.enable_gcm_crypto_suites = true;
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
@@ -992,12 +1087,12 @@
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
- EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(acd, 1U, CS_AEAD_AES_256_GCM);
EXPECT_EQ(MEDIA_TYPE_DATA, dcd->type());
EXPECT_EQ(MAKE_VECTOR(kDataCodecsAnswer), dcd->codecs());
- EXPECT_NE(0U, dcd->first_ssrc()); // a random nonzero ssrc
+ EXPECT_EQ(0U, dcd->first_ssrc()); // no sender is attached
EXPECT_TRUE(dcd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(dcd, 1U, CS_AEAD_AES_256_GCM);
EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), dcd->protocol());
@@ -1007,7 +1102,7 @@
// The answer's use_sctpmap flag should match the offer's.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerUsesSctpmap) {
MediaSessionOptions opts;
- opts.data_channel_type = cricket::DCT_SCTP;
+ AddDataSection(cricket::DCT_SCTP, cricket::MD_SENDRECV, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
ContentInfo* dc_offer = offer->GetContentByName("data");
@@ -1028,7 +1123,7 @@
// The answer's use_sctpmap flag should match the offer's.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerWithoutSctpmap) {
MediaSessionOptions opts;
- opts.data_channel_type = cricket::DCT_SCTP;
+ AddDataSection(cricket::DCT_SCTP, cricket::MD_SENDRECV, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
ContentInfo* dc_offer = offer->GetContentByName("data");
@@ -1058,7 +1153,7 @@
tdf2_.set_secure(SEC_ENABLED);
MediaSessionOptions opts;
- opts.data_channel_type = cricket::DCT_SCTP;
+ AddDataSection(cricket::DCT_SCTP, cricket::MD_SENDRECV, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
ASSERT_TRUE(offer.get() != nullptr);
ContentInfo* dc_offer = offer->GetContentByName("data");
@@ -1087,19 +1182,20 @@
MediaSessionOptions opts;
// Creates a data only offer.
- opts.recv_audio = false;
- opts.data_channel_type = cricket::DCT_SCTP;
+ AddDataSection(cricket::DCT_SCTP, cricket::MD_SENDRECV, &opts);
std::unique_ptr<SessionDescription> offer1(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer1.get() != NULL);
// Appends audio to the offer.
- opts.recv_audio = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &opts);
std::unique_ptr<SessionDescription> offer2(
f1_.CreateOffer(opts, offer1.get()));
ASSERT_TRUE(offer2.get() != NULL);
// Appends video to the offer.
- opts.recv_video = true;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &opts);
std::unique_ptr<SessionDescription> offer3(
f1_.CreateOffer(opts, offer2.get()));
ASSERT_TRUE(offer3.get() != NULL);
@@ -1144,8 +1240,7 @@
TEST_F(MediaSessionDescriptionFactoryTest,
CreateDataAnswerToOfferWithUnknownProtocol) {
MediaSessionOptions opts;
- opts.data_channel_type = cricket::DCT_RTP;
- opts.recv_audio = false;
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &opts);
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
@@ -1171,7 +1266,7 @@
// Test that the media protocol is RTP/AVPF if DTLS and SDES are disabled.
TEST_F(MediaSessionDescriptionFactoryTest, AudioOfferAnswerWithCryptoDisabled) {
- MediaSessionOptions opts;
+ MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
f1_.set_secure(SEC_DISABLED);
f2_.set_secure(SEC_DISABLED);
tdf1_.set_secure(SEC_DISABLED);
@@ -1200,8 +1295,7 @@
// matches what we expect.
TEST_F(MediaSessionDescriptionFactoryTest, TestOfferAnswerWithRtpExtensions) {
MediaSessionOptions opts;
- opts.recv_video = true;
-
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f1_.set_audio_rtp_header_extensions(MAKE_VECTOR(kAudioRtpExtension1));
f1_.set_video_rtp_header_extensions(MAKE_VECTOR(kVideoRtpExtension1));
f2_.set_audio_rtp_header_extensions(MAKE_VECTOR(kAudioRtpExtension2));
@@ -1229,7 +1323,7 @@
TEST_F(MediaSessionDescriptionFactoryTest,
TestOfferAnswerWithEncryptedRtpExtensionsBoth) {
MediaSessionOptions opts;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f1_.set_enable_encrypted_rtp_header_extensions(true);
f2_.set_enable_encrypted_rtp_header_extensions(true);
@@ -1265,7 +1359,7 @@
TEST_F(MediaSessionDescriptionFactoryTest,
TestOfferAnswerWithEncryptedRtpExtensionsOffer) {
MediaSessionOptions opts;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f1_.set_enable_encrypted_rtp_header_extensions(true);
@@ -1300,7 +1394,7 @@
TEST_F(MediaSessionDescriptionFactoryTest,
TestOfferAnswerWithEncryptedRtpExtensionsAnswer) {
MediaSessionOptions opts;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f2_.set_enable_encrypted_rtp_header_extensions(true);
@@ -1336,10 +1430,8 @@
TEST_F(MediaSessionDescriptionFactoryTest,
TestCreateAnswerWithoutLegacyStreams) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.data_channel_type = cricket::DCT_RTP;
- f1_.set_add_legacy_streams(false);
- f2_.set_add_legacy_streams(false);
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
std::unique_ptr<SessionDescription> answer(
@@ -1363,8 +1455,8 @@
TEST_F(MediaSessionDescriptionFactoryTest, TestPartial) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &opts);
f1_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
@@ -1400,18 +1492,18 @@
// Create a typical video answer, and ensure it matches what we expect.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerRtcpMux) {
MediaSessionOptions offer_opts;
+ AddAudioVideoSections(cricket::MD_SENDRECV, &offer_opts);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_SENDRECV, &offer_opts);
+
MediaSessionOptions answer_opts;
- answer_opts.recv_video = true;
- offer_opts.recv_video = true;
- answer_opts.data_channel_type = cricket::DCT_RTP;
- offer_opts.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_SENDRECV, &answer_opts);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_SENDRECV, &answer_opts);
std::unique_ptr<SessionDescription> offer;
std::unique_ptr<SessionDescription> answer;
offer_opts.rtcp_mux_enabled = true;
answer_opts.rtcp_mux_enabled = true;
-
offer.reset(f1_.CreateOffer(offer_opts, NULL));
answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL));
ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
@@ -1429,7 +1521,6 @@
offer_opts.rtcp_mux_enabled = true;
answer_opts.rtcp_mux_enabled = false;
-
offer.reset(f1_.CreateOffer(offer_opts, NULL));
answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL));
ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
@@ -1447,7 +1538,6 @@
offer_opts.rtcp_mux_enabled = false;
answer_opts.rtcp_mux_enabled = true;
-
offer.reset(f1_.CreateOffer(offer_opts, NULL));
answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL));
ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
@@ -1465,7 +1555,6 @@
offer_opts.rtcp_mux_enabled = false;
answer_opts.rtcp_mux_enabled = false;
-
offer.reset(f1_.CreateOffer(offer_opts, NULL));
answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL));
ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
@@ -1485,11 +1574,16 @@
// Create an audio-only answer to a video offer.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerToVideo) {
MediaSessionOptions opts;
- opts.recv_video = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
+
+ opts.media_description_options[1].stopped = true;
std::unique_ptr<SessionDescription> answer(
- f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL));
+ f2_.CreateAnswer(offer.get(), opts, NULL));
const ContentInfo* ac = answer->GetContentByName("audio");
const ContentInfo* vc = answer->GetContentByName("video");
ASSERT_TRUE(ac != NULL);
@@ -1500,12 +1594,16 @@
// Create an audio-only answer to an offer with data.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateNoDataAnswerToDataOffer) {
- MediaSessionOptions opts;
+ MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
opts.data_channel_type = cricket::DCT_RTP;
+ AddMediaSection(MEDIA_TYPE_DATA, "data", cricket::MD_RECVONLY, kActive,
+ &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
+
+ opts.media_description_options[1].stopped = true;
std::unique_ptr<SessionDescription> answer(
- f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL));
+ f2_.CreateAnswer(offer.get(), opts, NULL));
const ContentInfo* ac = answer->GetContentByName("audio");
const ContentInfo* dc = answer->GetContentByName("data");
ASSERT_TRUE(ac != NULL);
@@ -1518,8 +1616,8 @@
TEST_F(MediaSessionDescriptionFactoryTest,
CreateAnswerToOfferWithRejectedMedia) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
ContentInfo* ac = offer->GetContentByName("audio");
@@ -1552,12 +1650,19 @@
// adding a new video track and replaces one of the audio tracks.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) {
MediaSessionOptions opts;
- opts.AddSendStream(MEDIA_TYPE_VIDEO, kVideoTrack1, kMediaStream1);
- opts.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack1, kMediaStream1);
- opts.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack2, kMediaStream1);
- opts.data_channel_type = cricket::DCT_RTP;
- opts.AddSendStream(MEDIA_TYPE_DATA, kDataTrack1, kMediaStream1);
- opts.AddSendStream(MEDIA_TYPE_DATA, kDataTrack2, kMediaStream1);
+ AddAudioVideoSections(cricket::MD_SENDRECV, &opts);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
+ kMediaStream1, 1, &opts);
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
+ kMediaStream1, 1, &opts);
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack2,
+ kMediaStream1, 1, &opts);
+
+ AddDataSection(cricket::DCT_RTP, cricket::MD_SENDRECV, &opts);
+ AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack1,
+ kMediaStream1, 1, &opts);
+ AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack2,
+ kMediaStream1, 1, &opts);
f1_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
@@ -1622,14 +1727,16 @@
EXPECT_TRUE(dcd->rtcp_mux()); // rtcp-mux defaults on
ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80);
-
// Update the offer. Add a new video track that is not synched to the
// other tracks and replace audio track 2 with audio track 3.
- opts.AddSendStream(MEDIA_TYPE_VIDEO, kVideoTrack2, kMediaStream2);
- opts.RemoveSendStream(MEDIA_TYPE_AUDIO, kAudioTrack2);
- opts.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack3, kMediaStream1);
- opts.RemoveSendStream(MEDIA_TYPE_DATA, kDataTrack2);
- opts.AddSendStream(MEDIA_TYPE_DATA, kDataTrack3, kMediaStream1);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack2,
+ kMediaStream2, 1, &opts);
+ DetachSenderFromMediaSection("audio", kAudioTrack2, &opts);
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack3,
+ kMediaStream1, 1, &opts);
+ DetachSenderFromMediaSection("data", kDataTrack2, &opts);
+ AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack3,
+ kMediaStream1, 1, &opts);
std::unique_ptr<SessionDescription> updated_offer(
f1_.CreateOffer(opts, offer.get()));
@@ -1691,8 +1798,13 @@
// Create an offer with simulcast video stream.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) {
MediaSessionOptions opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_SENDRECV, kActive,
+ &opts);
const int num_sim_layers = 3;
- opts.AddSendVideoStream(kVideoTrack1, kMediaStream1, num_sim_layers);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
+ kMediaStream1, num_sim_layers, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
@@ -1718,22 +1830,39 @@
// adding a new video track and removes one of the audio tracks.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) {
MediaSessionOptions offer_opts;
- offer_opts.recv_video = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &offer_opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &offer_opts);
offer_opts.data_channel_type = cricket::DCT_RTP;
+ AddMediaSection(MEDIA_TYPE_DATA, "data", cricket::MD_RECVONLY, kActive,
+ &offer_opts);
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(offer_opts, NULL));
- MediaSessionOptions opts;
- opts.AddSendStream(MEDIA_TYPE_VIDEO, kVideoTrack1, kMediaStream1);
- opts.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack1, kMediaStream1);
- opts.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack2, kMediaStream1);
- opts.data_channel_type = cricket::DCT_RTP;
- opts.AddSendStream(MEDIA_TYPE_DATA, kDataTrack1, kMediaStream1);
- opts.AddSendStream(MEDIA_TYPE_DATA, kDataTrack2, kMediaStream1);
+ MediaSessionOptions answer_opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_SENDRECV, kActive,
+ &answer_opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_SENDRECV, kActive,
+ &answer_opts);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
+ kMediaStream1, 1, &answer_opts);
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
+ kMediaStream1, 1, &answer_opts);
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack2,
+ kMediaStream1, 1, &answer_opts);
+
+ AddMediaSection(MEDIA_TYPE_DATA, "data", cricket::MD_SENDRECV, kActive,
+ &answer_opts);
+ AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack1,
+ kMediaStream1, 1, &answer_opts);
+ AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack2,
+ kMediaStream1, 1, &answer_opts);
+ answer_opts.data_channel_type = cricket::DCT_RTP;
std::unique_ptr<SessionDescription> answer(
- f2_.CreateAnswer(offer.get(), opts, NULL));
+ f2_.CreateAnswer(offer.get(), answer_opts, NULL));
ASSERT_TRUE(answer.get() != NULL);
const ContentInfo* ac = answer->GetContentByName("audio");
@@ -1797,11 +1926,12 @@
// Update the answer. Add a new video track that is not synched to the
// other tracks and remove 1 audio track.
- opts.AddSendStream(MEDIA_TYPE_VIDEO, kVideoTrack2, kMediaStream2);
- opts.RemoveSendStream(MEDIA_TYPE_AUDIO, kAudioTrack2);
- opts.RemoveSendStream(MEDIA_TYPE_DATA, kDataTrack2);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack2,
+ kMediaStream2, 1, &answer_opts);
+ DetachSenderFromMediaSection("audio", kAudioTrack2, &answer_opts);
+ DetachSenderFromMediaSection("data", kDataTrack2, &answer_opts);
std::unique_ptr<SessionDescription> updated_answer(
- f2_.CreateAnswer(offer.get(), opts, answer.get()));
+ f2_.CreateAnswer(offer.get(), answer_opts, answer.get()));
ASSERT_TRUE(updated_answer.get() != NULL);
ac = updated_answer->GetContentByName("audio");
@@ -1853,8 +1983,7 @@
TEST_F(MediaSessionDescriptionFactoryTest,
RespondentCreatesOfferAfterCreatingAnswer) {
MediaSessionOptions opts;
- opts.recv_audio = true;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
std::unique_ptr<SessionDescription> answer(
@@ -1905,8 +2034,8 @@
TEST_F(MediaSessionDescriptionFactoryTest,
RespondentCreatesOfferAfterCreatingAnswerWithRtx) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = false;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates rtx for H264 with the payload type |f1_| uses.
AddRtxCodec(VideoCodec::CreateRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
@@ -1958,8 +2087,8 @@
f1_.set_video_codecs(f1_codecs);
MediaSessionOptions opts;
- opts.recv_audio = true;
- opts.recv_video = false;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
std::unique_ptr<SessionDescription> answer(
@@ -1972,8 +2101,8 @@
// Now - let |f2_| add video with RTX and let the payload type the RTX codec
// reference be the same as an audio codec that was negotiated in the
// first offer/answer exchange.
- opts.recv_audio = true;
- opts.recv_video = true;
+ opts.media_description_options.clear();
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
int used_pl_type = acd->codecs()[0].id;
@@ -2010,8 +2139,7 @@
TEST_F(MediaSessionDescriptionFactoryTest,
RespondentCreatesOfferWithRtxAfterCreatingAnswerWithoutRtx) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
// This creates rtx for H264 with the payload type |f2_| uses.
@@ -2049,8 +2177,8 @@
// Test that RTX is ignored when there is no associated payload type parameter.
TEST_F(MediaSessionDescriptionFactoryTest, RtxWithoutApt) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = false;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX without associated payload type parameter.
AddRtxCodec(VideoCodec(126, cricket::kRtxCodecName), &f1_codecs);
@@ -2093,8 +2221,8 @@
// type doesn't match the local value.
TEST_F(MediaSessionDescriptionFactoryTest, FilterOutRtxIfAptDoesntMatch) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = false;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX for H264 in sender.
AddRtxCodec(VideoCodec::CreateRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
@@ -2123,8 +2251,8 @@
TEST_F(MediaSessionDescriptionFactoryTest,
FilterOutUnsupportedRtxWhenCreatingAnswer) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = false;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX for H264-SVC in sender.
AddRtxCodec(VideoCodec::CreateRtxCodec(125, kVideoCodecs1[0].id), &f1_codecs);
@@ -2158,8 +2286,8 @@
// to add another.
TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = false;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_RECVONLY, kActive,
+ &opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX for H264 for the offerer.
AddRtxCodec(VideoCodec::CreateRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
@@ -2193,11 +2321,11 @@
// generated for each simulcast ssrc and correctly grouped.
TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = false;
-
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_SENDRECV, kActive,
+ &opts);
// Add simulcast streams.
- opts.AddSendVideoStream("stream1", "stream1label", 3);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, "stream1",
+ "stream1label", 3, &opts);
// Use a single real codec, and then add RTX for it.
std::vector<VideoCodec> f1_codecs;
@@ -2234,11 +2362,11 @@
// together with a FEC-FR grouping.
TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = false;
-
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_SENDRECV, kActive,
+ &opts);
// Add single stream.
- opts.AddSendVideoStream("stream1", "stream1label", 1);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, "stream1",
+ "stream1label", 1, &opts);
// Use a single real codec, and then add FlexFEC for it.
std::vector<VideoCodec> f1_codecs;
@@ -2274,11 +2402,11 @@
// multiple FlexfecSenders, or through multistream protection.
TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateNoFlexfecSsrcs) {
MediaSessionOptions opts;
- opts.recv_video = true;
- opts.recv_audio = false;
-
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_SENDRECV, kActive,
+ &opts);
// Add simulcast streams.
- opts.AddSendVideoStream("stream1", "stream1label", 3);
+ AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, "stream1",
+ "stream1label", 3, &opts);
// Use a single real codec, and then add FlexFEC for it.
std::vector<VideoCodec> f1_codecs;
@@ -2318,8 +2446,7 @@
TEST_F(MediaSessionDescriptionFactoryTest,
RespondentCreatesOfferAfterCreatingAnswerWithRtpExtensions) {
MediaSessionOptions opts;
- opts.recv_audio = true;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f1_.set_audio_rtp_header_extensions(MAKE_VECTOR(kAudioRtpExtension1));
f1_.set_video_rtp_header_extensions(MAKE_VECTOR(kVideoRtpExtension1));
@@ -2373,8 +2500,7 @@
// updated offer (this was previously a bug).
TEST_F(MediaSessionDescriptionFactoryTest, RtpExtensionIdReused) {
MediaSessionOptions opts;
- opts.recv_audio = true;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f1_.set_audio_rtp_header_extensions(MAKE_VECTOR(kAudioRtpExtension3));
f1_.set_video_rtp_header_extensions(MAKE_VECTOR(kVideoRtpExtension3));
@@ -2409,8 +2535,7 @@
// Same as "RtpExtensionIdReused" above for encrypted RTP extensions.
TEST_F(MediaSessionDescriptionFactoryTest, RtpExtensionIdReusedEncrypted) {
MediaSessionOptions opts;
- opts.recv_audio = true;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
f1_.set_enable_encrypted_rtp_header_extensions(true);
f2_.set_enable_encrypted_rtp_header_extensions(true);
@@ -2486,45 +2611,47 @@
// ensure the TransportInfo in the SessionDescription matches what we expect.
TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferAudio) {
MediaSessionOptions options;
- options.recv_audio = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &options);
TestTransportInfo(true, options, false);
}
TEST_F(MediaSessionDescriptionFactoryTest,
TestTransportInfoOfferIceRenomination) {
MediaSessionOptions options;
- options.enable_ice_renomination = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &options);
+ options.media_description_options[0]
+ .transport_options.enable_ice_renomination = true;
TestTransportInfo(true, options, false);
}
TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferAudioCurrent) {
MediaSessionOptions options;
- options.recv_audio = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &options);
TestTransportInfo(true, options, true);
}
TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferMultimedia) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
TestTransportInfo(true, options, false);
}
TEST_F(MediaSessionDescriptionFactoryTest,
TestTransportInfoOfferMultimediaCurrent) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
TestTransportInfo(true, options, true);
}
TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferBundle) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
options.bundle_enabled = true;
TestTransportInfo(true, options, false);
}
@@ -2532,55 +2659,56 @@
TEST_F(MediaSessionDescriptionFactoryTest,
TestTransportInfoOfferBundleCurrent) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
options.bundle_enabled = true;
TestTransportInfo(true, options, true);
}
TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerAudio) {
MediaSessionOptions options;
- options.recv_audio = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &options);
TestTransportInfo(false, options, false);
}
TEST_F(MediaSessionDescriptionFactoryTest,
TestTransportInfoAnswerIceRenomination) {
MediaSessionOptions options;
- options.enable_ice_renomination = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &options);
+ options.media_description_options[0]
+ .transport_options.enable_ice_renomination = true;
TestTransportInfo(false, options, false);
}
TEST_F(MediaSessionDescriptionFactoryTest,
TestTransportInfoAnswerAudioCurrent) {
MediaSessionOptions options;
- options.recv_audio = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_RECVONLY, kActive,
+ &options);
TestTransportInfo(false, options, true);
}
TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerMultimedia) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
TestTransportInfo(false, options, false);
}
TEST_F(MediaSessionDescriptionFactoryTest,
TestTransportInfoAnswerMultimediaCurrent) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
TestTransportInfo(false, options, true);
}
TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerBundle) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
options.bundle_enabled = true;
TestTransportInfo(false, options, false);
}
@@ -2588,9 +2716,8 @@
TEST_F(MediaSessionDescriptionFactoryTest,
TestTransportInfoAnswerBundleCurrent) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
options.bundle_enabled = true;
TestTransportInfo(false, options, true);
}
@@ -2617,7 +2744,7 @@
tdf2_.set_secure(SEC_DISABLED);
std::unique_ptr<SessionDescription> offer(
- f1_.CreateOffer(MediaSessionOptions(), NULL));
+ f1_.CreateOffer(CreatePlanBMediaSessionOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
ContentInfo* offer_content = offer->GetContentByName("audio");
ASSERT_TRUE(offer_content != NULL);
@@ -2626,7 +2753,7 @@
offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf);
std::unique_ptr<SessionDescription> answer(
- f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL));
+ f2_.CreateAnswer(offer.get(), CreatePlanBMediaSessionOptions(), NULL));
ASSERT_TRUE(answer != NULL);
ContentInfo* answer_content = answer->GetContentByName("audio");
ASSERT_TRUE(answer_content != NULL);
@@ -2643,7 +2770,7 @@
tdf2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer(
- f1_.CreateOffer(MediaSessionOptions(), NULL));
+ f1_.CreateOffer(CreatePlanBMediaSessionOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
ContentInfo* offer_content = offer->GetContentByName("audio");
ASSERT_TRUE(offer_content != NULL);
@@ -2652,7 +2779,7 @@
offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf);
std::unique_ptr<SessionDescription> answer(
- f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL));
+ f2_.CreateAnswer(offer.get(), CreatePlanBMediaSessionOptions(), NULL));
ASSERT_TRUE(answer != NULL);
const ContentInfo* answer_content = answer->GetContentByName("audio");
@@ -2673,8 +2800,7 @@
tdf1_.set_secure(SEC_ENABLED);
tdf2_.set_secure(SEC_DISABLED);
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
std::unique_ptr<SessionDescription> offer, answer;
const cricket::MediaContentDescription* audio_media_desc;
const cricket::MediaContentDescription* video_media_desc;
@@ -2770,7 +2896,7 @@
// Test that an answer can't be created if cryptos are required but the offer is
// unsecure.
TEST_F(MediaSessionDescriptionFactoryTest, TestSecureAnswerToUnsecureOffer) {
- MediaSessionOptions options;
+ MediaSessionOptions options = CreatePlanBMediaSessionOptions();
f1_.set_secure(SEC_DISABLED);
tdf1_.set_secure(SEC_DISABLED);
f2_.set_secure(SEC_REQUIRED);
@@ -2791,9 +2917,8 @@
tdf1_.set_secure(SEC_ENABLED);
tdf2_.set_secure(SEC_ENABLED);
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
- options.data_channel_type = cricket::DCT_RTP;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
+ AddDataSection(cricket::DCT_RTP, cricket::MD_RECVONLY, &options);
std::unique_ptr<SessionDescription> offer, answer;
@@ -2840,8 +2965,7 @@
// offer or answer.
TEST_F(MediaSessionDescriptionFactoryTest, TestVADEnableOption) {
MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &options);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(options, NULL));
ASSERT_TRUE(offer.get() != NULL);
const ContentInfo* audio_content = offer->GetContentByName("audio");
@@ -2859,22 +2983,21 @@
EXPECT_TRUE(VerifyNoCNCodecs(audio_content));
}
-// Test that the content name ("mid" in SDP) is unchanged when creating a
-// new offer.
-TEST_F(MediaSessionDescriptionFactoryTest,
- TestContentNameNotChangedInSubsequentOffers) {
+// Test that the generated MIDs match the existing offer.
+TEST_F(MediaSessionDescriptionFactoryTest, TestMIDsMatchesExistingOffer) {
MediaSessionOptions opts;
- opts.recv_audio = true;
- opts.recv_video = true;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio_modified", cricket::MD_RECVONLY,
+ kActive, &opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video_modified", cricket::MD_RECVONLY,
+ kActive, &opts);
opts.data_channel_type = cricket::DCT_SCTP;
- // Create offer and modify the default content names.
+ AddMediaSection(MEDIA_TYPE_DATA, "data_modified", cricket::MD_SENDRECV,
+ kActive, &opts);
+ // Create offer.
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
- for (ContentInfo& content : offer->contents()) {
- content.name.append("_modified");
- }
-
std::unique_ptr<SessionDescription> updated_offer(
f1_.CreateOffer(opts, offer.get()));
+
const ContentInfo* audio_content = GetFirstAudioContent(updated_offer.get());
const ContentInfo* video_content = GetFirstVideoContent(updated_offer.get());
const ContentInfo* data_content = GetFirstDataContent(updated_offer.get());
@@ -2886,6 +3009,340 @@
EXPECT_EQ("data_modified", data_content->name);
}
+// The following tests verify that the unified plan SDP is supported.
+// Test that we can create an offer with multiple media sections of same media
+// type.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ CreateOfferWithMultipleAVMediaSections) {
+ MediaSessionOptions opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio_1", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AttachSenderToMediaSection("audio_1", MEDIA_TYPE_AUDIO, kAudioTrack1,
+ kMediaStream1, 1, &opts);
+
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video_1", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AttachSenderToMediaSection("video_1", MEDIA_TYPE_VIDEO, kVideoTrack1,
+ kMediaStream1, 1, &opts);
+
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio_2", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AttachSenderToMediaSection("audio_2", MEDIA_TYPE_AUDIO, kAudioTrack2,
+ kMediaStream2, 1, &opts);
+
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video_2", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AttachSenderToMediaSection("video_2", MEDIA_TYPE_VIDEO, kVideoTrack2,
+ kMediaStream2, 1, &opts);
+ std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
+ ASSERT_TRUE(offer);
+
+ ASSERT_EQ(4u, offer->contents().size());
+ EXPECT_FALSE(offer->contents()[0].rejected);
+ const AudioContentDescription* acd =
+ static_cast<const AudioContentDescription*>(
+ offer->contents()[0].description);
+ ASSERT_EQ(1u, acd->streams().size());
+ EXPECT_EQ(kAudioTrack1, acd->streams()[0].id);
+ EXPECT_EQ(cricket::MD_SENDRECV, acd->direction());
+
+ EXPECT_FALSE(offer->contents()[1].rejected);
+ const VideoContentDescription* vcd =
+ static_cast<const VideoContentDescription*>(
+ offer->contents()[1].description);
+ ASSERT_EQ(1u, vcd->streams().size());
+ EXPECT_EQ(kVideoTrack1, vcd->streams()[0].id);
+ EXPECT_EQ(cricket::MD_SENDRECV, vcd->direction());
+
+ EXPECT_FALSE(offer->contents()[2].rejected);
+ acd = static_cast<const AudioContentDescription*>(
+ offer->contents()[2].description);
+ ASSERT_EQ(1u, acd->streams().size());
+ EXPECT_EQ(kAudioTrack2, acd->streams()[0].id);
+ EXPECT_EQ(cricket::MD_SENDRECV, acd->direction());
+
+ EXPECT_FALSE(offer->contents()[3].rejected);
+ vcd = static_cast<const VideoContentDescription*>(
+ offer->contents()[3].description);
+ ASSERT_EQ(1u, vcd->streams().size());
+ EXPECT_EQ(kVideoTrack2, vcd->streams()[0].id);
+ EXPECT_EQ(cricket::MD_SENDRECV, vcd->direction());
+}
+
+// Test that we can create an answer with multiple media sections of same media
+// type.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ CreateAnswerWithMultipleAVMediaSections) {
+ MediaSessionOptions opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio_1", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AttachSenderToMediaSection("audio_1", MEDIA_TYPE_AUDIO, kAudioTrack1,
+ kMediaStream1, 1, &opts);
+
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video_1", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AttachSenderToMediaSection("video_1", MEDIA_TYPE_VIDEO, kVideoTrack1,
+ kMediaStream1, 1, &opts);
+
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio_2", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AttachSenderToMediaSection("audio_2", MEDIA_TYPE_AUDIO, kAudioTrack2,
+ kMediaStream2, 1, &opts);
+
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video_2", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AttachSenderToMediaSection("video_2", MEDIA_TYPE_VIDEO, kVideoTrack2,
+ kMediaStream2, 1, &opts);
+
+ std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
+ ASSERT_TRUE(offer);
+ std::unique_ptr<SessionDescription> answer(
+ f2_.CreateAnswer(offer.get(), opts, nullptr));
+
+ ASSERT_EQ(4u, answer->contents().size());
+ EXPECT_FALSE(answer->contents()[0].rejected);
+ const AudioContentDescription* acd =
+ static_cast<const AudioContentDescription*>(
+ answer->contents()[0].description);
+ ASSERT_EQ(1u, acd->streams().size());
+ EXPECT_EQ(kAudioTrack1, acd->streams()[0].id);
+ EXPECT_EQ(cricket::MD_SENDRECV, acd->direction());
+
+ EXPECT_FALSE(answer->contents()[1].rejected);
+ const VideoContentDescription* vcd =
+ static_cast<const VideoContentDescription*>(
+ answer->contents()[1].description);
+ ASSERT_EQ(1u, vcd->streams().size());
+ EXPECT_EQ(kVideoTrack1, vcd->streams()[0].id);
+ EXPECT_EQ(cricket::MD_SENDRECV, vcd->direction());
+
+ EXPECT_FALSE(answer->contents()[2].rejected);
+ acd = static_cast<const AudioContentDescription*>(
+ answer->contents()[2].description);
+ ASSERT_EQ(1u, acd->streams().size());
+ EXPECT_EQ(kAudioTrack2, acd->streams()[0].id);
+ EXPECT_EQ(cricket::MD_SENDRECV, acd->direction());
+
+ EXPECT_FALSE(answer->contents()[3].rejected);
+ vcd = static_cast<const VideoContentDescription*>(
+ answer->contents()[3].description);
+ ASSERT_EQ(1u, vcd->streams().size());
+ EXPECT_EQ(kVideoTrack2, vcd->streams()[0].id);
+ EXPECT_EQ(cricket::MD_SENDRECV, vcd->direction());
+}
+
+// Test that the media section will be rejected in offer if the corresponding
+// MediaDescriptionOptions is stopped by the offerer.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ CreateOfferWithMediaSectionStoppedByOfferer) {
+ // Create an offer with two audio sections and one of them is stopped.
+ MediaSessionOptions offer_opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", cricket::MD_SENDRECV, kActive,
+ &offer_opts);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", cricket::MD_INACTIVE, kStopped,
+ &offer_opts);
+ std::unique_ptr<SessionDescription> offer(
+ f1_.CreateOffer(offer_opts, nullptr));
+ ASSERT_TRUE(offer);
+ ASSERT_EQ(2u, offer->contents().size());
+ EXPECT_FALSE(offer->contents()[0].rejected);
+ EXPECT_TRUE(offer->contents()[1].rejected);
+}
+
+// Test that the media section will be rejected in answer if the corresponding
+// MediaDescriptionOptions is stopped by the offerer.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ CreateAnswerWithMediaSectionStoppedByOfferer) {
+ // Create an offer with two audio sections and one of them is stopped.
+ MediaSessionOptions offer_opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", cricket::MD_SENDRECV, kActive,
+ &offer_opts);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", cricket::MD_INACTIVE, kStopped,
+ &offer_opts);
+ std::unique_ptr<SessionDescription> offer(
+ f1_.CreateOffer(offer_opts, nullptr));
+ ASSERT_TRUE(offer);
+ ASSERT_EQ(2u, offer->contents().size());
+ EXPECT_FALSE(offer->contents()[0].rejected);
+ EXPECT_TRUE(offer->contents()[1].rejected);
+
+ // Create an answer based on the offer.
+ MediaSessionOptions answer_opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", cricket::MD_SENDRECV, kActive,
+ &answer_opts);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", cricket::MD_SENDRECV, kActive,
+ &answer_opts);
+ std::unique_ptr<SessionDescription> answer(
+ f2_.CreateAnswer(offer.get(), answer_opts, nullptr));
+ ASSERT_EQ(2u, answer->contents().size());
+ EXPECT_FALSE(answer->contents()[0].rejected);
+ EXPECT_TRUE(answer->contents()[1].rejected);
+}
+
+// Test that the media section will be rejected in answer if the corresponding
+// MediaDescriptionOptions is stopped by the answerer.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ CreateAnswerWithMediaSectionRejectedByAnswerer) {
+ // Create an offer with two audio sections.
+ MediaSessionOptions offer_opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", cricket::MD_SENDRECV, kActive,
+ &offer_opts);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", cricket::MD_SENDRECV, kActive,
+ &offer_opts);
+ std::unique_ptr<SessionDescription> offer(
+ f1_.CreateOffer(offer_opts, nullptr));
+ ASSERT_TRUE(offer);
+ ASSERT_EQ(2u, offer->contents().size());
+ ASSERT_FALSE(offer->contents()[0].rejected);
+ ASSERT_FALSE(offer->contents()[1].rejected);
+
+ // The answerer rejects one of the audio sections.
+ MediaSessionOptions answer_opts;
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", cricket::MD_SENDRECV, kActive,
+ &answer_opts);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", cricket::MD_INACTIVE, kStopped,
+ &answer_opts);
+ std::unique_ptr<SessionDescription> answer(
+ f2_.CreateAnswer(offer.get(), answer_opts, nullptr));
+ ASSERT_EQ(2u, answer->contents().size());
+ EXPECT_FALSE(answer->contents()[0].rejected);
+ EXPECT_TRUE(answer->contents()[1].rejected);
+}
+
+// Test the generated media sections has the same order of the
+// corresponding MediaDescriptionOptions.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ CreateOfferRespectsMediaDescriptionOptionsOrder) {
+ MediaSessionOptions opts;
+ // This tests put video section first because normally audio comes first by
+ // default.
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", cricket::MD_SENDRECV, kActive,
+ &opts);
+ std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
+
+ ASSERT_TRUE(offer);
+ ASSERT_EQ(2u, offer->contents().size());
+ EXPECT_EQ("video", offer->contents()[0].name);
+ EXPECT_EQ("audio", offer->contents()[1].name);
+}
+
+// Test that different media sections using the same codec have same payload
+// type.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ PayloadTypesSharedByMediaSectionsOfSameType) {
+ MediaSessionOptions opts;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video1", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video2", cricket::MD_SENDRECV, kActive,
+ &opts);
+ // Create an offer with two video sections using same codecs.
+ std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
+ ASSERT_TRUE(offer);
+ ASSERT_EQ(2u, offer->contents().size());
+ const VideoContentDescription* vcd1 =
+ static_cast<const VideoContentDescription*>(
+ offer->contents()[0].description);
+ const VideoContentDescription* vcd2 =
+ static_cast<const VideoContentDescription*>(
+ offer->contents()[1].description);
+ EXPECT_EQ(vcd1->codecs().size(), vcd2->codecs().size());
+ ASSERT_EQ(2u, vcd1->codecs().size());
+ EXPECT_EQ(vcd1->codecs()[0].name, vcd2->codecs()[0].name);
+ EXPECT_EQ(vcd1->codecs()[0].id, vcd2->codecs()[0].id);
+ EXPECT_EQ(vcd1->codecs()[1].name, vcd2->codecs()[1].name);
+ EXPECT_EQ(vcd1->codecs()[1].id, vcd2->codecs()[1].id);
+
+ // Create answer and negotiate the codecs.
+ std::unique_ptr<SessionDescription> answer(
+ f2_.CreateAnswer(offer.get(), opts, nullptr));
+ ASSERT_TRUE(answer);
+ ASSERT_EQ(2u, answer->contents().size());
+ vcd1 = static_cast<const VideoContentDescription*>(
+ answer->contents()[0].description);
+ vcd2 = static_cast<const VideoContentDescription*>(
+ answer->contents()[1].description);
+ EXPECT_EQ(vcd1->codecs().size(), vcd2->codecs().size());
+ ASSERT_EQ(1u, vcd1->codecs().size());
+ EXPECT_EQ(vcd1->codecs()[0].name, vcd2->codecs()[0].name);
+ EXPECT_EQ(vcd1->codecs()[0].id, vcd2->codecs()[0].id);
+}
+
+// Test that the codec preference order per media section is respected in
+// subsequent offer.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ CreateOfferRespectsCodecPreferenceOrder) {
+ MediaSessionOptions opts;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video1", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video2", cricket::MD_SENDRECV, kActive,
+ &opts);
+ // Create an offer with two video sections using same codecs.
+ std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
+ ASSERT_TRUE(offer);
+ ASSERT_EQ(2u, offer->contents().size());
+ VideoContentDescription* vcd1 =
+ static_cast<VideoContentDescription*>(offer->contents()[0].description);
+ const VideoContentDescription* vcd2 =
+ static_cast<const VideoContentDescription*>(
+ offer->contents()[1].description);
+ auto video_codecs = MAKE_VECTOR(kVideoCodecs1);
+ EXPECT_EQ(video_codecs, vcd1->codecs());
+ EXPECT_EQ(video_codecs, vcd2->codecs());
+
+ // Change the codec preference of the first video section and create a
+ // follow-up offer.
+ auto video_codecs_reverse = MAKE_VECTOR(kVideoCodecs1Reverse);
+ vcd1->set_codecs(video_codecs_reverse);
+ std::unique_ptr<SessionDescription> updated_offer(
+ f1_.CreateOffer(opts, offer.get()));
+ vcd1 = static_cast<VideoContentDescription*>(
+ updated_offer->contents()[0].description);
+ vcd2 = static_cast<const VideoContentDescription*>(
+ updated_offer->contents()[1].description);
+ // The video codec preference order should be respected.
+ EXPECT_EQ(video_codecs_reverse, vcd1->codecs());
+ EXPECT_EQ(video_codecs, vcd2->codecs());
+}
+
+// Test that the codec preference order per media section is respected in
+// the answer.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ CreateAnswerRespectsCodecPreferenceOrder) {
+ MediaSessionOptions opts;
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video1", cricket::MD_SENDRECV, kActive,
+ &opts);
+ AddMediaSection(MEDIA_TYPE_VIDEO, "video2", cricket::MD_SENDRECV, kActive,
+ &opts);
+ // Create an offer with two video sections using same codecs.
+ std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
+ ASSERT_TRUE(offer);
+ ASSERT_EQ(2u, offer->contents().size());
+ VideoContentDescription* vcd1 =
+ static_cast<VideoContentDescription*>(offer->contents()[0].description);
+ const VideoContentDescription* vcd2 =
+ static_cast<const VideoContentDescription*>(
+ offer->contents()[1].description);
+ auto video_codecs = MAKE_VECTOR(kVideoCodecs1);
+ EXPECT_EQ(video_codecs, vcd1->codecs());
+ EXPECT_EQ(video_codecs, vcd2->codecs());
+
+ // Change the codec preference of the first video section and create an
+ // answer.
+ auto video_codecs_reverse = MAKE_VECTOR(kVideoCodecs1Reverse);
+ vcd1->set_codecs(video_codecs_reverse);
+ std::unique_ptr<SessionDescription> answer(
+ f1_.CreateAnswer(offer.get(), opts, nullptr));
+ vcd1 =
+ static_cast<VideoContentDescription*>(answer->contents()[0].description);
+ vcd2 = static_cast<const VideoContentDescription*>(
+ answer->contents()[1].description);
+ // The video codec preference order should be respected.
+ EXPECT_EQ(video_codecs_reverse, vcd1->codecs());
+ EXPECT_EQ(video_codecs, vcd2->codecs());
+}
+
class MediaProtocolTest : public ::testing::TestWithParam<const char*> {
public:
MediaProtocolTest() : f1_(&tdf1_), f2_(&tdf2_) {
@@ -2916,7 +3373,7 @@
TEST_P(MediaProtocolTest, TestAudioVideoAcceptance) {
MediaSessionOptions opts;
- opts.recv_video = true;
+ AddAudioVideoSections(cricket::MD_RECVONLY, &opts);
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
ASSERT_TRUE(offer.get() != nullptr);
// Set the protocol for all the contents.
@@ -2976,32 +3433,47 @@
// Test proper merge
sf.set_audio_codecs(send_codecs, recv_codecs);
- EXPECT_TRUE(sf.audio_send_codecs() == send_codecs);
- EXPECT_TRUE(sf.audio_recv_codecs() == recv_codecs);
- EXPECT_TRUE(sf.audio_sendrecv_codecs() == sendrecv_codecs);
+ EXPECT_EQ(send_codecs, sf.audio_send_codecs());
+ EXPECT_EQ(recv_codecs, sf.audio_recv_codecs());
+ EXPECT_EQ(sendrecv_codecs, sf.audio_sendrecv_codecs());
// Test empty send codecs list
sf.set_audio_codecs(no_codecs, recv_codecs);
- EXPECT_TRUE(sf.audio_send_codecs() == no_codecs);
- EXPECT_TRUE(sf.audio_recv_codecs() == recv_codecs);
- EXPECT_TRUE(sf.audio_sendrecv_codecs() == no_codecs);
+ EXPECT_EQ(no_codecs, sf.audio_send_codecs());
+ EXPECT_EQ(recv_codecs, sf.audio_recv_codecs());
+ EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs());
// Test empty recv codecs list
sf.set_audio_codecs(send_codecs, no_codecs);
- EXPECT_TRUE(sf.audio_send_codecs() == send_codecs);
- EXPECT_TRUE(sf.audio_recv_codecs() == no_codecs);
- EXPECT_TRUE(sf.audio_sendrecv_codecs() == no_codecs);
+ EXPECT_EQ(send_codecs, sf.audio_send_codecs());
+ EXPECT_EQ(no_codecs, sf.audio_recv_codecs());
+ EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs());
// Test all empty codec lists
sf.set_audio_codecs(no_codecs, no_codecs);
- EXPECT_TRUE(sf.audio_send_codecs() == no_codecs);
- EXPECT_TRUE(sf.audio_recv_codecs() == no_codecs);
- EXPECT_TRUE(sf.audio_sendrecv_codecs() == no_codecs);
+ EXPECT_EQ(no_codecs, sf.audio_send_codecs());
+ EXPECT_EQ(no_codecs, sf.audio_recv_codecs());
+ EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs());
}
namespace {
-void TestAudioCodecsOffer(MediaContentDirection direction,
- bool add_legacy_stream) {
+// Compare the two vectors of codecs ignoring the payload type.
+template <class Codec>
+bool CodecsMatch(const std::vector<Codec>& codecs1,
+ const std::vector<Codec>& codecs2) {
+ if (codecs1.size() != codecs2.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < codecs1.size(); ++i) {
+ if (!codecs1[i].Matches(codecs2[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TestAudioCodecsOffer(MediaContentDirection direction) {
TransportDescriptionFactory tdf;
MediaSessionDescriptionFactory sf(&tdf);
const std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
@@ -3009,57 +3481,56 @@
const std::vector<AudioCodec> sendrecv_codecs =
MAKE_VECTOR(kAudioCodecsAnswer);
sf.set_audio_codecs(send_codecs, recv_codecs);
- sf.set_add_legacy_streams(add_legacy_stream);
MediaSessionOptions opts;
- opts.recv_audio = (direction == cricket::MD_RECVONLY ||
- direction == cricket::MD_SENDRECV);
- opts.recv_video = false;
- if (direction == cricket::MD_SENDONLY || direction == cricket::MD_SENDRECV)
- opts.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack1, kMediaStream1);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", direction, kActive, &opts);
+
+ if (RtpTransceiverDirection::FromMediaContentDirection(direction).send) {
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
+ kMediaStream1, 1, &opts);
+ }
std::unique_ptr<SessionDescription> offer(sf.CreateOffer(opts, NULL));
ASSERT_TRUE(offer.get() != NULL);
const ContentInfo* ac = offer->GetContentByName("audio");
// If the factory didn't add any audio content to the offer, we cannot check
- // that the codecs put in are right. This happens when we neither want to send
- // nor receive audio. The checks are still in place if at some point we'd
- // instead create an inactive stream.
+ // that the codecs put in are right. This happens when we neither want to
+ // send nor receive audio. The checks are still in place if at some point
+ // we'd instead create an inactive stream.
if (ac) {
AudioContentDescription* acd =
static_cast<AudioContentDescription*>(ac->description);
- // sendrecv and inactive should both present lists as if the channel was to
- // be used for sending and receiving. Inactive essentially means it might
- // eventually be used anything, but we don't know more at this moment.
+ // sendrecv and inactive should both present lists as if the channel was
+ // to be used for sending and receiving. Inactive essentially means it
+ // might eventually be used anything, but we don't know more at this
+ // moment.
if (acd->direction() == cricket::MD_SENDONLY) {
- EXPECT_TRUE(acd->codecs() == send_codecs);
+ EXPECT_TRUE(CodecsMatch<AudioCodec>(send_codecs, acd->codecs()));
} else if (acd->direction() == cricket::MD_RECVONLY) {
- EXPECT_TRUE(acd->codecs() == recv_codecs);
+ EXPECT_TRUE(CodecsMatch<AudioCodec>(recv_codecs, acd->codecs()));
} else {
- EXPECT_TRUE(acd->codecs() == sendrecv_codecs);
+ EXPECT_TRUE(CodecsMatch<AudioCodec>(sendrecv_codecs, acd->codecs()));
}
}
}
static const AudioCodec kOfferAnswerCodecs[] = {
- AudioCodec(0, "codec0", 16000, -1, 1),
- AudioCodec(1, "codec1", 8000, 13300, 1),
- AudioCodec(2, "codec2", 8000, 64000, 1),
- AudioCodec(3, "codec3", 8000, 64000, 1),
- AudioCodec(4, "codec4", 8000, 0, 2),
- AudioCodec(5, "codec5", 32000, 0, 1),
- AudioCodec(6, "codec6", 48000, 0, 1)
-};
+ AudioCodec(0, "codec0", 16000, -1, 1),
+ AudioCodec(1, "codec1", 8000, 13300, 1),
+ AudioCodec(2, "codec2", 8000, 64000, 1),
+ AudioCodec(3, "codec3", 8000, 64000, 1),
+ AudioCodec(4, "codec4", 8000, 0, 2),
+ AudioCodec(5, "codec5", 32000, 0, 1),
+ AudioCodec(6, "codec6", 48000, 0, 1)};
-
-/* The codecs groups below are chosen as per the matrix below. The objective is
- * to have different sets of codecs in the inputs, to get unique sets of codecs
- * after negotiation, depending on offer and answer communication directions.
- * One-way directions in the offer should either result in the opposite
- * direction in the answer, or an inactive answer. Regardless, the choice of
- * codecs should be as if the answer contained the opposite direction.
- * Inactive offers should be treated as sendrecv/sendrecv.
+/* The codecs groups below are chosen as per the matrix below. The objective
+ * is to have different sets of codecs in the inputs, to get unique sets of
+ * codecs after negotiation, depending on offer and answer communication
+ * directions. One-way directions in the offer should either result in the
+ * opposite direction in the answer, or an inactive answer. Regardless, the
+ * choice of codecs should be as if the answer contained the opposite
+ * direction. Inactive offers should be treated as sendrecv/sendrecv.
*
* | Offer | Answer | Result
* codec|send recv sr | send recv sr | s/r r/s sr/s sr/r sr/sr
@@ -3072,18 +3543,18 @@
* 6 | x x x | x x x | x x x x x
*/
// Codecs used by offerer in the AudioCodecsAnswerTest
-static const int kOfferSendCodecs[] = { 0, 1, 3, 5, 6 };
-static const int kOfferRecvCodecs[] = { 1, 2, 3, 4, 6 };
+static const int kOfferSendCodecs[] = {0, 1, 3, 5, 6};
+static const int kOfferRecvCodecs[] = {1, 2, 3, 4, 6};
// Codecs used in the answerer in the AudioCodecsAnswerTest. The order is
// jumbled to catch the answer not following the order in the offer.
-static const int kAnswerSendCodecs[] = { 6, 5, 2, 3, 4 };
-static const int kAnswerRecvCodecs[] = { 6, 5, 4, 1, 0 };
+static const int kAnswerSendCodecs[] = {6, 5, 2, 3, 4};
+static const int kAnswerRecvCodecs[] = {6, 5, 4, 1, 0};
// The resulting sets of codecs in the answer in the AudioCodecsAnswerTest
-static const int kResultSend_RecvCodecs[] = { 0, 1, 5, 6 };
-static const int kResultRecv_SendCodecs[] = { 2, 3, 4, 6 };
-static const int kResultSendrecv_SendCodecs[] = { 3, 6 };
-static const int kResultSendrecv_RecvCodecs[] = { 1, 6 };
-static const int kResultSendrecv_SendrecvCodecs[] = { 6 };
+static const int kResultSend_RecvCodecs[] = {0, 1, 5, 6};
+static const int kResultRecv_SendCodecs[] = {2, 3, 4, 6};
+static const int kResultSendrecv_SendCodecs[] = {3, 6};
+static const int kResultSendrecv_RecvCodecs[] = {1, 6};
+static const int kResultSendrecv_SendrecvCodecs[] = {6};
template <typename T, int IDXS>
std::vector<T> VectorFromIndices(const T* array, const int (&indices)[IDXS]) {
@@ -3109,17 +3580,14 @@
VectorFromIndices(kOfferAnswerCodecs, kAnswerSendCodecs),
VectorFromIndices(kOfferAnswerCodecs, kAnswerRecvCodecs));
- // Never add a legacy stream to offer - we want to control the offer
- // parameters exactly.
- offer_factory.set_add_legacy_streams(false);
- answer_factory.set_add_legacy_streams(add_legacy_stream);
MediaSessionOptions offer_opts;
- offer_opts.recv_audio = (offer_direction == cricket::MD_RECVONLY ||
- offer_direction == cricket::MD_SENDRECV);
- offer_opts.recv_video = false;
- if (offer_direction == cricket::MD_SENDONLY ||
- offer_direction == cricket::MD_SENDRECV) {
- offer_opts.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack1, kMediaStream1);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", offer_direction, kActive,
+ &offer_opts);
+
+ if (RtpTransceiverDirection::FromMediaContentDirection(offer_direction)
+ .send) {
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
+ kMediaStream1, 1, &offer_opts);
}
std::unique_ptr<SessionDescription> offer(
@@ -3127,27 +3595,27 @@
ASSERT_TRUE(offer.get() != NULL);
MediaSessionOptions answer_opts;
- answer_opts.recv_audio = (answer_direction == cricket::MD_RECVONLY ||
- answer_direction == cricket::MD_SENDRECV);
- answer_opts.recv_video = false;
- if (answer_direction == cricket::MD_SENDONLY ||
- answer_direction == cricket::MD_SENDRECV) {
- answer_opts.AddSendStream(MEDIA_TYPE_AUDIO, kAudioTrack1, kMediaStream1);
+ AddMediaSection(MEDIA_TYPE_AUDIO, "audio", answer_direction, kActive,
+ &answer_opts);
+
+ if (RtpTransceiverDirection::FromMediaContentDirection(answer_direction)
+ .send) {
+ AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
+ kMediaStream1, 1, &answer_opts);
}
std::unique_ptr<SessionDescription> answer(
answer_factory.CreateAnswer(offer.get(), answer_opts, NULL));
const ContentInfo* ac = answer->GetContentByName("audio");
- // If the factory didn't add any audio content to the answer, we cannot check
- // that the codecs put in are right. This happens when we neither want to send
- // nor receive audio. The checks are still in place if at some point we'd
- // instead create an inactive stream.
+ // If the factory didn't add any audio content to the answer, we cannot
+ // check that the codecs put in are right. This happens when we neither want
+ // to send nor receive audio. The checks are still in place if at some point
+ // we'd instead create an inactive stream.
if (ac) {
const AudioContentDescription* acd =
static_cast<const AudioContentDescription*>(ac->description);
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
-
std::vector<AudioCodec> target_codecs;
// For offers with sendrecv or inactive, we should never reply with more
// codecs than offered, with these codec sets.
@@ -3157,20 +3625,20 @@
kResultSendrecv_SendrecvCodecs);
break;
case cricket::MD_SENDONLY:
- target_codecs = VectorFromIndices(kOfferAnswerCodecs,
- kResultSend_RecvCodecs);
+ target_codecs =
+ VectorFromIndices(kOfferAnswerCodecs, kResultSend_RecvCodecs);
break;
case cricket::MD_RECVONLY:
- target_codecs = VectorFromIndices(kOfferAnswerCodecs,
- kResultRecv_SendCodecs);
+ target_codecs =
+ VectorFromIndices(kOfferAnswerCodecs, kResultRecv_SendCodecs);
break;
case cricket::MD_SENDRECV:
if (acd->direction() == cricket::MD_SENDONLY) {
- target_codecs = VectorFromIndices(kOfferAnswerCodecs,
- kResultSendrecv_SendCodecs);
+ target_codecs =
+ VectorFromIndices(kOfferAnswerCodecs, kResultSendrecv_SendCodecs);
} else if (acd->direction() == cricket::MD_RECVONLY) {
- target_codecs = VectorFromIndices(kOfferAnswerCodecs,
- kResultSendrecv_RecvCodecs);
+ target_codecs =
+ VectorFromIndices(kOfferAnswerCodecs, kResultSendrecv_RecvCodecs);
} else {
target_codecs = VectorFromIndices(kOfferAnswerCodecs,
kResultSendrecv_SendrecvCodecs);
@@ -3178,7 +3646,7 @@
break;
}
- auto format_codecs = [] (const std::vector<AudioCodec>& codecs) {
+ auto format_codecs = [](const std::vector<AudioCodec>& codecs) {
std::stringstream os;
bool first = true;
os << "{";
@@ -3199,36 +3667,31 @@
<< "; got: " << MediaContentDirectionToString(acd->direction());
} else {
EXPECT_EQ(offer_direction, cricket::MD_INACTIVE)
- << "Only inactive offers are allowed to not generate any audio content";
+ << "Only inactive offers are allowed to not generate any audio "
+ "content";
}
}
} // namespace
class AudioCodecsOfferTest
- : public ::testing::TestWithParam<::testing::tuple<MediaContentDirection,
- bool>> {
-};
+ : public ::testing::TestWithParam<MediaContentDirection> {};
TEST_P(AudioCodecsOfferTest, TestCodecsInOffer) {
- TestAudioCodecsOffer(::testing::get<0>(GetParam()),
- ::testing::get<1>(GetParam()));
+ TestAudioCodecsOffer(GetParam());
}
INSTANTIATE_TEST_CASE_P(MediaSessionDescriptionFactoryTest,
AudioCodecsOfferTest,
- ::testing::Combine(
- ::testing::Values(cricket::MD_SENDONLY,
- cricket::MD_RECVONLY,
- cricket::MD_SENDRECV,
- cricket::MD_INACTIVE),
- ::testing::Bool()));
+ ::testing::Values(cricket::MD_SENDONLY,
+ cricket::MD_RECVONLY,
+ cricket::MD_SENDRECV,
+ cricket::MD_INACTIVE));
class AudioCodecsAnswerTest
: public ::testing::TestWithParam<::testing::tuple<MediaContentDirection,
MediaContentDirection,
- bool>> {
-};
+ bool>> {};
TEST_P(AudioCodecsAnswerTest, TestCodecsInAnswer) {
TestAudioCodecsAnswer(::testing::get<0>(GetParam()),
@@ -3236,15 +3699,15 @@
::testing::get<2>(GetParam()));
}
-INSTANTIATE_TEST_CASE_P(MediaSessionDescriptionFactoryTest,
- AudioCodecsAnswerTest,
- ::testing::Combine(
- ::testing::Values(cricket::MD_SENDONLY,
- cricket::MD_RECVONLY,
- cricket::MD_SENDRECV,
- cricket::MD_INACTIVE),
- ::testing::Values(cricket::MD_SENDONLY,
- cricket::MD_RECVONLY,
- cricket::MD_SENDRECV,
- cricket::MD_INACTIVE),
- ::testing::Bool()));
+INSTANTIATE_TEST_CASE_P(
+ MediaSessionDescriptionFactoryTest,
+ AudioCodecsAnswerTest,
+ ::testing::Combine(::testing::Values(cricket::MD_SENDONLY,
+ cricket::MD_RECVONLY,
+ cricket::MD_SENDRECV,
+ cricket::MD_INACTIVE),
+ ::testing::Values(cricket::MD_SENDONLY,
+ cricket::MD_RECVONLY,
+ cricket::MD_SENDRECV,
+ cricket::MD_INACTIVE),
+ ::testing::Bool()));
diff --git a/webrtc/pc/peerconnection.cc b/webrtc/pc/peerconnection.cc
index 3af5667..d975ed9 100644
--- a/webrtc/pc/peerconnection.cc
+++ b/webrtc/pc/peerconnection.cc
@@ -133,32 +133,45 @@
(value <= Options::kMaxOfferToReceiveMedia);
}
-// Add the stream and RTP data channel info to |session_options|.
-void AddSendStreams(
- cricket::MediaSessionOptions* session_options,
+// Add options to |[audio/video]_media_description_options| from |senders|.
+void AddRtpSenderOptions(
const std::vector<rtc::scoped_refptr<
RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
- const std::map<std::string, rtc::scoped_refptr<DataChannel>>&
- rtp_data_channels) {
- session_options->streams.clear();
+ cricket::MediaDescriptionOptions* audio_media_description_options,
+ cricket::MediaDescriptionOptions* video_media_description_options) {
for (const auto& sender : senders) {
- session_options->AddSendStream(sender->media_type(), sender->id(),
- sender->internal()->stream_id());
+ if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
+ if (audio_media_description_options) {
+ audio_media_description_options->AddAudioSender(
+ sender->id(), sender->internal()->stream_id());
+ }
+ } 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_id(), 1);
+ }
+ }
}
+}
+// Add options to |session_options| from |rtp_data_channels|.
+void AddRtpDataChannelOptions(
+ const std::map<std::string, rtc::scoped_refptr<DataChannel>>&
+ rtp_data_channels,
+ cricket::MediaDescriptionOptions* data_media_description_options) {
+ if (!data_media_description_options) {
+ return;
+ }
// Check for data channels.
for (const auto& kv : rtp_data_channels) {
const DataChannel* channel = kv.second;
if (channel->state() == DataChannel::kConnecting ||
channel->state() == DataChannel::kOpen) {
- // |streamid| and |sync_label| are both set to the DataChannel label
- // here so they can be signaled the same way as MediaStreams and Tracks.
- // For MediaStreams, the sync_label is the MediaStream label and the
- // track label is the same as |streamid|.
- const std::string& streamid = channel->label();
- const std::string& sync_label = channel->label();
- session_options->AddSendStream(cricket::MEDIA_TYPE_DATA, streamid,
- sync_label);
+ // Legacy RTP data channels are signaled with the track/stream ID set to
+ // the data channel's label.
+ data_media_description_options->AddRtpDataChannel(channel->label(),
+ channel->label());
}
}
}
@@ -314,92 +327,62 @@
return cname;
}
-bool ExtractMediaSessionOptions(
- const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
- bool is_offer,
- cricket::MediaSessionOptions* session_options) {
- typedef PeerConnectionInterface::RTCOfferAnswerOptions RTCOfferAnswerOptions;
- if (!IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) ||
- !IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video)) {
- return false;
- }
-
- // If constraints don't prevent us, we always accept video.
- if (rtc_options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) {
- session_options->recv_audio = (rtc_options.offer_to_receive_audio > 0);
- } else {
- session_options->recv_audio = true;
- }
- // For offers, we only offer video if we have it or it's forced by options.
- // For answers, we will always accept video (if offered).
- if (rtc_options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) {
- session_options->recv_video = (rtc_options.offer_to_receive_video > 0);
- } else if (is_offer) {
- session_options->recv_video = false;
- } else {
- session_options->recv_video = true;
- }
-
- session_options->vad_enabled = rtc_options.voice_activity_detection;
- session_options->bundle_enabled = rtc_options.use_rtp_mux;
- for (auto& kv : session_options->transport_options) {
- kv.second.ice_restart = rtc_options.ice_restart;
- }
-
- return true;
+bool ValidateOfferAnswerOptions(
+ const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) {
+ return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) &&
+ IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video);
}
-bool ParseConstraintsForAnswer(const MediaConstraintsInterface* constraints,
- cricket::MediaSessionOptions* session_options) {
- bool value = false;
- size_t mandatory_constraints_satisfied = 0;
+// From |rtc_options|, fill parts of |session_options| shared by all generated
+// m= sections (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;
+}
- // kOfferToReceiveAudio defaults to true according to spec.
- if (!FindConstraint(constraints,
- MediaConstraintsInterface::kOfferToReceiveAudio, &value,
- &mandatory_constraints_satisfied) ||
- value) {
- session_options->recv_audio = true;
- }
-
- // kOfferToReceiveVideo defaults to false according to spec. But
- // if it is an answer and video is offered, we should still accept video
- // per default.
- value = false;
- if (!FindConstraint(constraints,
- MediaConstraintsInterface::kOfferToReceiveVideo, &value,
- &mandatory_constraints_satisfied) ||
- value) {
- session_options->recv_video = true;
- }
-
- if (FindConstraint(constraints,
- MediaConstraintsInterface::kVoiceActivityDetection, &value,
- &mandatory_constraints_satisfied)) {
- session_options->vad_enabled = value;
- }
-
- if (FindConstraint(constraints, MediaConstraintsInterface::kUseRtpMux, &value,
- &mandatory_constraints_satisfied)) {
- session_options->bundle_enabled = value;
- } else {
- // kUseRtpMux defaults to true according to spec.
- session_options->bundle_enabled = true;
- }
-
- bool ice_restart = false;
- if (FindConstraint(constraints, MediaConstraintsInterface::kIceRestart,
- &value, &mandatory_constraints_satisfied)) {
- // kIceRestart defaults to false according to spec.
- ice_restart = true;
- }
- for (auto& kv : session_options->transport_options) {
- kv.second.ice_restart = ice_restart;
- }
-
+bool ConvertConstraintsToOfferAnswerOptions(
+ const MediaConstraintsInterface* constraints,
+ PeerConnectionInterface::RTCOfferAnswerOptions* offer_answer_options) {
if (!constraints) {
return true;
}
+
+ bool value = false;
+ size_t mandatory_constraints_satisfied = 0;
+
+ if (FindConstraint(constraints,
+ MediaConstraintsInterface::kOfferToReceiveAudio, &value,
+ &mandatory_constraints_satisfied)) {
+ offer_answer_options->offer_to_receive_audio =
+ value ? PeerConnectionInterface::RTCOfferAnswerOptions::
+ kOfferToReceiveMediaTrue
+ : 0;
+ }
+
+ if (FindConstraint(constraints,
+ MediaConstraintsInterface::kOfferToReceiveVideo, &value,
+ &mandatory_constraints_satisfied)) {
+ offer_answer_options->offer_to_receive_video =
+ value ? PeerConnectionInterface::RTCOfferAnswerOptions::
+ kOfferToReceiveMediaTrue
+ : 0;
+ }
+ if (FindConstraint(constraints,
+ MediaConstraintsInterface::kVoiceActivityDetection, &value,
+ &mandatory_constraints_satisfied)) {
+ offer_answer_options->voice_activity_detection = value;
+ }
+ if (FindConstraint(constraints, MediaConstraintsInterface::kUseRtpMux, &value,
+ &mandatory_constraints_satisfied)) {
+ offer_answer_options->use_rtp_mux = value;
+ }
+ if (FindConstraint(constraints, MediaConstraintsInterface::kIceRestart,
+ &value, &mandatory_constraints_satisfied)) {
+ offer_answer_options->ice_restart = value;
+ }
+
return mandatory_constraints_satisfied == constraints->GetMandatory().size();
}
@@ -842,49 +825,17 @@
LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
return;
}
- RTCOfferAnswerOptions options;
+ PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options;
+ // Always create an offer even if |ConvertConstraintsToOfferAnswerOptions|
+ // returns false for now. Because |ConvertConstraintsToOfferAnswerOptions|
+ // compares the mandatory fields parsed with the mandatory fields added in the
+ // |constraints| and some downstream applications might create offers with
+ // mandatory fields which would not be parsed in the helper method. For
+ // example, in Chromium/remoting, |kEnableDtlsSrtp| is added to the
+ // |constraints| as a mandatory field but it is not parsed.
+ ConvertConstraintsToOfferAnswerOptions(constraints, &offer_answer_options);
- bool value;
- size_t mandatory_constraints = 0;
-
- if (FindConstraint(constraints,
- MediaConstraintsInterface::kOfferToReceiveAudio,
- &value,
- &mandatory_constraints)) {
- options.offer_to_receive_audio =
- value ? RTCOfferAnswerOptions::kOfferToReceiveMediaTrue : 0;
- }
-
- if (FindConstraint(constraints,
- MediaConstraintsInterface::kOfferToReceiveVideo,
- &value,
- &mandatory_constraints)) {
- options.offer_to_receive_video =
- value ? RTCOfferAnswerOptions::kOfferToReceiveMediaTrue : 0;
- }
-
- if (FindConstraint(constraints,
- MediaConstraintsInterface::kVoiceActivityDetection,
- &value,
- &mandatory_constraints)) {
- options.voice_activity_detection = value;
- }
-
- if (FindConstraint(constraints,
- MediaConstraintsInterface::kIceRestart,
- &value,
- &mandatory_constraints)) {
- options.ice_restart = value;
- }
-
- if (FindConstraint(constraints,
- MediaConstraintsInterface::kUseRtpMux,
- &value,
- &mandatory_constraints)) {
- options.use_rtp_mux = value;
- }
-
- CreateOffer(observer, options);
+ CreateOffer(observer, offer_answer_options);
}
void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
@@ -895,14 +846,15 @@
return;
}
- cricket::MediaSessionOptions session_options;
- if (!GetOptionsForOffer(options, &session_options)) {
+ if (!ValidateOfferAnswerOptions(options)) {
std::string error = "CreateOffer called with invalid options.";
LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailure(observer, error);
return;
}
+ cricket::MediaSessionOptions session_options;
+ GetOptionsForOffer(options, &session_options);
session_->CreateOffer(observer, options, session_options);
}
@@ -915,14 +867,26 @@
return;
}
- cricket::MediaSessionOptions session_options;
- if (!GetOptionsForAnswer(constraints, &session_options)) {
+ if (!session_->remote_description() ||
+ session_->remote_description()->type() !=
+ SessionDescriptionInterface::kOffer) {
+ std::string error = "CreateAnswer called without remote offer.";
+ LOG(LS_ERROR) << error;
+ PostCreateSessionDescriptionFailure(observer, error);
+ return;
+ }
+
+ PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options;
+ if (!ConvertConstraintsToOfferAnswerOptions(constraints,
+ &offer_answer_options)) {
std::string error = "CreateAnswer called with invalid constraints.";
LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailure(observer, error);
return;
}
+ cricket::MediaSessionOptions session_options;
+ GetOptionsForAnswer(offer_answer_options, &session_options);
session_->CreateAnswer(observer, session_options);
}
@@ -935,12 +899,7 @@
}
cricket::MediaSessionOptions session_options;
- if (!GetOptionsForAnswer(options, &session_options)) {
- std::string error = "CreateAnswer called with invalid options.";
- LOG(LS_ERROR) << error;
- PostCreateSessionDescriptionFailure(observer, error);
- return;
- }
+ GetOptionsForAnswer(options, &session_options);
session_->CreateAnswer(observer, session_options);
}
@@ -1698,121 +1657,242 @@
MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
}
-bool PeerConnection::GetOptionsForOffer(
+void PeerConnection::GetOptionsForOffer(
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
cricket::MediaSessionOptions* session_options) {
- // TODO(deadbeef): Once we have transceivers, enumerate them here instead of
- // ContentInfos.
+ ExtractSharedMediaSessionOptions(rtc_options, session_options);
+
+ // Figure out transceiver directional preferences.
+ bool send_audio = HasRtpSender(cricket::MEDIA_TYPE_AUDIO);
+ bool send_video = HasRtpSender(cricket::MEDIA_TYPE_VIDEO);
+
+ // 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 = HasDataChannels();
+
+ // The "offer_to_receive_X" options allow those defaults to be overridden.
+ if (rtc_options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) {
+ recv_audio = (rtc_options.offer_to_receive_audio > 0);
+ offer_new_audio_description =
+ offer_new_audio_description || (rtc_options.offer_to_receive_audio > 0);
+ }
+ if (rtc_options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) {
+ recv_video = (rtc_options.offer_to_receive_video > 0);
+ offer_new_video_description =
+ offer_new_video_description || (rtc_options.offer_to_receive_video > 0);
+ }
+
+ int audio_index = -1;
+ int video_index = -1;
+ int data_index = -1;
+ // 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 (session_->local_description()) {
- for (const cricket::ContentInfo& content :
- session_->local_description()->description()->contents()) {
- session_options->transport_options[content.name] =
- cricket::TransportOptions();
- }
- }
- session_options->enable_ice_renomination =
- configuration_.enable_ice_renomination;
-
- if (!ExtractMediaSessionOptions(rtc_options, true, session_options)) {
- return false;
+ GenerateMediaDescriptionOptions(
+ session_->local_description(),
+ cricket::RtpTransceiverDirection(send_audio, recv_audio),
+ cricket::RtpTransceiverDirection(send_video, recv_video), &audio_index,
+ &video_index, &data_index, session_options);
}
- AddSendStreams(session_options, senders_, rtp_data_channels_);
- // Offer to receive audio/video if the constraint is not set and there are
- // send streams, or we're currently receiving.
- if (rtc_options.offer_to_receive_audio == RTCOfferAnswerOptions::kUndefined) {
- session_options->recv_audio =
- session_options->HasSendMediaStream(cricket::MEDIA_TYPE_AUDIO) ||
- !remote_audio_tracks_.empty();
+ // Add audio/video/data m= sections to the end if needed.
+ if (audio_index == -1 && offer_new_audio_description) {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
+ cricket::RtpTransceiverDirection(send_audio, recv_audio), false));
+ audio_index = session_options->media_description_options.size() - 1;
}
- if (rtc_options.offer_to_receive_video == RTCOfferAnswerOptions::kUndefined) {
- session_options->recv_video =
- session_options->HasSendMediaStream(cricket::MEDIA_TYPE_VIDEO) ||
- !remote_video_tracks_.empty();
+ if (video_index == -1 && offer_new_video_description) {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
+ cricket::RtpTransceiverDirection(send_video, recv_video), false));
+ video_index = session_options->media_description_options.size() - 1;
}
+ if (data_index == -1 && offer_new_data_description) {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_DATA, cricket::CN_DATA,
+ cricket::RtpTransceiverDirection(true, true), false));
+ data_index = session_options->media_description_options.size() - 1;
+ }
+
+ cricket::MediaDescriptionOptions* audio_media_description_options =
+ audio_index == -1
+ ? nullptr
+ : &session_options->media_description_options[audio_index];
+ cricket::MediaDescriptionOptions* video_media_description_options =
+ video_index == -1
+ ? nullptr
+ : &session_options->media_description_options[video_index];
+ cricket::MediaDescriptionOptions* data_media_description_options =
+ data_index == -1
+ ? nullptr
+ : &session_options->media_description_options[data_index];
+
+ // Apply ICE restart flag and renomination flag.
+ for (auto& options : session_options->media_description_options) {
+ options.transport_options.ice_restart = rtc_options.ice_restart;
+ options.transport_options.enable_ice_renomination =
+ configuration_.enable_ice_renomination;
+ }
+
+ AddRtpSenderOptions(senders_, audio_media_description_options,
+ video_media_description_options);
+ AddRtpDataChannelOptions(rtp_data_channels_, data_media_description_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 (HasDataChannels() && session_->data_channel_type() != cricket::DCT_RTP) {
+ if (!rtp_data_channels_.empty() ||
+ session_->data_channel_type() != cricket::DCT_RTP) {
session_options->data_channel_type = session_->data_channel_type();
}
- session_options->bundle_enabled =
- session_options->bundle_enabled &&
- (session_options->has_audio() || session_options->has_video() ||
- session_options->has_data());
-
session_options->rtcp_cname = rtcp_cname_;
session_options->crypto_options = factory_->options().crypto_options;
- return true;
}
-void PeerConnection::InitializeOptionsForAnswer(
+void PeerConnection::GetOptionsForAnswer(
+ const RTCOfferAnswerOptions& rtc_options,
cricket::MediaSessionOptions* session_options) {
- session_options->recv_audio = false;
- session_options->recv_video = false;
- session_options->enable_ice_renomination =
- configuration_.enable_ice_renomination;
-}
+ ExtractSharedMediaSessionOptions(rtc_options, session_options);
-void PeerConnection::FinishOptionsForAnswer(
- cricket::MediaSessionOptions* session_options) {
- // TODO(deadbeef): Once we have transceivers, enumerate them here instead of
- // ContentInfos.
- if (session_->remote_description()) {
- // Initialize the transport_options map.
- for (const cricket::ContentInfo& content :
- session_->remote_description()->description()->contents()) {
- session_options->transport_options[content.name] =
- cricket::TransportOptions();
- }
+ // Figure out transceiver directional preferences.
+ bool send_audio = HasRtpSender(cricket::MEDIA_TYPE_AUDIO);
+ bool send_video = HasRtpSender(cricket::MEDIA_TYPE_VIDEO);
+
+ // 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 (rtc_options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) {
+ recv_audio = (rtc_options.offer_to_receive_audio > 0);
}
- AddSendStreams(session_options, senders_, rtp_data_channels_);
- // RTP data channel is handled in MediaSessionOptions::AddStream. SCTP streams
- // are not signaled in the SDP so does not go through that path and must be
- // handled here.
+ if (rtc_options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) {
+ recv_video = (rtc_options.offer_to_receive_video > 0);
+ }
+
+ int audio_index = -1;
+ int video_index = -1;
+ int data_index = -1;
+ // There should be a pending remote description that's an offer...
+ RTC_DCHECK(session_->remote_description());
+ RTC_DCHECK(session_->remote_description()->type() ==
+ SessionDescriptionInterface::kOffer);
+ // Generate m= sections that match those in the offer.
+ // Note that mediasession.cc will handle intersection our preferred direction
+ // with the offered direction.
+ GenerateMediaDescriptionOptions(
+ session_->remote_description(),
+ cricket::RtpTransceiverDirection(send_audio, recv_audio),
+ cricket::RtpTransceiverDirection(send_video, recv_video), &audio_index,
+ &video_index, &data_index, session_options);
+
+ cricket::MediaDescriptionOptions* audio_media_description_options =
+ audio_index == -1
+ ? nullptr
+ : &session_options->media_description_options[audio_index];
+ cricket::MediaDescriptionOptions* video_media_description_options =
+ video_index == -1
+ ? nullptr
+ : &session_options->media_description_options[video_index];
+ cricket::MediaDescriptionOptions* data_media_description_options =
+ data_index == -1
+ ? nullptr
+ : &session_options->media_description_options[data_index];
+
+ // Apply ICE renomination flag.
+ for (auto& options : session_options->media_description_options) {
+ options.transport_options.enable_ice_renomination =
+ configuration_.enable_ice_renomination;
+ }
+
+ AddRtpSenderOptions(senders_, audio_media_description_options,
+ video_media_description_options);
+ AddRtpDataChannelOptions(rtp_data_channels_, data_media_description_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 (session_->data_channel_type() != cricket::DCT_RTP) {
+ if (!rtp_data_channels_.empty() ||
+ session_->data_channel_type() != cricket::DCT_RTP) {
session_options->data_channel_type = session_->data_channel_type();
}
- session_options->bundle_enabled =
- session_options->bundle_enabled &&
- (session_options->has_audio() || session_options->has_video() ||
- session_options->has_data());
+ session_options->rtcp_cname = rtcp_cname_;
session_options->crypto_options = factory_->options().crypto_options;
}
-bool PeerConnection::GetOptionsForAnswer(
- const MediaConstraintsInterface* constraints,
+void PeerConnection::GenerateMediaDescriptionOptions(
+ const SessionDescriptionInterface* session_desc,
+ cricket::RtpTransceiverDirection audio_direction,
+ cricket::RtpTransceiverDirection video_direction,
+ int* audio_index,
+ int* video_index,
+ int* data_index,
cricket::MediaSessionOptions* session_options) {
- InitializeOptionsForAnswer(session_options);
- if (!ParseConstraintsForAnswer(constraints, session_options)) {
- return false;
+ for (const cricket::ContentInfo& content :
+ session_desc->description()->contents()) {
+ if (IsAudioContent(&content)) {
+ // If we already have an audio m= section, reject this extra one.
+ if (*audio_index != -1) {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_AUDIO, content.name,
+ cricket::RtpTransceiverDirection(false, false), true));
+ } else {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_AUDIO, content.name, audio_direction,
+ !audio_direction.send && !audio_direction.recv));
+ *audio_index = session_options->media_description_options.size() - 1;
+ }
+ } else if (IsVideoContent(&content)) {
+ // If we already have an video m= section, reject this extra one.
+ if (*video_index != -1) {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_VIDEO, content.name,
+ cricket::RtpTransceiverDirection(false, false), true));
+ } else {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_VIDEO, content.name, video_direction,
+ !video_direction.send && !video_direction.recv));
+ *video_index = session_options->media_description_options.size() - 1;
+ }
+ } else {
+ RTC_DCHECK(IsDataContent(&content));
+ // If we already have an data m= section, reject this extra one.
+ if (*data_index != -1) {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_DATA, content.name,
+ cricket::RtpTransceiverDirection(false, false), true));
+ } else {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_DATA, content.name,
+ // Direction for data sections is meaningless, but legacy
+ // endpoints might expect sendrecv.
+ cricket::RtpTransceiverDirection(true, true), false));
+ *data_index = session_options->media_description_options.size() - 1;
+ }
+ }
}
- session_options->rtcp_cname = rtcp_cname_;
-
- FinishOptionsForAnswer(session_options);
- return true;
-}
-
-bool PeerConnection::GetOptionsForAnswer(
- const RTCOfferAnswerOptions& options,
- cricket::MediaSessionOptions* session_options) {
- InitializeOptionsForAnswer(session_options);
- if (!ExtractMediaSessionOptions(options, false, session_options)) {
- return false;
- }
- session_options->rtcp_cname = rtcp_cname_;
-
- FinishOptionsForAnswer(session_options);
- return true;
}
void PeerConnection::RemoveTracks(cricket::MediaType media_type) {
@@ -2285,6 +2365,15 @@
observer_->OnDataChannel(std::move(proxy_channel));
}
+bool PeerConnection::HasRtpSender(cricket::MediaType type) const {
+ return std::find_if(
+ senders_.begin(), senders_.end(),
+ [type](const rtc::scoped_refptr<
+ RtpSenderProxyWithInternal<RtpSenderInternal>>& sender) {
+ return sender->media_type() == type;
+ }) != senders_.end();
+}
+
RtpSenderInternal* PeerConnection::FindSenderById(const std::string& id) {
auto it = std::find_if(
senders_.begin(), senders_.end(),
diff --git a/webrtc/pc/peerconnection.h b/webrtc/pc/peerconnection.h
index f8b6a54..48e9628 100644
--- a/webrtc/pc/peerconnection.h
+++ b/webrtc/pc/peerconnection.h
@@ -32,28 +32,12 @@
class VideoRtpReceiver;
class RtcEventLog;
-// Populates |session_options| from |rtc_options|, and returns true if options
-// are valid.
-// |session_options|->transport_options map entries must exist in order for
-// them to be populated from |rtc_options|.
-bool ExtractMediaSessionOptions(
+// TODO(zhihuang): Remove this declaration when the WebRtcSession tests don't
+// need it.
+void ExtractSharedMediaSessionOptions(
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
- bool is_offer,
cricket::MediaSessionOptions* session_options);
-// Populates |session_options| from |constraints|, and returns true if all
-// mandatory constraints are satisfied.
-// Assumes that |session_options|->transport_options map entries exist.
-// Will also set defaults if corresponding constraints are not present:
-// recv_audio=true, recv_video=true, bundle_enabled=true.
-// Other fields will be left with existing values.
-//
-// Deprecated. Will be removed once callers that use constraints are gone.
-// TODO(hta): Remove when callers are gone.
-// https://bugs.chromium.org/p/webrtc/issues/detail?id=5617
-bool ParseConstraintsForAnswer(const MediaConstraintsInterface* constraints,
- cricket::MediaSessionOptions* session_options);
-
// PeerConnection implements the PeerConnectionInterface interface.
// It uses WebRtcSession to implement the PeerConnection functionality.
class PeerConnection : public PeerConnectionInterface,
@@ -244,26 +228,24 @@
// Returns a MediaSessionOptions struct with options decided by |options|,
// the local MediaStreams and DataChannels.
- virtual bool GetOptionsForOffer(
+ void GetOptionsForOffer(
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
cricket::MediaSessionOptions* session_options);
// Returns a MediaSessionOptions struct with options decided by
// |constraints|, the local MediaStreams and DataChannels.
- // Deprecated, use version without constraints.
- virtual bool GetOptionsForAnswer(
- const MediaConstraintsInterface* constraints,
- cricket::MediaSessionOptions* session_options);
- virtual bool GetOptionsForAnswer(
- const RTCOfferAnswerOptions& options,
- cricket::MediaSessionOptions* session_options);
+ void GetOptionsForAnswer(const RTCOfferAnswerOptions& options,
+ cricket::MediaSessionOptions* session_options);
- void InitializeOptionsForAnswer(
- cricket::MediaSessionOptions* session_options);
-
- // Helper function for options processing.
- // Deprecated.
- virtual void FinishOptionsForAnswer(
+ // Generates MediaDescriptionOptions for the |session_opts| based on existing
+ // local description or remote description.
+ void GenerateMediaDescriptionOptions(
+ const SessionDescriptionInterface* session_desc,
+ cricket::RtpTransceiverDirection audio_direction,
+ cricket::RtpTransceiverDirection video_direction,
+ int* audio_index,
+ int* video_index,
+ int* data_index,
cricket::MediaSessionOptions* session_options);
// Remove all local and remote tracks of type |media_type|.
@@ -361,6 +343,7 @@
void OnDataChannelOpenMessage(const std::string& label,
const InternalDataChannelInit& config);
+ bool HasRtpSender(cricket::MediaType type) const;
RtpSenderInternal* FindSenderById(const std::string& id);
std::vector<rtc::scoped_refptr<
diff --git a/webrtc/pc/peerconnectioninterface_unittest.cc b/webrtc/pc/peerconnectioninterface_unittest.cc
index 0e52c20..892615a 100644
--- a/webrtc/pc/peerconnectioninterface_unittest.cc
+++ b/webrtc/pc/peerconnectioninterface_unittest.cc
@@ -1184,6 +1184,57 @@
return audio_desc->streams()[0].cname;
}
+ std::unique_ptr<SessionDescriptionInterface> CreateOfferWithOptions(
+ const RTCOfferAnswerOptions& offer_answer_options) {
+ RTC_DCHECK(pc_);
+ rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer(
+ new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>());
+ pc_->CreateOffer(observer, offer_answer_options);
+ EXPECT_EQ_WAIT(true, observer->called(), kTimeout);
+ return observer->MoveDescription();
+ }
+
+ void CreateOfferWithOptionsAsRemoteDescription(
+ std::unique_ptr<SessionDescriptionInterface>* desc,
+ const RTCOfferAnswerOptions& offer_answer_options) {
+ *desc = CreateOfferWithOptions(offer_answer_options);
+ ASSERT_TRUE(desc != nullptr);
+ std::string sdp;
+ EXPECT_TRUE((*desc)->ToString(&sdp));
+ SessionDescriptionInterface* remote_offer =
+ webrtc::CreateSessionDescription(SessionDescriptionInterface::kOffer,
+ sdp, NULL);
+ EXPECT_TRUE(DoSetRemoteDescription(remote_offer));
+ EXPECT_EQ(PeerConnectionInterface::kHaveRemoteOffer, observer_.state_);
+ }
+
+ void CreateOfferWithOptionsAsLocalDescription(
+ std::unique_ptr<SessionDescriptionInterface>* desc,
+ const RTCOfferAnswerOptions& offer_answer_options) {
+ *desc = CreateOfferWithOptions(offer_answer_options);
+ ASSERT_TRUE(desc != nullptr);
+ std::string sdp;
+ EXPECT_TRUE((*desc)->ToString(&sdp));
+ SessionDescriptionInterface* new_offer = webrtc::CreateSessionDescription(
+ SessionDescriptionInterface::kOffer, sdp, NULL);
+
+ EXPECT_TRUE(DoSetLocalDescription(new_offer));
+ EXPECT_EQ(PeerConnectionInterface::kHaveLocalOffer, observer_.state_);
+ }
+
+ bool HasCNCodecs(const cricket::ContentInfo* content) {
+ const cricket::ContentDescription* description = content->description;
+ RTC_DCHECK(description);
+ const cricket::AudioContentDescription* audio_content_desc =
+ static_cast<const cricket::AudioContentDescription*>(description);
+ RTC_DCHECK(audio_content_desc);
+ for (size_t i = 0; i < audio_content_desc->codecs().size(); ++i) {
+ if (audio_content_desc->codecs()[i].name == "CN")
+ return true;
+ }
+ return false;
+ }
+
std::unique_ptr<rtc::VirtualSocketServer> vss_;
rtc::AutoSocketServerThread main_;
cricket::FakePortAllocator* port_allocator_ = nullptr;
@@ -3494,6 +3545,224 @@
EXPECT_TRUE(pc_->SetBitrate(bitrate).ok());
}
+// The following tests verify that the offer can be created correctly.
+TEST_F(PeerConnectionInterfaceTest,
+ CreateOfferFailsWithInvalidOfferToReceiveAudio) {
+ RTCOfferAnswerOptions rtc_options;
+
+ // Setting offer_to_receive_audio to a value lower than kUndefined or greater
+ // than kMaxOfferToReceiveMedia should be treated as invalid.
+ rtc_options.offer_to_receive_audio = RTCOfferAnswerOptions::kUndefined - 1;
+ CreatePeerConnection();
+ EXPECT_FALSE(CreateOfferWithOptions(rtc_options));
+
+ rtc_options.offer_to_receive_audio =
+ RTCOfferAnswerOptions::kMaxOfferToReceiveMedia + 1;
+ EXPECT_FALSE(CreateOfferWithOptions(rtc_options));
+}
+
+TEST_F(PeerConnectionInterfaceTest,
+ CreateOfferFailsWithInvalidOfferToReceiveVideo) {
+ RTCOfferAnswerOptions rtc_options;
+
+ // Setting offer_to_receive_video to a value lower than kUndefined or greater
+ // than kMaxOfferToReceiveMedia should be treated as invalid.
+ rtc_options.offer_to_receive_video = RTCOfferAnswerOptions::kUndefined - 1;
+ CreatePeerConnection();
+ EXPECT_FALSE(CreateOfferWithOptions(rtc_options));
+
+ rtc_options.offer_to_receive_video =
+ RTCOfferAnswerOptions::kMaxOfferToReceiveMedia + 1;
+ EXPECT_FALSE(CreateOfferWithOptions(rtc_options));
+}
+
+// Test that the audio and video content will be added to an offer if both
+// |offer_to_receive_audio| and |offer_to_receive_video| options are 1.
+TEST_F(PeerConnectionInterfaceTest, CreateOfferWithAudioVideoOptions) {
+ RTCOfferAnswerOptions rtc_options;
+ rtc_options.offer_to_receive_audio = 1;
+ rtc_options.offer_to_receive_video = 1;
+
+ std::unique_ptr<SessionDescriptionInterface> offer;
+ CreatePeerConnection();
+ offer = CreateOfferWithOptions(rtc_options);
+ ASSERT_TRUE(offer);
+ EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
+ EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
+}
+
+// Test that only audio content will be added to the offer if only
+// |offer_to_receive_audio| options is 1.
+TEST_F(PeerConnectionInterfaceTest, CreateOfferWithAudioOnlyOptions) {
+ RTCOfferAnswerOptions rtc_options;
+ rtc_options.offer_to_receive_audio = 1;
+ rtc_options.offer_to_receive_video = 0;
+
+ std::unique_ptr<SessionDescriptionInterface> offer;
+ CreatePeerConnection();
+ offer = CreateOfferWithOptions(rtc_options);
+ ASSERT_TRUE(offer);
+ EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
+ EXPECT_EQ(nullptr, GetFirstVideoContent(offer->description()));
+}
+
+// Test that only video content will be added if only |offer_to_receive_video|
+// options is 1.
+TEST_F(PeerConnectionInterfaceTest, CreateOfferWithVideoOnlyOptions) {
+ RTCOfferAnswerOptions rtc_options;
+ rtc_options.offer_to_receive_audio = 0;
+ rtc_options.offer_to_receive_video = 1;
+
+ std::unique_ptr<SessionDescriptionInterface> offer;
+ CreatePeerConnection();
+ offer = CreateOfferWithOptions(rtc_options);
+ ASSERT_TRUE(offer);
+ EXPECT_EQ(nullptr, GetFirstAudioContent(offer->description()));
+ EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
+}
+
+// Test that if |voice_activity_detection| is false, no CN codec is added to the
+// offer.
+TEST_F(PeerConnectionInterfaceTest, CreateOfferWithVADOptions) {
+ RTCOfferAnswerOptions rtc_options;
+ rtc_options.offer_to_receive_audio = 1;
+ rtc_options.offer_to_receive_video = 0;
+
+ std::unique_ptr<SessionDescriptionInterface> offer;
+ CreatePeerConnection();
+ offer = CreateOfferWithOptions(rtc_options);
+ ASSERT_TRUE(offer);
+ const cricket::ContentInfo* audio_content =
+ offer->description()->GetContentByName(cricket::CN_AUDIO);
+ ASSERT_TRUE(audio_content);
+ // |voice_activity_detection| is true by default.
+ EXPECT_TRUE(HasCNCodecs(audio_content));
+
+ rtc_options.voice_activity_detection = false;
+ CreatePeerConnection();
+ offer = CreateOfferWithOptions(rtc_options);
+ ASSERT_TRUE(offer);
+ audio_content = offer->description()->GetContentByName(cricket::CN_AUDIO);
+ ASSERT_TRUE(audio_content);
+ EXPECT_FALSE(HasCNCodecs(audio_content));
+}
+
+// Test that no media content will be added to the offer if using default
+// RTCOfferAnswerOptions.
+TEST_F(PeerConnectionInterfaceTest, CreateOfferWithDefaultOfferAnswerOptions) {
+ RTCOfferAnswerOptions rtc_options;
+
+ std::unique_ptr<SessionDescriptionInterface> offer;
+ CreatePeerConnection();
+ offer = CreateOfferWithOptions(rtc_options);
+ ASSERT_TRUE(offer);
+ EXPECT_EQ(nullptr, GetFirstAudioContent(offer->description()));
+ EXPECT_EQ(nullptr, GetFirstVideoContent(offer->description()));
+}
+
+// Test that if |ice_restart| is true, the ufrag/pwd will change, otherwise
+// ufrag/pwd will be the same in the new offer.
+TEST_F(PeerConnectionInterfaceTest, CreateOfferWithIceRestart) {
+ RTCOfferAnswerOptions rtc_options;
+ rtc_options.ice_restart = false;
+ rtc_options.offer_to_receive_audio = 1;
+
+ std::unique_ptr<SessionDescriptionInterface> offer;
+ CreatePeerConnection();
+ CreateOfferWithOptionsAsLocalDescription(&offer, rtc_options);
+ auto ufrag1 = offer->description()
+ ->GetTransportInfoByName(cricket::CN_AUDIO)
+ ->description.ice_ufrag;
+ auto pwd1 = offer->description()
+ ->GetTransportInfoByName(cricket::CN_AUDIO)
+ ->description.ice_pwd;
+
+ // |ice_restart| is false, the ufrag/pwd shouldn't change.
+ CreateOfferWithOptionsAsLocalDescription(&offer, rtc_options);
+ auto ufrag2 = offer->description()
+ ->GetTransportInfoByName(cricket::CN_AUDIO)
+ ->description.ice_ufrag;
+ auto pwd2 = offer->description()
+ ->GetTransportInfoByName(cricket::CN_AUDIO)
+ ->description.ice_pwd;
+
+ // |ice_restart| is true, the ufrag/pwd should change.
+ rtc_options.ice_restart = true;
+ CreateOfferWithOptionsAsLocalDescription(&offer, rtc_options);
+ auto ufrag3 = offer->description()
+ ->GetTransportInfoByName(cricket::CN_AUDIO)
+ ->description.ice_ufrag;
+ auto pwd3 = offer->description()
+ ->GetTransportInfoByName(cricket::CN_AUDIO)
+ ->description.ice_pwd;
+
+ EXPECT_EQ(ufrag1, ufrag2);
+ EXPECT_EQ(pwd1, pwd2);
+ EXPECT_NE(ufrag2, ufrag3);
+ EXPECT_NE(pwd2, pwd3);
+}
+
+// Test that if |use_rtp_mux| is true, the bundling will be enabled in the
+// offer; if it is false, there won't be any bundle group in the offer.
+TEST_F(PeerConnectionInterfaceTest, CreateOfferWithRtpMux) {
+ RTCOfferAnswerOptions rtc_options;
+ rtc_options.offer_to_receive_audio = 1;
+ rtc_options.offer_to_receive_video = 1;
+
+ std::unique_ptr<SessionDescriptionInterface> offer;
+ CreatePeerConnection();
+
+ rtc_options.use_rtp_mux = true;
+ offer = CreateOfferWithOptions(rtc_options);
+ ASSERT_TRUE(offer);
+ EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
+ EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
+ EXPECT_TRUE(offer->description()->HasGroup(cricket::GROUP_TYPE_BUNDLE));
+
+ rtc_options.use_rtp_mux = false;
+ offer = CreateOfferWithOptions(rtc_options);
+ ASSERT_TRUE(offer);
+ EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
+ EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
+ EXPECT_FALSE(offer->description()->HasGroup(cricket::GROUP_TYPE_BUNDLE));
+}
+
+// If SetMandatoryReceiveAudio(false) and SetMandatoryReceiveVideo(false) are
+// called for the answer constraints, but an audio and a video section were
+// offered, there will still be an audio and a video section in the answer.
+TEST_F(PeerConnectionInterfaceTest,
+ RejectAudioAndVideoInAnswerWithConstraints) {
+ // Offer both audio and video.
+ RTCOfferAnswerOptions rtc_offer_options;
+ rtc_offer_options.offer_to_receive_audio = 1;
+ rtc_offer_options.offer_to_receive_video = 1;
+
+ CreatePeerConnection();
+ std::unique_ptr<SessionDescriptionInterface> offer;
+ CreateOfferWithOptionsAsRemoteDescription(&offer, rtc_offer_options);
+ EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
+ EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
+
+ // Since an offer has been created with both audio and video,
+ // Answers will contain the media types that exist in the offer regardless of
+ // the value of |answer_options.has_audio| and |answer_options.has_video|.
+ FakeConstraints answer_c;
+ // Reject both audio and video.
+ answer_c.SetMandatoryReceiveAudio(false);
+ answer_c.SetMandatoryReceiveVideo(false);
+
+ std::unique_ptr<SessionDescriptionInterface> answer;
+ ASSERT_TRUE(DoCreateAnswer(&answer, &answer_c));
+ const cricket::ContentInfo* audio_content =
+ GetFirstAudioContent(answer->description());
+ const cricket::ContentInfo* video_content =
+ GetFirstVideoContent(answer->description());
+ ASSERT_NE(nullptr, audio_content);
+ ASSERT_NE(nullptr, video_content);
+ EXPECT_TRUE(audio_content->rejected);
+ EXPECT_TRUE(video_content->rejected);
+}
+
class PeerConnectionMediaConfigTest : public testing::Test {
protected:
void SetUp() override {
@@ -3502,8 +3771,7 @@
}
const cricket::MediaConfig TestCreatePeerConnection(
const PeerConnectionInterface::RTCConfiguration& config,
- const MediaConstraintsInterface *constraints) {
-
+ const MediaConstraintsInterface* constraints) {
rtc::scoped_refptr<PeerConnectionInterface> pc(pcf_->CreatePeerConnection(
config, constraints, nullptr, nullptr, &observer_));
EXPECT_TRUE(pc.get());
@@ -3585,177 +3853,6 @@
EXPECT_TRUE(media_config.video.suspend_below_min_bitrate);
}
-// The following tests verify that session options are created correctly.
-// TODO(deadbeef): Convert these tests to be more end-to-end. Instead of
-// "verify options are converted correctly", should be "pass options into
-// CreateOffer and verify the correct offer is produced."
-
-TEST(CreateSessionOptionsTest, GetOptionsForOfferWithInvalidAudioOption) {
- RTCOfferAnswerOptions rtc_options;
- rtc_options.offer_to_receive_audio = RTCOfferAnswerOptions::kUndefined - 1;
-
- cricket::MediaSessionOptions options;
- EXPECT_FALSE(ExtractMediaSessionOptions(rtc_options, true, &options));
-
- rtc_options.offer_to_receive_audio =
- RTCOfferAnswerOptions::kMaxOfferToReceiveMedia + 1;
- EXPECT_FALSE(ExtractMediaSessionOptions(rtc_options, true, &options));
-}
-
-TEST(CreateSessionOptionsTest, GetOptionsForOfferWithInvalidVideoOption) {
- RTCOfferAnswerOptions rtc_options;
- rtc_options.offer_to_receive_video = RTCOfferAnswerOptions::kUndefined - 1;
-
- cricket::MediaSessionOptions options;
- EXPECT_FALSE(ExtractMediaSessionOptions(rtc_options, true, &options));
-
- rtc_options.offer_to_receive_video =
- RTCOfferAnswerOptions::kMaxOfferToReceiveMedia + 1;
- EXPECT_FALSE(ExtractMediaSessionOptions(rtc_options, true, &options));
-}
-
-// Test that a MediaSessionOptions is created for an offer if
-// OfferToReceiveAudio and OfferToReceiveVideo options are set.
-TEST(CreateSessionOptionsTest, GetMediaSessionOptionsForOfferWithAudioVideo) {
- RTCOfferAnswerOptions rtc_options;
- rtc_options.offer_to_receive_audio = 1;
- rtc_options.offer_to_receive_video = 1;
-
- cricket::MediaSessionOptions options;
- EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
- EXPECT_TRUE(options.has_audio());
- EXPECT_TRUE(options.has_video());
- EXPECT_TRUE(options.bundle_enabled);
-}
-
-// Test that a correct MediaSessionOptions is created for an offer if
-// OfferToReceiveAudio is set.
-TEST(CreateSessionOptionsTest, GetMediaSessionOptionsForOfferWithAudio) {
- RTCOfferAnswerOptions rtc_options;
- rtc_options.offer_to_receive_audio = 1;
-
- cricket::MediaSessionOptions options;
- EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
- EXPECT_TRUE(options.has_audio());
- EXPECT_FALSE(options.has_video());
- EXPECT_TRUE(options.bundle_enabled);
-}
-
-// Test that a correct MediaSessionOptions is created for an offer if
-// the default OfferOptions are used.
-TEST(CreateSessionOptionsTest, GetDefaultMediaSessionOptionsForOffer) {
- RTCOfferAnswerOptions rtc_options;
-
- cricket::MediaSessionOptions options;
- options.transport_options["audio"] = cricket::TransportOptions();
- options.transport_options["video"] = cricket::TransportOptions();
- EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
- EXPECT_TRUE(options.has_audio());
- EXPECT_FALSE(options.has_video());
- EXPECT_TRUE(options.bundle_enabled);
- EXPECT_TRUE(options.vad_enabled);
- EXPECT_FALSE(options.transport_options["audio"].ice_restart);
- EXPECT_FALSE(options.transport_options["video"].ice_restart);
-}
-
-// Test that a correct MediaSessionOptions is created for an offer if
-// OfferToReceiveVideo is set.
-TEST(CreateSessionOptionsTest, GetMediaSessionOptionsForOfferWithVideo) {
- RTCOfferAnswerOptions rtc_options;
- rtc_options.offer_to_receive_audio = 0;
- rtc_options.offer_to_receive_video = 1;
-
- cricket::MediaSessionOptions options;
- EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
- EXPECT_FALSE(options.has_audio());
- EXPECT_TRUE(options.has_video());
- EXPECT_TRUE(options.bundle_enabled);
-}
-
-// Test that a correct MediaSessionOptions is created for an offer if
-// UseRtpMux is set to false.
-TEST(CreateSessionOptionsTest,
- GetMediaSessionOptionsForOfferWithBundleDisabled) {
- RTCOfferAnswerOptions rtc_options;
- rtc_options.offer_to_receive_audio = 1;
- rtc_options.offer_to_receive_video = 1;
- rtc_options.use_rtp_mux = false;
-
- cricket::MediaSessionOptions options;
- EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
- EXPECT_TRUE(options.has_audio());
- EXPECT_TRUE(options.has_video());
- EXPECT_FALSE(options.bundle_enabled);
-}
-
-// Test that a correct MediaSessionOptions is created to restart ice if
-// IceRestart is set. It also tests that subsequent MediaSessionOptions don't
-// have |audio_transport_options.ice_restart| etc. set.
-TEST(CreateSessionOptionsTest, GetMediaSessionOptionsForOfferWithIceRestart) {
- RTCOfferAnswerOptions rtc_options;
- rtc_options.ice_restart = true;
-
- cricket::MediaSessionOptions options;
- options.transport_options["audio"] = cricket::TransportOptions();
- options.transport_options["video"] = cricket::TransportOptions();
- EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
- EXPECT_TRUE(options.transport_options["audio"].ice_restart);
- EXPECT_TRUE(options.transport_options["video"].ice_restart);
-
- rtc_options = RTCOfferAnswerOptions();
- EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
- EXPECT_FALSE(options.transport_options["audio"].ice_restart);
- EXPECT_FALSE(options.transport_options["video"].ice_restart);
-}
-
-// Test that the MediaConstraints in an answer don't affect if audio and video
-// is offered in an offer but that if kOfferToReceiveAudio or
-// kOfferToReceiveVideo constraints are true in an offer, the media type will be
-// included in subsequent answers.
-TEST(CreateSessionOptionsTest, MediaConstraintsInAnswer) {
- FakeConstraints answer_c;
- answer_c.SetMandatoryReceiveAudio(true);
- answer_c.SetMandatoryReceiveVideo(true);
-
- cricket::MediaSessionOptions answer_options;
- EXPECT_TRUE(ParseConstraintsForAnswer(&answer_c, &answer_options));
- EXPECT_TRUE(answer_options.has_audio());
- EXPECT_TRUE(answer_options.has_video());
-
- RTCOfferAnswerOptions rtc_offer_options;
-
- cricket::MediaSessionOptions offer_options;
- EXPECT_TRUE(
- ExtractMediaSessionOptions(rtc_offer_options, false, &offer_options));
- EXPECT_TRUE(offer_options.has_audio());
- EXPECT_TRUE(offer_options.has_video());
-
- RTCOfferAnswerOptions updated_rtc_offer_options;
- updated_rtc_offer_options.offer_to_receive_audio = 1;
- updated_rtc_offer_options.offer_to_receive_video = 1;
-
- cricket::MediaSessionOptions updated_offer_options;
- EXPECT_TRUE(ExtractMediaSessionOptions(updated_rtc_offer_options, false,
- &updated_offer_options));
- EXPECT_TRUE(updated_offer_options.has_audio());
- EXPECT_TRUE(updated_offer_options.has_video());
-
- // Since an offer has been created with both audio and video, subsequent
- // offers and answers should contain both audio and video.
- // Answers will only contain the media types that exist in the offer
- // regardless of the value of |updated_answer_options.has_audio| and
- // |updated_answer_options.has_video|.
- FakeConstraints updated_answer_c;
- answer_c.SetMandatoryReceiveAudio(false);
- answer_c.SetMandatoryReceiveVideo(false);
-
- cricket::MediaSessionOptions updated_answer_options;
- EXPECT_TRUE(
- ParseConstraintsForAnswer(&updated_answer_c, &updated_answer_options));
- EXPECT_TRUE(updated_answer_options.has_audio());
- EXPECT_TRUE(updated_answer_options.has_video());
-}
-
// Tests a few random fields being different.
TEST(RTCConfigurationTest, ComparisonOperators) {
PeerConnectionInterface::RTCConfiguration a;
diff --git a/webrtc/pc/webrtcsession_unittest.cc b/webrtc/pc/webrtcsession_unittest.cc
index f6d334e..bb1c877 100644
--- a/webrtc/pc/webrtcsession_unittest.cc
+++ b/webrtc/pc/webrtcsession_unittest.cc
@@ -143,6 +143,9 @@
static const char kVideoTrack2[] = "video2";
static const char kAudioTrack2[] = "audio2";
+static constexpr bool kStopped = true;
+static constexpr bool kActive = false;
+
enum RTCCertificateGenerationMethod { ALREADY_GENERATED, DTLS_IDENTITY_STORE };
class MockIceObserver : public webrtc::IceObserver {
@@ -398,7 +401,6 @@
allocator_->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
cricket::PORTALLOCATOR_DISABLE_RELAY);
EXPECT_TRUE(channel_manager_->Init());
- desc_factory_->set_add_legacy_streams(false);
allocator_->set_step_delay(cricket::kMinimumStepDelay);
}
@@ -507,107 +509,237 @@
InitWithCryptoOptions(crypto_options);
}
+ // The following convenience functions can be applied for both local side and
+ // remote side. The flags can be overwritten for different use cases.
void SendAudioVideoStream1() {
send_stream_1_ = true;
send_stream_2_ = false;
- send_audio_ = true;
- send_video_ = true;
+ local_send_audio_ = true;
+ local_send_video_ = true;
+ remote_send_audio_ = true;
+ remote_send_video_ = true;
}
void SendAudioVideoStream2() {
send_stream_1_ = false;
send_stream_2_ = true;
- send_audio_ = true;
- send_video_ = true;
+ local_send_audio_ = true;
+ local_send_video_ = true;
+ remote_send_audio_ = true;
+ remote_send_video_ = true;
}
void SendAudioVideoStream1And2() {
send_stream_1_ = true;
send_stream_2_ = true;
- send_audio_ = true;
- send_video_ = true;
+ local_send_audio_ = true;
+ local_send_video_ = true;
+ remote_send_audio_ = true;
+ remote_send_video_ = true;
}
void SendNothing() {
send_stream_1_ = false;
send_stream_2_ = false;
- send_audio_ = false;
- send_video_ = false;
+ local_send_audio_ = false;
+ local_send_video_ = false;
+ remote_send_audio_ = false;
+ remote_send_video_ = false;
}
void SendAudioOnlyStream2() {
send_stream_1_ = false;
send_stream_2_ = true;
- send_audio_ = true;
- send_video_ = false;
+ local_send_audio_ = true;
+ local_send_video_ = false;
+ remote_send_audio_ = true;
+ remote_send_video_ = false;
}
void SendVideoOnlyStream2() {
send_stream_1_ = false;
send_stream_2_ = true;
- send_audio_ = false;
- send_video_ = true;
+ local_send_audio_ = false;
+ local_send_video_ = true;
+ remote_send_audio_ = false;
+ remote_send_video_ = true;
}
- void AddStreamsToOptions(cricket::MediaSessionOptions* session_options) {
- if (send_stream_1_ && send_audio_) {
- session_options->AddSendStream(cricket::MEDIA_TYPE_AUDIO, kAudioTrack1,
- kStream1);
+ // Helper function used to add a specific media section to the
+ // |session_options|.
+ void AddMediaSection(cricket::MediaType type,
+ const std::string& mid,
+ cricket::MediaContentDirection direction,
+ bool stopped,
+ cricket::MediaSessionOptions* opts) {
+ opts->media_description_options.push_back(cricket::MediaDescriptionOptions(
+ type, mid,
+ cricket::RtpTransceiverDirection::FromMediaContentDirection(direction),
+ stopped));
+ }
+
+ // Add the media sections to the options from |offered_media_sections_| when
+ // creating an answer or a new offer.
+ // This duplicates a lot of logic from PeerConnection but this can be fixed
+ // when PeerConnection and WebRtcSession are merged.
+ void AddExistingMediaSectionsAndSendersToOptions(
+ cricket::MediaSessionOptions* session_options,
+ bool send_audio,
+ bool recv_audio,
+ bool send_video,
+ bool recv_video) {
+ int num_sim_layer = 1;
+ for (auto media_description_options : offered_media_sections_) {
+ if (media_description_options.type == cricket::MEDIA_TYPE_AUDIO) {
+ bool stopped = !send_audio && !recv_audio;
+ auto media_desc_options = cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_AUDIO, media_description_options.mid,
+ cricket::RtpTransceiverDirection(send_audio, recv_audio), stopped);
+ if (send_stream_1_ && send_audio) {
+ media_desc_options.AddAudioSender(kAudioTrack1, kStream1);
+ }
+ if (send_stream_2_ && send_audio) {
+ media_desc_options.AddAudioSender(kAudioTrack2, kStream2);
+ }
+ session_options->media_description_options.push_back(
+ media_desc_options);
+ } else if (media_description_options.type == cricket::MEDIA_TYPE_VIDEO) {
+ bool stopped = !send_video && !recv_video;
+ auto media_desc_options = cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_VIDEO, media_description_options.mid,
+ cricket::RtpTransceiverDirection(send_video, recv_video), stopped);
+ if (send_stream_1_ && send_video) {
+ media_desc_options.AddVideoSender(kVideoTrack1, kStream1,
+ num_sim_layer);
+ }
+ if (send_stream_2_ && send_video) {
+ media_desc_options.AddVideoSender(kVideoTrack2, kStream2,
+ num_sim_layer);
+ }
+ session_options->media_description_options.push_back(
+ media_desc_options);
+ } else if (media_description_options.type == cricket::MEDIA_TYPE_DATA) {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_DATA, media_description_options.mid,
+ // Direction for data sections is meaningless, but legacy
+ // endpoints might expect sendrecv.
+ cricket::RtpTransceiverDirection(true, true), false));
+ } else {
+ RTC_NOTREACHED();
+ }
}
- if (send_stream_1_ && send_video_) {
- session_options->AddSendStream(cricket::MEDIA_TYPE_VIDEO, kVideoTrack1,
- kStream1);
+ }
+
+ // Add the existing media sections first and then add new media sections if
+ // needed.
+ void AddMediaSectionsAndSendersToOptions(
+ cricket::MediaSessionOptions* session_options,
+ bool send_audio,
+ bool recv_audio,
+ bool send_video,
+ bool recv_video) {
+ AddExistingMediaSectionsAndSendersToOptions(
+ session_options, send_audio, recv_audio, send_video, recv_video);
+
+ if (!session_options->has_audio() && (send_audio || recv_audio)) {
+ cricket::MediaDescriptionOptions media_desc_options =
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
+ cricket::RtpTransceiverDirection(send_audio, recv_audio),
+ kActive);
+ if (send_stream_1_ && send_audio) {
+ media_desc_options.AddAudioSender(kAudioTrack1, kStream1);
+ }
+ if (send_stream_2_ && send_audio) {
+ media_desc_options.AddAudioSender(kAudioTrack2, kStream2);
+ }
+ session_options->media_description_options.push_back(media_desc_options);
+ offered_media_sections_.push_back(media_desc_options);
}
- if (send_stream_2_ && send_audio_) {
- session_options->AddSendStream(cricket::MEDIA_TYPE_AUDIO, kAudioTrack2,
- kStream2);
+
+ if (!session_options->has_video() && (send_video || recv_video)) {
+ cricket::MediaDescriptionOptions media_desc_options =
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
+ cricket::RtpTransceiverDirection(send_video, recv_video),
+ kActive);
+ int num_sim_layer = 1;
+ if (send_stream_1_ && send_video) {
+ media_desc_options.AddVideoSender(kVideoTrack1, kStream1,
+ num_sim_layer);
+ }
+ if (send_stream_2_ && send_video) {
+ media_desc_options.AddVideoSender(kVideoTrack2, kStream2,
+ num_sim_layer);
+ }
+ session_options->media_description_options.push_back(media_desc_options);
+ offered_media_sections_.push_back(media_desc_options);
}
- if (send_stream_2_ && send_video_) {
- session_options->AddSendStream(cricket::MEDIA_TYPE_VIDEO, kVideoTrack2,
- kStream2);
- }
- if (data_channel_ && session_->data_channel_type() == cricket::DCT_RTP) {
- session_options->AddSendStream(cricket::MEDIA_TYPE_DATA,
- data_channel_->label(),
- data_channel_->label());
+
+ if (!session_options->has_data() &&
+ (data_channel_ ||
+ session_options->data_channel_type != cricket::DCT_NONE)) {
+ cricket::MediaDescriptionOptions media_desc_options =
+ cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_DATA, cricket::CN_DATA,
+ cricket::RtpTransceiverDirection(true, true), kActive);
+ if (session_options->data_channel_type == cricket::DCT_RTP) {
+ media_desc_options.AddRtpDataChannel(data_channel_->label(),
+ data_channel_->label());
+ }
+ session_options->media_description_options.push_back(media_desc_options);
+ offered_media_sections_.push_back(media_desc_options);
}
}
void GetOptionsForOffer(
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
cricket::MediaSessionOptions* session_options) {
- ASSERT_TRUE(ExtractMediaSessionOptions(rtc_options, true, session_options));
+ ExtractSharedMediaSessionOptions(rtc_options, session_options);
- AddStreamsToOptions(session_options);
- if (rtc_options.offer_to_receive_audio ==
- RTCOfferAnswerOptions::kUndefined) {
- session_options->recv_audio =
- session_options->HasSendMediaStream(cricket::MEDIA_TYPE_AUDIO);
- }
- if (rtc_options.offer_to_receive_video ==
- RTCOfferAnswerOptions::kUndefined) {
- session_options->recv_video =
- session_options->HasSendMediaStream(cricket::MEDIA_TYPE_VIDEO);
- }
+ // |recv_X| is true by default if |offer_to_receive_X| is undefined.
+ bool recv_audio = rtc_options.offer_to_receive_audio != 0;
+ bool recv_video = rtc_options.offer_to_receive_video != 0;
+
+ AddMediaSectionsAndSendersToOptions(session_options, local_send_audio_,
+ recv_audio, local_send_video_,
+ recv_video);
session_options->bundle_enabled =
session_options->bundle_enabled &&
(session_options->has_audio() || session_options->has_video() ||
session_options->has_data());
- if (session_->data_channel_type() == cricket::DCT_SCTP && data_channel_) {
- session_options->data_channel_type = cricket::DCT_SCTP;
- } else if (session_->data_channel_type() == cricket::DCT_QUIC) {
- session_options->data_channel_type = cricket::DCT_QUIC;
+ session_options->crypto_options = crypto_options_;
+ }
+
+ void GetOptionsForAnswer(cricket::MediaSessionOptions* session_options) {
+ AddExistingMediaSectionsAndSendersToOptions(
+ session_options, local_send_audio_, local_recv_audio_,
+ local_send_video_, local_recv_video_);
+
+ session_options->bundle_enabled =
+ session_options->bundle_enabled &&
+ (session_options->has_audio() || session_options->has_video() ||
+ session_options->has_data());
+
+ if (session_->data_channel_type() != cricket::DCT_RTP) {
+ session_options->data_channel_type = session_->data_channel_type();
}
session_options->crypto_options = crypto_options_;
}
- void GetOptionsForAnswer(cricket::MediaSessionOptions* session_options) {
- // ParseConstraintsForAnswer is used to set some defaults.
- ASSERT_TRUE(webrtc::ParseConstraintsForAnswer(nullptr, session_options));
+ void GetOptionsForRemoteAnswer(
+ cricket::MediaSessionOptions* session_options) {
+ bool recv_audio = local_send_audio_ || remote_recv_audio_;
+ bool recv_video = local_send_video_ || remote_recv_video_;
+ bool send_audio = false;
+ bool send_video = false;
- AddStreamsToOptions(session_options);
+ AddExistingMediaSectionsAndSendersToOptions(
+ session_options, send_audio, recv_audio, send_video, recv_video);
+
session_options->bundle_enabled =
session_options->bundle_enabled &&
(session_options->has_audio() || session_options->has_video() ||
@@ -620,6 +752,28 @@
session_options->crypto_options = crypto_options_;
}
+ void GetOptionsForAudioOnlyRemoteOffer(
+ cricket::MediaSessionOptions* session_options) {
+ remote_recv_audio_ = true;
+ remote_recv_video_ = false;
+ GetOptionsForRemoteOffer(session_options);
+ }
+
+ void GetOptionsForRemoteOffer(cricket::MediaSessionOptions* session_options) {
+ AddMediaSectionsAndSendersToOptions(session_options, remote_send_audio_,
+ remote_recv_audio_, remote_send_video_,
+ remote_recv_video_);
+ session_options->bundle_enabled =
+ (session_options->has_audio() || session_options->has_video() ||
+ session_options->has_data());
+
+ if (session_->data_channel_type() != cricket::DCT_RTP) {
+ session_options->data_channel_type = session_->data_channel_type();
+ }
+
+ session_options->crypto_options = crypto_options_;
+ }
+
// Creates a local offer and applies it. Starts ICE.
// Call SendAudioVideoStreamX() before this function
// to decide which streams to create.
@@ -635,7 +789,6 @@
PeerConnectionInterface::RTCOfferAnswerOptions options;
options.offer_to_receive_audio =
RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
-
return CreateOffer(options);
}
@@ -658,9 +811,6 @@
= new WebRtcSessionCreateSDPObserverForTest();
cricket::MediaSessionOptions session_options = options;
GetOptionsForAnswer(&session_options);
- // Overwrite recv_audio and recv_video with passed-in values.
- session_options.recv_video = options.recv_video;
- session_options.recv_audio = options.recv_audio;
session_->CreateAnswer(observer, session_options);
EXPECT_TRUE_WAIT(
observer->state() != WebRtcSessionCreateSDPObserverForTest::kInit,
@@ -670,8 +820,7 @@
SessionDescriptionInterface* CreateAnswer() {
cricket::MediaSessionOptions options;
- options.recv_video = true;
- options.recv_audio = true;
+ options.bundle_enabled = true;
return CreateAnswer(options);
}
@@ -789,7 +938,7 @@
void VerifyAnswerFromNonCryptoOffer() {
// Create an SDP without Crypto.
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
JsepSessionDescription* offer(
CreateRemoteOffer(options, cricket::SEC_DISABLED));
ASSERT_TRUE(offer != NULL);
@@ -803,7 +952,7 @@
void VerifyAnswerFromCryptoOffer() {
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
options.bundle_enabled = true;
std::unique_ptr<JsepSessionDescription> offer(
CreateRemoteOffer(options, cricket::SEC_REQUIRED));
@@ -926,7 +1075,7 @@
SetLocalDescriptionWithoutError(answer);
}
void SetLocalDescriptionWithoutError(SessionDescriptionInterface* desc) {
- EXPECT_TRUE(session_->SetLocalDescription(desc, NULL));
+ ASSERT_TRUE(session_->SetLocalDescription(desc, nullptr));
session_->MaybeStartGathering();
}
void SetLocalDescriptionExpectState(SessionDescriptionInterface* desc,
@@ -955,7 +1104,7 @@
expected_error, desc);
}
void SetRemoteDescriptionWithoutError(SessionDescriptionInterface* desc) {
- EXPECT_TRUE(session_->SetRemoteDescription(desc, NULL));
+ ASSERT_TRUE(session_->SetRemoteDescription(desc, nullptr));
}
void SetRemoteDescriptionExpectState(SessionDescriptionInterface* desc,
WebRtcSession::State expected_state) {
@@ -992,21 +1141,26 @@
SessionDescriptionInterface** nocrypto_answer) {
// Create a SDP without Crypto.
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
options.bundle_enabled = true;
*offer = CreateRemoteOffer(options, cricket::SEC_ENABLED);
ASSERT_TRUE(*offer != NULL);
VerifyCryptoParams((*offer)->description());
- *nocrypto_answer = CreateRemoteAnswer(*offer, options,
- cricket::SEC_DISABLED);
+ cricket::MediaSessionOptions answer_options;
+ GetOptionsForRemoteAnswer(&answer_options);
+ *nocrypto_answer =
+ CreateRemoteAnswer(*offer, answer_options, cricket::SEC_DISABLED);
EXPECT_TRUE(*nocrypto_answer != NULL);
}
void CreateDtlsOfferAndNonDtlsAnswer(SessionDescriptionInterface** offer,
SessionDescriptionInterface** nodtls_answer) {
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ AddMediaSection(cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
+ cricket::MD_RECVONLY, kActive, &options);
+ AddMediaSection(cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
+ cricket::MD_RECVONLY, kActive, &options);
options.bundle_enabled = true;
std::unique_ptr<SessionDescriptionInterface> temp_offer(
@@ -1068,8 +1222,7 @@
const char* sctp_stream_name, int new_port,
cricket::MediaSessionOptions options) {
options.data_channel_type = cricket::DCT_SCTP;
- options.AddSendStream(cricket::MEDIA_TYPE_DATA, "datachannel",
- sctp_stream_name);
+ GetOptionsForRemoteOffer(&options);
return ChangeSDPSctpPort(new_port, CreateRemoteOffer(options));
}
@@ -1097,7 +1250,7 @@
// before this function to decide which streams to create.
JsepSessionDescription* CreateRemoteOffer() {
cricket::MediaSessionOptions options;
- GetOptionsForAnswer(&options);
+ GetOptionsForRemoteOffer(&options);
return CreateRemoteOffer(options, session_->remote_description());
}
@@ -1132,6 +1285,7 @@
const SessionDescriptionInterface* offer) {
cricket::MediaSessionOptions options;
GetOptionsForAnswer(&options);
+ options.bundle_enabled = true;
return CreateRemoteAnswer(offer, options, cricket::SEC_REQUIRED);
}
@@ -1455,6 +1609,7 @@
SetFactoryDtlsSrtp();
if (type == CreateSessionDescriptionRequest::kAnswer) {
cricket::MediaSessionOptions options;
+ GetOptionsForRemoteOffer(&options);
std::unique_ptr<JsepSessionDescription> offer(
CreateRemoteOffer(options, cricket::SEC_DISABLED));
ASSERT_TRUE(offer.get() != NULL);
@@ -1462,16 +1617,19 @@
}
PeerConnectionInterface::RTCOfferAnswerOptions options;
- cricket::MediaSessionOptions session_options;
+ cricket::MediaSessionOptions offer_session_options;
+ cricket::MediaSessionOptions answer_session_options;
+ GetOptionsForOffer(options, &offer_session_options);
+ GetOptionsForAnswer(&answer_session_options);
const int kNumber = 3;
rtc::scoped_refptr<WebRtcSessionCreateSDPObserverForTest>
observers[kNumber];
for (int i = 0; i < kNumber; ++i) {
observers[i] = new WebRtcSessionCreateSDPObserverForTest();
if (type == CreateSessionDescriptionRequest::kOffer) {
- session_->CreateOffer(observers[i], options, session_options);
+ session_->CreateOffer(observers[i], options, offer_session_options);
} else {
- session_->CreateAnswer(observers[i], session_options);
+ session_->CreateAnswer(observers[i], answer_session_options);
}
}
@@ -1529,8 +1687,15 @@
// The following flags affect options created for CreateOffer/CreateAnswer.
bool send_stream_1_ = false;
bool send_stream_2_ = false;
- bool send_audio_ = false;
- bool send_video_ = false;
+ bool local_send_audio_ = false;
+ bool local_send_video_ = false;
+ bool local_recv_audio_ = true;
+ bool local_recv_video_ = true;
+ bool remote_send_audio_ = false;
+ bool remote_send_video_ = false;
+ bool remote_recv_audio_ = true;
+ bool remote_recv_video_ = true;
+ std::vector<cricket::MediaDescriptionOptions> offered_media_sections_;
rtc::scoped_refptr<DataChannel> data_channel_;
// Last values received from data channel creation signal.
std::string last_data_channel_label_;
@@ -1770,7 +1935,7 @@
TEST_F(WebRtcSessionTest, TestSetNonSdesOfferWhenSdesOn) {
Init();
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
JsepSessionDescription* offer = CreateRemoteOffer(
options, cricket::SEC_DISABLED);
ASSERT_TRUE(offer != NULL);
@@ -1816,7 +1981,7 @@
InitWithDtls(GetParam());
SetFactoryDtlsSrtp();
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
JsepSessionDescription* offer =
CreateRemoteOffer(options, cricket::SEC_DISABLED);
ASSERT_TRUE(offer != NULL);
@@ -1855,7 +2020,7 @@
SetLocalDescriptionWithoutError(offer);
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForAnswer(&options);
JsepSessionDescription* answer =
CreateRemoteAnswer(offer, options, cricket::SEC_DISABLED);
ASSERT_TRUE(answer != NULL);
@@ -1871,7 +2036,7 @@
TEST_P(WebRtcSessionTest, TestReceiveNonDtlsOfferWhenDtlsOn) {
InitWithDtls(GetParam());
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
options.bundle_enabled = true;
JsepSessionDescription* offer = CreateRemoteOffer(
options, cricket::SEC_REQUIRED);
@@ -1909,12 +2074,16 @@
TEST_P(WebRtcSessionTest, TestSetRemoteNonDtlsAnswerWhenDtlsOn) {
InitWithDtls(GetParam());
SessionDescriptionInterface* offer = CreateOffer();
- cricket::MediaSessionOptions options;
- options.recv_video = true;
+ cricket::MediaSessionOptions offer_options;
+ GetOptionsForRemoteOffer(&offer_options);
+
std::unique_ptr<SessionDescriptionInterface> temp_offer(
- CreateRemoteOffer(options, cricket::SEC_ENABLED));
- JsepSessionDescription* answer =
- CreateRemoteAnswer(temp_offer.get(), options, cricket::SEC_ENABLED);
+ CreateRemoteOffer(offer_options, cricket::SEC_ENABLED));
+
+ cricket::MediaSessionOptions answer_options;
+ GetOptionsForAnswer(&answer_options);
+ JsepSessionDescription* answer = CreateRemoteAnswer(
+ temp_offer.get(), answer_options, cricket::SEC_ENABLED);
// SetRemoteDescription and SetLocalDescription will take the ownership of
// the offer and answer.
@@ -1941,7 +2110,7 @@
SetLocalDescriptionWithoutError(offer);
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForAnswer(&options);
JsepSessionDescription* answer =
CreateRemoteAnswer(offer, options, cricket::SEC_DISABLED);
ASSERT_TRUE(answer != NULL);
@@ -1959,7 +2128,7 @@
InitWithDtls(GetParam());
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
JsepSessionDescription* offer =
CreateRemoteOffer(options, cricket::SEC_DISABLED);
ASSERT_TRUE(offer != NULL);
@@ -1992,7 +2161,7 @@
SetLocalDescriptionWithoutError(offer);
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForAnswer(&options);
// First, negotiate different SSL roles.
SessionDescriptionInterface* answer =
@@ -2014,7 +2183,9 @@
session_->remote_description());
SetRemoteDescriptionWithoutError(offer);
- answer = CreateAnswer();
+ cricket::MediaSessionOptions answer_options;
+ answer_options.bundle_enabled = true;
+ answer = CreateAnswer(answer_options);
audio_transport_info = answer->description()->GetTransportInfoByName("audio");
EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
audio_transport_info->description.connection_role);
@@ -2031,7 +2202,7 @@
kSessionVersion,
session_->remote_description());
SetRemoteDescriptionWithoutError(offer);
- answer = CreateAnswer();
+ answer = CreateAnswer(answer_options);
audio_transport_info = answer->description()->GetTransportInfoByName("audio");
EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
audio_transport_info->description.connection_role);
@@ -2392,6 +2563,7 @@
std::unique_ptr<SessionDescriptionInterface> local_offer(CreateOffer());
+ ASSERT_TRUE(local_offer);
ASSERT_TRUE(local_offer->candidates(kMediaContentIndex0) != NULL);
EXPECT_LT(0u, local_offer->candidates(kMediaContentIndex0)->count());
@@ -2417,42 +2589,27 @@
// present in SDP.
std::string sdp;
EXPECT_TRUE(offer->ToString(&sdp));
- const std::string kAudioMid = "a=mid:audio";
- const std::string kAudioMidReplaceStr = "a=mid:audio_content_name";
- const std::string kVideoMid = "a=mid:video";
- const std::string kVideoMidReplaceStr = "a=mid:video_content_name";
-
- // Replacing |audio| with |audio_content_name|.
- rtc::replace_substrs(kAudioMid.c_str(), kAudioMid.length(),
- kAudioMidReplaceStr.c_str(),
- kAudioMidReplaceStr.length(),
- &sdp);
- // Replacing |video| with |video_content_name|.
- rtc::replace_substrs(kVideoMid.c_str(), kVideoMid.length(),
- kVideoMidReplaceStr.c_str(),
- kVideoMidReplaceStr.length(),
- &sdp);
SessionDescriptionInterface* modified_offer =
CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL);
SetRemoteDescriptionWithoutError(modified_offer);
- SessionDescriptionInterface* answer = CreateAnswer();
+ cricket::MediaSessionOptions answer_options;
+ answer_options.bundle_enabled = false;
+ SessionDescriptionInterface* answer = CreateAnswer(answer_options);
SetLocalDescriptionWithoutError(answer);
rtc::PacketTransportInternal* voice_transport_channel =
session_->voice_rtp_transport_channel();
EXPECT_TRUE(voice_transport_channel != NULL);
EXPECT_EQ(voice_transport_channel->debug_name(),
- "audio_content_name " +
- std::to_string(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+ "audio " + std::to_string(cricket::ICE_CANDIDATE_COMPONENT_RTP));
rtc::PacketTransportInternal* video_transport_channel =
session_->video_rtp_transport_channel();
ASSERT_TRUE(video_transport_channel != NULL);
EXPECT_EQ(video_transport_channel->debug_name(),
- "video_content_name " +
- std::to_string(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+ "video " + std::to_string(cricket::ICE_CANDIDATE_COMPONENT_RTP));
EXPECT_TRUE((video_channel_ = media_engine_->GetVideoChannel(0)) != NULL);
EXPECT_TRUE((voice_channel_ = media_engine_->GetVoiceChannel(0)) != NULL);
}
@@ -2466,9 +2623,17 @@
ASSERT_TRUE(offer != NULL);
const cricket::ContentInfo* content =
cricket::GetFirstAudioContent(offer->description());
- EXPECT_TRUE(content != NULL);
+ ASSERT_TRUE(content != NULL);
+ EXPECT_EQ(
+ cricket::MD_RECVONLY,
+ static_cast<const cricket::AudioContentDescription*>(content->description)
+ ->direction());
content = cricket::GetFirstVideoContent(offer->description());
- EXPECT_TRUE(content == NULL);
+ ASSERT_TRUE(content != NULL);
+ EXPECT_EQ(
+ cricket::MD_RECVONLY,
+ static_cast<const cricket::VideoContentDescription*>(content->description)
+ ->direction());
}
// Test that an offer contains the correct media content descriptions based on
@@ -2481,17 +2646,34 @@
const cricket::ContentInfo* content =
cricket::GetFirstAudioContent(offer->description());
- EXPECT_TRUE(content != NULL);
+ ASSERT_TRUE(content != NULL);
+ EXPECT_EQ(
+ cricket::MD_SENDRECV,
+ static_cast<const cricket::AudioContentDescription*>(content->description)
+ ->direction());
content = cricket::GetFirstVideoContent(offer->description());
- EXPECT_TRUE(content == NULL);
+ ASSERT_TRUE(content != NULL);
+ EXPECT_EQ(
+ cricket::MD_RECVONLY,
+ static_cast<const cricket::VideoContentDescription*>(content->description)
+ ->direction());
// Test Audio / Video offer.
SendAudioVideoStream1();
offer.reset(CreateOffer());
content = cricket::GetFirstAudioContent(offer->description());
- EXPECT_TRUE(content != NULL);
+ ASSERT_TRUE(content != NULL);
+ EXPECT_EQ(
+ cricket::MD_SENDRECV,
+ static_cast<const cricket::AudioContentDescription*>(content->description)
+ ->direction());
+
content = cricket::GetFirstVideoContent(offer->description());
- EXPECT_TRUE(content != NULL);
+ ASSERT_TRUE(content != NULL);
+ EXPECT_EQ(
+ cricket::MD_SENDRECV,
+ static_cast<const cricket::VideoContentDescription*>(content->description)
+ ->direction());
}
// Test that an offer contains no media content descriptions if
@@ -2519,6 +2701,7 @@
PeerConnectionInterface::RTCOfferAnswerOptions options;
options.offer_to_receive_audio =
RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
+ options.offer_to_receive_video = 0;
std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer(options));
@@ -2553,6 +2736,8 @@
// removed.
options.offer_to_receive_audio = 0;
options.offer_to_receive_video = 0;
+ // Remove the media sections added in previous offer.
+ offered_media_sections_.clear();
offer.reset(CreateOffer(options));
content = cricket::GetFirstAudioContent(offer->description());
@@ -2596,6 +2781,7 @@
Init();
// Create a remote offer with audio only.
cricket::MediaSessionOptions options;
+ GetOptionsForAudioOnlyRemoteOffer(&options);
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
ASSERT_TRUE(cricket::GetFirstVideoContent(offer->description()) == NULL);
@@ -2640,8 +2826,10 @@
SetRemoteDescriptionWithoutError(offer.release());
cricket::MediaSessionOptions session_options;
- session_options.recv_audio = false;
- session_options.recv_video = false;
+ remote_send_audio_ = false;
+ remote_send_video_ = false;
+ local_recv_audio_ = false;
+ local_recv_video_ = false;
std::unique_ptr<SessionDescriptionInterface> answer(
CreateAnswer(session_options));
@@ -2664,9 +2852,6 @@
SetRemoteDescriptionWithoutError(offer.release());
cricket::MediaSessionOptions options;
- options.recv_audio = false;
- options.recv_video = false;
-
// Test with a stream with tracks.
SendAudioVideoStream1();
std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer(options));
@@ -2726,6 +2911,11 @@
SessionDescriptionInterface* offer = CreateOffer();
cricket::MediaSessionOptions options;
+ AddMediaSection(cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
+ cricket::MD_RECVONLY, kActive, &options);
+ AddMediaSection(cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
+ cricket::MD_INACTIVE, kStopped, &options);
+ local_recv_video_ = false;
SessionDescriptionInterface* answer = CreateRemoteAnswer(offer, options);
// SetLocalDescription and SetRemoteDescriptions takes ownership of offer
@@ -2736,7 +2926,7 @@
video_channel_ = media_engine_->GetVideoChannel(0);
voice_channel_ = media_engine_->GetVoiceChannel(0);
- ASSERT_TRUE(video_channel_ == NULL);
+ ASSERT_TRUE(video_channel_ == nullptr);
ASSERT_EQ(0u, voice_channel_->recv_streams().size());
ASSERT_EQ(1u, voice_channel_->send_streams().size());
@@ -2744,13 +2934,14 @@
// Let the remote end update the session descriptions, with Audio and Video.
SendAudioVideoStream2();
+ local_recv_video_ = true;
CreateAndSetRemoteOfferAndLocalAnswer();
video_channel_ = media_engine_->GetVideoChannel(0);
voice_channel_ = media_engine_->GetVoiceChannel(0);
- ASSERT_TRUE(video_channel_ != NULL);
- ASSERT_TRUE(voice_channel_ != NULL);
+ ASSERT_TRUE(video_channel_ != nullptr);
+ ASSERT_TRUE(voice_channel_ != nullptr);
ASSERT_EQ(1u, video_channel_->recv_streams().size());
ASSERT_EQ(1u, video_channel_->send_streams().size());
@@ -2762,10 +2953,17 @@
EXPECT_EQ(kAudioTrack2, voice_channel_->send_streams()[0].id);
// Change session back to audio only.
+ // The remote side doesn't send and recv video.
SendAudioOnlyStream2();
+ remote_recv_video_ = false;
CreateAndSetRemoteOfferAndLocalAnswer();
- EXPECT_EQ(0u, video_channel_->recv_streams().size());
+ video_channel_ = media_engine_->GetVideoChannel(0);
+ voice_channel_ = media_engine_->GetVoiceChannel(0);
+
+ // The audio is expected to be rejected.
+ EXPECT_TRUE(video_channel_ == nullptr);
+
ASSERT_EQ(1u, voice_channel_->recv_streams().size());
EXPECT_EQ(kAudioTrack2, voice_channel_->recv_streams()[0].id);
ASSERT_EQ(1u, voice_channel_->send_streams().size());
@@ -2782,10 +2980,13 @@
SessionDescriptionInterface* offer = CreateOffer();
cricket::MediaSessionOptions options;
- options.recv_audio = false;
- options.recv_video = true;
- SessionDescriptionInterface* answer = CreateRemoteAnswer(
- offer, options, cricket::SEC_ENABLED);
+ AddMediaSection(cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
+ cricket::MD_INACTIVE, kStopped, &options);
+ AddMediaSection(cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
+ cricket::MD_RECVONLY, kActive, &options);
+ local_recv_audio_ = false;
+ SessionDescriptionInterface* answer =
+ CreateRemoteAnswer(offer, options, cricket::SEC_ENABLED);
// SetLocalDescription and SetRemoteDescriptions takes ownership of offer
// and answer.
@@ -2804,23 +3005,41 @@
// Update the session descriptions, with Audio and Video.
SendAudioVideoStream2();
- CreateAndSetRemoteOfferAndLocalAnswer();
+ local_recv_audio_ = true;
+ SessionDescriptionInterface* offer2 = CreateRemoteOffer();
+ SetRemoteDescriptionWithoutError(offer2);
+ cricket::MediaSessionOptions answer_options;
+ // Disable the bundling here. If the media is bundled on audio
+ // transport, then we can't reject the audio because switching the bundled
+ // transport is not currently supported.
+ // (https://bugs.chromium.org/p/webrtc/issues/detail?id=6704)
+ answer_options.bundle_enabled = false;
+ SessionDescriptionInterface* answer2 = CreateAnswer(answer_options);
+ SetLocalDescriptionWithoutError(answer2);
voice_channel_ = media_engine_->GetVoiceChannel(0);
- ASSERT_TRUE(voice_channel_ != NULL);
+ ASSERT_TRUE(voice_channel_ != NULL);
ASSERT_EQ(1u, voice_channel_->recv_streams().size());
ASSERT_EQ(1u, voice_channel_->send_streams().size());
EXPECT_EQ(kAudioTrack2, voice_channel_->recv_streams()[0].id);
EXPECT_EQ(kAudioTrack2, voice_channel_->send_streams()[0].id);
// Change session back to video only.
+ // The remote side doesn't send and recv audio.
SendVideoOnlyStream2();
- CreateAndSetRemoteOfferAndLocalAnswer();
+ remote_recv_audio_ = false;
+ SessionDescriptionInterface* offer3 = CreateRemoteOffer();
+ SetRemoteDescriptionWithoutError(offer3);
+ SessionDescriptionInterface* answer3 = CreateAnswer(answer_options);
+ SetLocalDescriptionWithoutError(answer3);
video_channel_ = media_engine_->GetVideoChannel(0);
voice_channel_ = media_engine_->GetVoiceChannel(0);
+ // The video is expected to be rejected.
+ EXPECT_TRUE(voice_channel_ == nullptr);
+
ASSERT_EQ(1u, video_channel_->recv_streams().size());
EXPECT_EQ(kVideoTrack2, video_channel_->recv_streams()[0].id);
ASSERT_EQ(1u, video_channel_->send_streams().size());
@@ -3025,13 +3244,16 @@
InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced);
SendAudioVideoStream1();
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
+ cricket::MediaSessionOptions offer_options;
+ GetOptionsForRemoteOffer(&offer_options);
+ offer_options.bundle_enabled = true;
- SessionDescriptionInterface* offer = CreateRemoteOffer();
+ SessionDescriptionInterface* offer = CreateRemoteOffer(offer_options);
SetRemoteDescriptionWithoutError(offer);
- SessionDescriptionInterface* answer = CreateAnswer();
+ cricket::MediaSessionOptions answer_options;
+ answer_options.bundle_enabled = true;
+ SessionDescriptionInterface* answer = CreateAnswer(answer_options);
SetLocalDescriptionWithoutError(answer);
EXPECT_EQ(session_->voice_rtp_transport_channel(),
@@ -3196,10 +3418,11 @@
EXPECT_EQ(session_->voice_rtp_transport_channel(),
session_->video_rtp_transport_channel());
- SendAudioVideoStream2();
+ SendVideoOnlyStream2();
+ local_send_audio_ = false;
+ remote_recv_audio_ = false;
cricket::MediaSessionOptions recv_options;
- recv_options.recv_audio = false;
- recv_options.recv_video = true;
+ GetOptionsForRemoteAnswer(&recv_options);
SessionDescriptionInterface* answer =
CreateRemoteAnswer(session_->local_description(), recv_options);
SetRemoteDescriptionWithoutError(answer);
@@ -3286,10 +3509,10 @@
InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxCompat);
SendAudioVideoStream1();
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
+ PeerConnectionInterface::RTCOfferAnswerOptions rtc_options;
+ rtc_options.use_rtp_mux = true;
- SessionDescriptionInterface* offer = CreateOffer(options);
+ SessionDescriptionInterface* offer = CreateOffer(rtc_options);
SetLocalDescriptionWithoutError(offer);
EXPECT_NE(session_->voice_rtp_transport_channel(),
@@ -3645,7 +3868,7 @@
TEST_F(WebRtcSessionTest, TestCreateAnswerWithNewUfragAndPassword) {
Init();
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
SetRemoteDescriptionWithoutError(offer.release());
@@ -3654,10 +3877,10 @@
SetLocalDescriptionWithoutError(answer.release());
// Receive an offer with new ufrag and password.
- for (const cricket::ContentInfo& content :
- session_->local_description()->description()->contents()) {
- options.transport_options[content.name].ice_restart = true;
+ for (size_t i = 0; i < options.media_description_options.size(); ++i) {
+ options.media_description_options[i].transport_options.ice_restart = true;
}
+
std::unique_ptr<JsepSessionDescription> updated_offer1(
CreateRemoteOffer(options, session_->remote_description()));
SetRemoteDescriptionWithoutError(updated_offer1.release());
@@ -3685,8 +3908,7 @@
TEST_F(WebRtcSessionTest, TestOfferChangingOnlyUfragOrPassword) {
Init();
cricket::MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
// Create an offer with audio and video.
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
SetIceUfragPwd(offer.get(), "original_ufrag", "original_password12345");
@@ -3726,7 +3948,7 @@
TEST_F(WebRtcSessionTest, TestCreateAnswerWithOldUfragAndPassword) {
Init();
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
SetRemoteDescriptionWithoutError(offer.release());
@@ -3753,8 +3975,7 @@
TEST_F(WebRtcSessionTest, TestCreateAnswerWithNewAndOldUfragAndPassword) {
Init();
cricket::MediaSessionOptions options;
- options.recv_video = true;
- options.recv_audio = true;
+ GetOptionsForRemoteOffer(&options);
options.bundle_enabled = false;
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
@@ -3886,9 +4107,9 @@
// Create answer that finishes BUNDLE negotiation, which means everything
// should be bundled on the first transport (audio).
cricket::MediaSessionOptions answer_options;
- answer_options.recv_video = true;
answer_options.bundle_enabled = true;
answer_options.data_channel_type = cricket::DCT_SCTP;
+ GetOptionsForAnswer(&answer_options);
SetRemoteDescriptionWithoutError(CreateRemoteAnswer(
session_->local_description(), answer_options, cricket::SEC_DISABLED));
ASSERT_TRUE(session_->sctp_content_name());
@@ -3912,6 +4133,7 @@
// Create remote offer with SCTP.
cricket::MediaSessionOptions options;
options.data_channel_type = cricket::DCT_SCTP;
+ GetOptionsForRemoteOffer(&options);
JsepSessionDescription* offer =
CreateRemoteOffer(options, cricket::SEC_DISABLED);
SetRemoteDescriptionWithoutError(offer);
@@ -4055,7 +4277,7 @@
SetFactoryDtlsSrtp();
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
std::unique_ptr<JsepSessionDescription> offer(
CreateRemoteOffer(options, cricket::SEC_DISABLED));
ASSERT_TRUE(offer.get() != NULL);
@@ -4129,6 +4351,7 @@
Init();
// Create a remote offer with secured transport disabled.
cricket::MediaSessionOptions options;
+ GetOptionsForRemoteOffer(&options);
JsepSessionDescription* offer(CreateRemoteOffer(
options, cricket::SEC_DISABLED));
// Adds a DTLS fingerprint to the remote offer.
@@ -4172,7 +4395,7 @@
SetRemoteDescriptionWithoutError(answer);
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
offer = CreateRemoteOffer(options, cricket::SEC_DISABLED);
cricket::Candidate candidate1;
@@ -4201,7 +4424,7 @@
SetRemoteDescriptionWithoutError(answer);
cricket::MediaSessionOptions options;
- options.recv_video = true;
+ GetOptionsForRemoteOffer(&options);
offer = CreateRemoteOffer(options, cricket::SEC_DISABLED);
SetRemoteDescriptionWithoutError(offer);
@@ -4226,8 +4449,7 @@
ASSERT_TRUE(offer->description());
SetLocalDescriptionWithoutError(offer);
cricket::MediaSessionOptions options;
- options.recv_audio = true;
- options.recv_video = true;
+ GetOptionsForAnswer(&options);
SessionDescriptionInterface* answer =
CreateRemoteAnswer(offer, options, cricket::SEC_DISABLED);
ASSERT_TRUE(answer);
@@ -4240,7 +4462,8 @@
// by local side.
TEST_F(WebRtcSessionTest, TestRtxRemovedByCreateAnswer) {
Init();
- SendAudioVideoStream1();
+ // Send video only to match the |kSdpWithRtx|.
+ SendVideoOnlyStream2();
std::string offer_sdp(kSdpWithRtx);
SessionDescriptionInterface* offer =
@@ -4251,6 +4474,11 @@
EXPECT_TRUE(ContainsVideoCodecWithName(offer, "rtx"));
SetRemoteDescriptionWithoutError(offer);
+ // |offered_media_sections_| is used when creating answer.
+ offered_media_sections_.push_back(cricket::MediaDescriptionOptions(
+ cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
+ cricket::RtpTransceiverDirection(true, true), false));
+ // Don't create media section for audio in the answer.
SessionDescriptionInterface* answer = CreateAnswer();
// Answer SDP does not contain the RTX codec.
EXPECT_FALSE(ContainsVideoCodecWithName(answer, "rtx"));
@@ -4316,8 +4544,7 @@
options.offer_to_receive_audio =
RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
cricket::MediaSessionOptions session_options;
- session_options.recv_audio = true;
-
+ GetOptionsForOffer(options, &session_options);
for (auto& o : observers) {
o = new WebRtcSessionCreateSDPObserverForTest();
session_->CreateOffer(o, options, session_options);
diff --git a/webrtc/pc/webrtcsessiondescriptionfactory.cc b/webrtc/pc/webrtcsessiondescriptionfactory.cc
index 8eccd65..beb8d1e 100644
--- a/webrtc/pc/webrtcsessiondescriptionfactory.cc
+++ b/webrtc/pc/webrtcsessiondescriptionfactory.cc
@@ -30,24 +30,30 @@
static const uint64_t kInitSessionVersion = 2;
-static bool CompareStream(const MediaSessionOptions::Stream& stream1,
- const MediaSessionOptions::Stream& stream2) {
- return stream1.id < stream2.id;
+static bool CompareSenderOptions(const cricket::SenderOptions& sender1,
+ const cricket::SenderOptions& sender2) {
+ return sender1.track_id < sender2.track_id;
}
-static bool SameId(const MediaSessionOptions::Stream& stream1,
- const MediaSessionOptions::Stream& stream2) {
- return stream1.id == stream2.id;
+static bool SameId(const cricket::SenderOptions& sender1,
+ const cricket::SenderOptions& sender2) {
+ return sender1.track_id == sender2.track_id;
}
-// Checks if each Stream within the |streams| has unique id.
-static bool ValidStreams(const MediaSessionOptions::Streams& streams) {
- MediaSessionOptions::Streams sorted_streams = streams;
- std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
- MediaSessionOptions::Streams::iterator it =
- std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
- SameId);
- return it == sorted_streams.end();
+// Check that each sender has a unique ID.
+static bool ValidMediaSessionOptions(
+ const cricket::MediaSessionOptions& session_options) {
+ std::vector<cricket::SenderOptions> sorted_senders;
+ for (const cricket::MediaDescriptionOptions& media_description_options :
+ session_options.media_description_options) {
+ sorted_senders.insert(sorted_senders.end(),
+ media_description_options.sender_options.begin(),
+ media_description_options.sender_options.end());
+ }
+ std::sort(sorted_senders.begin(), sorted_senders.end(), CompareSenderOptions);
+ std::vector<cricket::SenderOptions>::iterator it =
+ std::adjacent_find(sorted_senders.begin(), sorted_senders.end(), SameId);
+ return it == sorted_senders.end();
}
enum {
@@ -128,7 +134,6 @@
session_id_(session_id),
certificate_request_state_(CERTIFICATE_NOT_NEEDED) {
RTC_DCHECK(signaling_thread_);
- session_desc_factory_.set_add_legacy_streams(false);
bool dtls_enabled = cert_generator_ || certificate;
// SRTP-SDES is disabled if DTLS is on.
SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED);
@@ -237,8 +242,8 @@
return;
}
- if (!ValidStreams(session_options.streams)) {
- error += " called with invalid media streams.";
+ if (!ValidMediaSessionOptions(session_options)) {
+ error += " called with invalid session options";
LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailed(observer, error);
return;
@@ -279,8 +284,8 @@
return;
}
- if (!ValidStreams(session_options.streams)) {
- error += " called with invalid media streams.";
+ if (!ValidMediaSessionOptions(session_options)) {
+ error += " called with invalid session options.";
LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailed(observer, error);
return;
@@ -340,13 +345,12 @@
void WebRtcSessionDescriptionFactory::InternalCreateOffer(
CreateSessionDescriptionRequest request) {
if (session_->local_description()) {
- for (const cricket::TransportInfo& transport :
- session_->local_description()->description()->transport_infos()) {
- // If the needs-ice-restart flag is set as described by JSEP, we should
- // generate an offer with a new ufrag/password to trigger an ICE restart.
- if (session_->NeedsIceRestart(transport.content_name)) {
- request.options.transport_options[transport.content_name].ice_restart =
- true;
+ // If the needs-ice-restart flag is set as described by JSEP, we should
+ // generate an offer with a new ufrag/password to trigger an ICE restart.
+ for (cricket::MediaDescriptionOptions& options :
+ request.options.media_description_options) {
+ if (session_->NeedsIceRestart(options.mid)) {
+ options.transport_options.ice_restart = true;
}
}
}
@@ -375,13 +379,11 @@
return;
}
if (session_->local_description()) {
- for (const cricket::ContentInfo& content :
- session_->local_description()->description()->contents()) {
- // Include all local ICE candidates in the SessionDescription unless
- // an ICE restart was requested.
- if (!request.options.transport_options[content.name].ice_restart) {
+ for (const cricket::MediaDescriptionOptions& options :
+ request.options.media_description_options) {
+ if (!options.transport_options.ice_restart) {
CopyCandidatesFromSessionDescription(session_->local_description(),
- content.name, offer);
+ options.mid, offer);
}
}
}
@@ -391,18 +393,18 @@
void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
CreateSessionDescriptionRequest request) {
if (session_->remote_description()) {
- for (const cricket::ContentInfo& content :
- session_->remote_description()->description()->contents()) {
+ for (cricket::MediaDescriptionOptions& options :
+ request.options.media_description_options) {
// According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
// an answer should also contain new ICE ufrag and password if an offer
// has been received with new ufrag and password.
- request.options.transport_options[content.name].ice_restart =
- session_->IceRestartPending(content.name);
+ options.transport_options.ice_restart =
+ session_->IceRestartPending(options.mid);
// We should pass the current SSL role to the transport description
// factory, if there is already an existing ongoing session.
rtc::SSLRole ssl_role;
- if (session_->GetSslRole(content.name, &ssl_role)) {
- request.options.transport_options[content.name].prefer_passive_role =
+ if (session_->GetSslRole(options.mid, &ssl_role)) {
+ options.transport_options.prefer_passive_role =
(rtc::SSL_SERVER == ssl_role);
}
}
@@ -433,13 +435,13 @@
return;
}
if (session_->local_description()) {
- for (const cricket::ContentInfo& content :
- session_->local_description()->description()->contents()) {
- // Include all local ICE candidates in the SessionDescription unless
- // the remote peer has requested an ICE restart.
- if (!request.options.transport_options[content.name].ice_restart) {
+ // Include all local ICE candidates in the SessionDescription unless
+ // the remote peer has requested an ICE restart.
+ for (const cricket::MediaDescriptionOptions& options :
+ request.options.media_description_options) {
+ if (!options.transport_options.ice_restart) {
CopyCandidatesFromSessionDescription(session_->local_description(),
- content.name, answer);
+ options.mid, answer);
}
}
}