Chromium Code Reviews| Index: webrtc/pc/mediasession.cc |
| diff --git a/webrtc/pc/mediasession.cc b/webrtc/pc/mediasession.cc |
| index de8f72c92612714268570a7a2403e2c6d16827c1..8b3a32583ae8eeb45805b99a0dce7420a35c1276 100644 |
| --- a/webrtc/pc/mediasession.cc |
| +++ b/webrtc/pc/mediasession.cc |
| @@ -188,11 +188,12 @@ bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites, |
| 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(); |
| + return &(static_cast<const MediaContentDescription*>(content->description) |
| + ->cryptos()); |
| } |
| bool FindMatchingCrypto(const CryptoParamsVec& cryptos, |
| @@ -428,15 +429,15 @@ class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> { |
| 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 +446,26 @@ static bool AddStreamParams(MediaType media_type, |
| const bool include_rtx_streams = |
| ContainsRtxCodec(content_description->codecs()); |
| - const MediaSessionOptions::Streams& streams = options.streams; |
| - if (streams.empty() && add_legacy_stream) { |
|
Taylor Brandstetter
2017/07/28 01:19:38
Somewhere in the CL description, can you note that
Zhi Huang
2017/08/02 04:38:34
Done.
|
| - // 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 +495,8 @@ static bool AddStreamParams(MediaType media_type, |
| << "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 +506,7 @@ static bool AddStreamParams(MediaType media_type, |
| // 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 +718,34 @@ static bool IsFlexfecCodec(const C& codec) { |
| 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 in a session-initiate 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 +853,42 @@ static bool FindMatchingCodec(const std::vector<C>& codecs1, |
| 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 +906,11 @@ static void FindCodecsToOffer( |
| !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 +929,22 @@ static void FindCodecsToOffer( |
| } |
| } |
| +static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions, |
| + const webrtc::RtpExtension& ext_to_match, |
| + webrtc::RtpExtension* found_extension) { |
| + // We assume that all URIs are given in a canonical format. |
| + const webrtc::RtpExtension* found = |
| + webrtc::RtpExtension::FindHeaderExtensionByUri(extensions, |
| + ext_to_match.uri); |
|
Taylor Brandstetter
2017/07/28 01:19:39
This doesn't seem like it would work... If "extens
Zhi Huang
2017/08/02 04:38:34
Agree. I'll update this.
|
| + if (!found || found->encrypt != ext_to_match.encrypt) { |
| + return false; |
| + } |
| + if (found_extension) { |
| + *found_extension = *found; |
| + } |
| + return true; |
| +} |
| + |
| static bool FindByUri(const RtpHeaderExtensions& extensions, |
| const webrtc::RtpExtension& ext_to_match, |
| webrtc::RtpExtension* found_extension) { |
| @@ -996,50 +988,38 @@ static bool FindByUriWithEncryptionPreference( |
| 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 used. |
| +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, |
| + NULL)) { |
|
Taylor Brandstetter
2017/07/28 01:19:38
nullptr?
Zhi Huang
2017/08/02 04:38:34
Done.
|
| 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, |
|
Taylor Brandstetter
2017/07/28 01:19:39
How could an extension be in encrypted_extensions
Zhi Huang
2017/08/02 04:38:34
In current use case, both the regular and encrypte
|
| + &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 +1083,27 @@ static void StripCNCodecs(AudioCodecs* audio_codecs) { |
| } |
| } |
| -// 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 in a session-accept 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 from the incoming |
| +// session-initiate. 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 std::vector<SenderOptions>& sender_options, |
| + // TODO: combine these two params into mediadescriptionoptions |
|
Taylor Brandstetter
2017/07/28 01:19:38
Can this be done, since there's now MediaDescripti
Zhi Huang
2017/08/02 04:38:34
Done. I should be able be fix this before.
|
| + RtpTransceiverDirection desired_direction, |
| + 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 +1117,15 @@ static bool CreateMediaContentAnswer( |
| &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 +1137,17 @@ static bool CreateMediaContentAnswer( |
| return false; |
| } |
| - if (!AddStreamParams(answer->type(), options, current_streams, answer, |
| - add_legacy_stream)) { |
| + if (!AddStreamParams(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, desired_direction) |
| + .ToMediaContentDirection()); |
| return true; |
| } |
| @@ -1239,21 +1209,17 @@ static const TransportDescription* GetTransportDescription( |
| } |
| // Gets the current DTLS state from the transport description. |
| -static bool IsDtlsActive( |
| - const std::string& content_name, |
| - const SessionDescription* current_description) { |
| - if (!current_description) |
| - return false; |
| - |
| - const ContentInfo* content = |
| - current_description->GetContentByName(content_name); |
| - if (!content) |
| +static bool IsDtlsActive(const ContentInfo* content, |
| + const SessionDescription* current_description) { |
| + if (!content) { |
| return false; |
| + } |
| const TransportDescription* current_tdesc = |
| - GetTransportDescription(content_name, current_description); |
| - if (!current_tdesc) |
| + GetTransportDescription(content->name, current_description); |
| + if (!current_tdesc) { |
| return false; |
| + } |
| return current_tdesc->secure(); |
| } |
| @@ -1281,75 +1247,61 @@ std::string MediaContentDirectionToString(MediaContentDirection direction) { |
| 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)); |
| +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); |
| +} |
| - // 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::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}); |
| } |
| -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(); |
| +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(); |
| } |
| -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; |
| +std::vector<MediaDescriptionOptions>::iterator |
| +MediaSessionOptions::FindMediaDescription(const std::string& mid) { |
| + return std::find_if( |
| + media_description_options.begin(), media_description_options.end(), |
| + [mid](const MediaDescriptionOptions& t) { return t.mid == mid; }); |
| } |
| 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() |
| @@ -1370,120 +1322,109 @@ void MediaSessionDescriptionFactory::set_audio_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_); |
| + all_audio_codecs_.clear(); |
|
Taylor Brandstetter
2017/07/28 01:19:38
Maybe the "clear" should happen inside ComputeAudi
Zhi Huang
2017/08/02 04:38:34
Done.
|
| + 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 audio_codecs; |
| - VideoCodecs video_codecs; |
| - DataCodecs data_codecs; |
| - GetCodecsToOffer(current_description, supported_audio_codecs, |
| - video_codecs_, data_codecs_, |
| - &audio_codecs, &video_codecs, &data_codecs); |
| + AudioCodecs offer_audio_codecs; |
| + VideoCodecs offer_video_codecs; |
| + DataCodecs offer_data_codecs; |
| + GetCodecsForOffer(current_description, &offer_audio_codecs, |
| + &offer_video_codecs, &offer_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. |
|
Taylor Brandstetter
2017/07/28 01:19:38
This conflicts with the comment in the header file
Zhi Huang
2017/08/02 04:38:34
Done.
|
| 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, |
| + RTC_DCHECK(current_description->contents().size() <= |
| + session_options.media_description_options.size()); |
| + } |
| + |
| + // Iterate through the media description options, matching with existing media |
| + // description in |current_description|. |
|
Taylor Brandstetter
2017/07/28 01:19:38
"descriptions", plural
Zhi Huang
2017/08/02 04:38:34
Done.
|
| + 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)); |
| + // This restriction doesn't exist before. |
|
Taylor Brandstetter
2017/07/28 01:19:38
Hmmm... We may want to remove this restriction. Th
Taylor Brandstetter
2017/07/28 01:23:14
In case you're not familiar with the terminology,
Zhi Huang
2017/08/02 04:38:34
Thanks for clarifying it.
|
| + RTC_DCHECK(media_description_options.mid == current_content->name); |
| + } |
| + 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 NULL; |
| + return nullptr; |
| } |
| - audio_added = true; |
| - } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) { |
| - if (!AddVideoContentForOffer(options, current_description, |
| - video_rtp_extensions, video_codecs, |
| + 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 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; |
| + return nullptr; |
| } |
| - if (!AddDataContentForOffer(options_copy, current_description, |
| - &data_codecs, ¤t_streams, |
| + break; |
| + case MEDIA_TYPE_DATA: |
| + if (!AddDataContentForOffer(media_description_options, session_options, |
| + current_content, current_description, |
| + offer_data_codecs, ¤t_streams, |
| offer.get())) { |
| - return NULL; |
| + return nullptr; |
| } |
| - data_added = true; |
| - } else { |
| + break; |
| + default: |
| RTC_NOTREACHED(); |
| - } |
| } |
| - } |
| - |
| - // 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; |
| + ++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; |
| } |
| } |
| @@ -1491,7 +1432,8 @@ SessionDescription* MediaSessionDescriptionFactory::CreateOffer( |
| } |
| 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 +1453,84 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( |
| // 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. |
|
Taylor Brandstetter
2017/07/28 01:19:38
nit: When referring to MIDs in comments, we should
Zhi Huang
2017/08/02 04:38:34
Done.
|
| + 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]; |
| + // Media types and mids must match. |
|
Taylor Brandstetter
2017/07/28 01:19:38
This is the same as the comment above; maybe the c
Zhi Huang
2017/08/02 04:38:34
Done.
|
| + RTC_DCHECK(IsMediaContentOfType(current_content, |
| + media_description_options.type)); |
| + RTC_DCHECK(media_description_options.mid == current_content->name); |
| } |
| + 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 +1590,37 @@ const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer( |
| } |
| } |
| -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 +1629,153 @@ void MediaSessionDescriptionFactory::GetCodecsToOffer( |
| 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); |
| + } |
| + |
| + // Add our codecs that are not in |current_description|. |
| + 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 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); |
| + } |
| + |
| + // 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 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); |
| + // 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_DATA)) { |
|
Taylor Brandstetter
2017/07/28 01:19:38
This "if (data) { continue; }" block can actually
Zhi Huang
2017/08/02 04:38:34
Done.
|
| + continue; |
| + } |
| + 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); |
| } |
| } |
| @@ -1744,34 +1830,47 @@ bool MediaSessionDescriptionFactory::AddTransportAnswer( |
| } |
| 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. |
| + AudioCodecs supported_audio_codecs = |
|
Taylor Brandstetter
2017/07/28 01:19:38
This can be a const ref to avoid a copy.
Zhi Huang
2017/08/02 04:38:34
Done.
|
| + GetAudioCodecsForOffer(media_description_options.direction); |
| + AudioCodecs filtered_codecs; |
| + if (current_description) { |
| + for (const AudioCodec& codec : audio_codecs) { |
| + if (FindMatchingCodec<AudioCodec>(audio_codecs, supported_audio_codecs, |
| + codec, nullptr)) { |
| + filtered_codecs.push_back(codec); |
| + } |
| + } |
| + } else { |
| + for (const AudioCodec& supported_audio_codec : supported_audio_codecs) { |
| + if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs, |
| + supported_audio_codec, nullptr)) { |
| + filtered_codecs.push_back(supported_audio_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 +1878,13 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( |
| 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, |
| + audio.release()); |
| + if (!AddTransportOffer(media_description_options.mid, |
| + media_description_options.transport_options, |
| current_description, desc)) { |
| return false; |
| } |
| @@ -1794,87 +1893,80 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( |
| } |
| 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; |
| + // Filter video_codecs (which includes all codecs, with correctly remapped |
| + // payload types). |
| + VideoCodecs filtered_codecs; |
| + for (const VideoCodec& codec : video_codecs) { |
| + if (FindMatchingCodec<VideoCodec>(video_codecs, video_codecs_, codec, |
| + nullptr)) { |
| + filtered_codecs.push_back(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<VideoContentDescription> video(new VideoContentDescription()); |
| std::vector<std::string> crypto_suites; |
| - GetSupportedVideoSdesCryptoSuiteNames(options.crypto_options, &crypto_suites); |
| + GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options, |
| + &crypto_suites); |
| 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, |
| + 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,32 +1982,29 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer( |
| 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, |
| + 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; |
| } |
| @@ -1923,194 +2012,202 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer( |
| } |
| 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; |
| + if (current_description) { |
| + for (const AudioCodec& codec : audio_codecs) { |
| + if (FindMatchingCodec<AudioCodec>(audio_codecs, supported_audio_codecs, |
| + codec, nullptr)) { |
| + filtered_codecs.push_back(codec); |
| + } |
| + } |
| + } else { |
| + for (const AudioCodec& supported_audio_codec : supported_audio_codecs) { |
| + if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs, |
| + supported_audio_codec, nullptr)) { |
| + filtered_codecs.push_back(supported_audio_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.sender_options, |
| + media_description_options.direction, 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; |
| } |
| + 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.sender_options, |
| + media_description_options.direction, session_options, video_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.sender_options, |
| + media_description_options.direction, 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 = 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 +2215,39 @@ bool MediaSessionDescriptionFactory::AddDataContentForAnswer( |
| // 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 || |