| Index: webrtc/pc/mediasession.cc
|
| diff --git a/webrtc/pc/mediasession.cc b/webrtc/pc/mediasession.cc
|
| index 4d4744aeb0f369994bc0e18deb0d49f5a07aa414..de8f72c92612714268570a7a2403e2c6d16827c1 100644
|
| --- a/webrtc/pc/mediasession.cc
|
| +++ b/webrtc/pc/mediasession.cc
|
| @@ -188,14 +188,11 @@
|
| return true;
|
| }
|
|
|
| -const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
|
| - if (!content) {
|
| - return nullptr;
|
| - }
|
| -
|
| - RTC_DCHECK(IsMediaContent(content));
|
| - return &(static_cast<const MediaContentDescription*>(content->description)
|
| - ->cryptos());
|
| +const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
|
| + if (!media) {
|
| + return NULL;
|
| + }
|
| + return &media->cryptos();
|
| }
|
|
|
| bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
|
| @@ -431,15 +428,15 @@
|
| private:
|
| };
|
|
|
| -// Adds a StreamParams for each SenderOptions in |sender_options| to
|
| -// content_description.
|
| +// Adds a StreamParams for each Stream in Streams with media type
|
| +// media_type to content_description.
|
| // |current_params| - All currently known StreamParams of any media type.
|
| template <class C>
|
| -static bool AddStreamParams(
|
| - const std::vector<SenderOptions>& sender_options,
|
| - const std::string& rtcp_cname,
|
| - StreamParamsVec* current_streams,
|
| - MediaContentDescriptionImpl<C>* content_description) {
|
| +static bool AddStreamParams(MediaType media_type,
|
| + const MediaSessionOptions& options,
|
| + StreamParamsVec* current_streams,
|
| + MediaContentDescriptionImpl<C>* content_description,
|
| + const bool add_legacy_stream) {
|
| // SCTP streams are not negotiated using SDP/ContentDescriptions.
|
| if (IsSctp(content_description->protocol())) {
|
| return true;
|
| @@ -448,26 +445,44 @@
|
| 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());
|
|
|
| - for (const SenderOptions& sender : sender_options) {
|
| + 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);
|
| // groupid is empty for StreamParams generated using
|
| // MediaSessionDescriptionFactory.
|
| - StreamParams* param =
|
| - GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
|
| if (!param) {
|
| - // This is a new sender.
|
| + // This is a new stream.
|
| std::vector<uint32_t> ssrcs;
|
| - GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs);
|
| + GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
|
| StreamParams stream_param;
|
| - stream_param.id = sender.track_id;
|
| + stream_param.id = stream_it->id;
|
| // Add the generated ssrc.
|
| for (size_t i = 0; i < ssrcs.size(); ++i) {
|
| stream_param.ssrcs.push_back(ssrcs[i]);
|
| }
|
| - if (sender.num_sim_layers > 1) {
|
| + if (stream_it->num_sim_layers > 1) {
|
| SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
|
| stream_param.ssrc_groups.push_back(group);
|
| }
|
| @@ -497,8 +512,8 @@
|
| << "media streams however, so no FlexFEC SSRC will be generated.";
|
| }
|
| }
|
| - stream_param.cname = rtcp_cname;
|
| - stream_param.sync_label = sender.stream_id;
|
| + stream_param.cname = options.rtcp_cname;
|
| + stream_param.sync_label = stream_it->sync_label;
|
| content_description->AddStream(stream_param);
|
|
|
| // Store the new StreamParams in current_streams.
|
| @@ -508,7 +523,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 = sender.stream_id;
|
| + param->sync_label = stream_it->sync_label;
|
| content_description->AddStream(*param);
|
| }
|
| }
|
| @@ -720,34 +735,46 @@
|
| return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
|
| }
|
|
|
| -// 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.
|
| +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.
|
| template <class C>
|
| static bool CreateMediaContentOffer(
|
| - const std::vector<SenderOptions>& sender_options,
|
| - const MediaSessionOptions& session_options,
|
| + const MediaSessionOptions& 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(session_options.rtcp_mux_enabled);
|
| + offer->set_rtcp_mux(options.rtcp_mux_enabled);
|
| if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
|
| offer->set_rtcp_reduced_size(true);
|
| }
|
| - offer->set_multistream(session_options.is_muc);
|
| + offer->set_multistream(options.is_muc);
|
| offer->set_rtp_header_extensions(rtp_extensions);
|
|
|
| - if (!AddStreamParams(sender_options, session_options.rtcp_cname,
|
| - current_streams, offer)) {
|
| + if (!AddStreamParams(offer->type(), options, current_streams, offer,
|
| + add_legacy_stream)) {
|
| return false;
|
| }
|
|
|
| @@ -855,42 +882,15 @@
|
| return false;
|
| }
|
|
|
| -// 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
|
| +// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
|
| // already exist in |offered_codecs| and ensure the payload types don't
|
| // collide.
|
| template <class C>
|
| -static void MergeCodecs(const std::vector<C>& reference_codecs,
|
| - std::vector<C>* offered_codecs,
|
| - UsedPayloadTypes* used_pltypes) {
|
| +static void FindCodecsToOffer(
|
| + 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,11 +908,33 @@
|
| !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 =
|
| - GetAssociatedCodec(reference_codecs, rtx_codec);
|
| + FindCodecById(reference_codecs, associated_pt);
|
| 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;
|
| @@ -929,22 +951,6 @@
|
| offered_codecs->push_back(rtx_codec);
|
| }
|
| }
|
| -}
|
| -
|
| -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,
|
| @@ -990,41 +996,50 @@
|
| return false;
|
| }
|
|
|
| -// 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) {
|
| +// 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) {
|
| for (auto reference_extension : reference_extensions) {
|
| - if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
|
| - nullptr)) {
|
| + if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
|
| webrtc::RtpExtension 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);
|
| - }
|
| + if (FindByUri(*all_extensions, reference_extension, &existing)) {
|
| + offered_extensions->push_back(existing);
|
| } else {
|
| - 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);
|
| - }
|
| + used_ids->FindAndSetIdUsed(&reference_extension);
|
| + all_extensions->push_back(reference_extension);
|
| + offered_extensions->push_back(reference_extension);
|
| }
|
| }
|
| }
|
| @@ -1088,24 +1103,26 @@
|
| }
|
| }
|
|
|
| -// 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.
|
| +// 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.
|
| template <class C>
|
| static bool CreateMediaContentAnswer(
|
| const MediaContentDescriptionImpl<C>* offer,
|
| - const MediaDescriptionOptions& media_description_options,
|
| - const MediaSessionOptions& session_options,
|
| + const MediaSessionOptions& 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;
|
| @@ -1119,15 +1136,14 @@
|
| &negotiated_rtp_extensions);
|
| answer->set_rtp_header_extensions(negotiated_rtp_extensions);
|
|
|
| - answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
|
| + answer->set_rtcp_mux(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, session_options.crypto_options,
|
| - &crypto)) {
|
| + if (SelectCrypto(offer, bundle_enabled, options.crypto_options, &crypto)) {
|
| if (current_cryptos) {
|
| FindMatchingCrypto(*current_cryptos, crypto, &crypto);
|
| }
|
| @@ -1139,17 +1155,29 @@
|
| return false;
|
| }
|
|
|
| - if (!AddStreamParams(media_description_options.sender_options,
|
| - session_options.rtcp_cname, current_streams, answer)) {
|
| + if (!AddStreamParams(answer->type(), options, current_streams, answer,
|
| + add_legacy_stream)) {
|
| 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());
|
| -
|
| - answer->set_direction(NegotiateRtpTransceiverDirection(
|
| - offer_rtd, media_description_options.direction)
|
| - .ToMediaContentDirection());
|
| + auto wants_rtd = RtpTransceiverDirection(wants_send, wants_receive);
|
| + answer->set_direction(NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd)
|
| + .ToMediaContentDirection());
|
| return true;
|
| }
|
|
|
| @@ -1211,20 +1239,23 @@
|
| }
|
|
|
| // Gets the current DTLS state from the transport description.
|
| -static bool IsDtlsActive(const ContentInfo* content,
|
| - const SessionDescription* current_description) {
|
| - if (!content) {
|
| - return false;
|
| - }
|
| -
|
| - size_t msection_index = content - ¤t_description->contents()[0];
|
| -
|
| - if (current_description->transport_infos().size() <= msection_index) {
|
| - return false;
|
| - }
|
| -
|
| - return current_description->transport_infos()[msection_index]
|
| - .description.secure();
|
| +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)
|
| + return false;
|
| +
|
| + const TransportDescription* current_tdesc =
|
| + GetTransportDescription(content_name, current_description);
|
| + if (!current_tdesc)
|
| + return false;
|
| +
|
| + return current_tdesc->secure();
|
| }
|
|
|
| std::string MediaContentDirectionToString(MediaContentDirection direction) {
|
| @@ -1250,54 +1281,75 @@
|
| return dir_str;
|
| }
|
|
|
| -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 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 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 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::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();
|
| +void MediaSessionOptions::AddSendStream(MediaType type,
|
| + const std::string& id,
|
| + const std::string& sync_label) {
|
| + AddSendStreamInternal(type, id, sync_label, 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 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 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::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;
|
| }
|
|
|
| MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
|
| const TransportDescriptionFactory* transport_desc_factory)
|
| - : transport_desc_factory_(transport_desc_factory) {}
|
| + : secure_(SEC_DISABLED),
|
| + add_legacy_(true),
|
| + transport_desc_factory_(transport_desc_factory) {
|
| +}
|
|
|
| MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
|
| ChannelManager* channel_manager,
|
| const TransportDescriptionFactory* transport_desc_factory)
|
| - : transport_desc_factory_(transport_desc_factory) {
|
| + : secure_(SEC_DISABLED),
|
| + add_legacy_(true),
|
| + 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_);
|
| - ComputeAudioCodecsIntersectionAndUnion();
|
| + NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
|
| + &audio_sendrecv_codecs_);
|
| }
|
|
|
| const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
|
| @@ -1317,114 +1369,129 @@
|
| const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
|
| audio_send_codecs_ = send_codecs;
|
| audio_recv_codecs_ = recv_codecs;
|
| - ComputeAudioCodecsIntersectionAndUnion();
|
| + 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_);
|
| }
|
|
|
| SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
|
| - const MediaSessionOptions& session_options,
|
| + const MediaSessionOptions& options,
|
| const SessionDescription* current_description) const {
|
| std::unique_ptr<SessionDescription> offer(new SessionDescription());
|
|
|
| StreamParamsVec current_streams;
|
| GetCurrentStreamParams(current_description, ¤t_streams);
|
|
|
| - 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 (!session_options.vad_enabled) {
|
| + 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);
|
| +
|
| + if (!options.vad_enabled) {
|
| // If application doesn't want CN codecs in offer.
|
| - StripCNCodecs(&offer_audio_codecs);
|
| - }
|
| - FilterDataCodecs(&offer_data_codecs,
|
| - session_options.data_channel_type == DCT_SCTP);
|
| + StripCNCodecs(&audio_codecs);
|
| + }
|
|
|
| RtpHeaderExtensions audio_rtp_extensions;
|
| RtpHeaderExtensions video_rtp_extensions;
|
| GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
|
| &video_rtp_extensions);
|
|
|
| - // Must have options for each existing section.
|
| + 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.
|
| if (current_description) {
|
| - RTC_DCHECK(current_description->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* 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,
|
| + 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 nullptr;
|
| + return NULL;
|
| }
|
| - break;
|
| - case MEDIA_TYPE_VIDEO:
|
| - if (!AddVideoContentForOffer(media_description_options, session_options,
|
| - current_content, current_description,
|
| - video_rtp_extensions, offer_video_codecs,
|
| + 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 nullptr;
|
| + return NULL;
|
| }
|
| - break;
|
| - case MEDIA_TYPE_DATA:
|
| - if (!AddDataContentForOffer(media_description_options, session_options,
|
| - current_content, current_description,
|
| - offer_data_codecs, ¤t_streams,
|
| + 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 nullptr;
|
| + return NULL;
|
| }
|
| - break;
|
| - default:
|
| + data_added = true;
|
| + } else {
|
| RTC_NOTREACHED();
|
| - }
|
| - ++msection_index;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // 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;
|
| }
|
|
|
| // Bundle the contents together, if we've been asked to do so, and update any
|
| // parameters that need to be tweaked for BUNDLE.
|
| - if (session_options.bundle_enabled && offer->contents().size() > 0u) {
|
| + if (options.bundle_enabled) {
|
| ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
|
| - 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);
|
| + for (ContentInfos::const_iterator content = offer->contents().begin();
|
| + content != offer->contents().end(); ++content) {
|
| + offer_bundle.AddContentName(content->name);
|
| }
|
| offer->AddGroup(offer_bundle);
|
| if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
|
| LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
|
| - return nullptr;
|
| + return NULL;
|
| }
|
| if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
|
| LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
|
| - return nullptr;
|
| - }
|
| - }
|
| + return NULL;
|
| + }
|
| + }
|
| +
|
| return offer.release();
|
| }
|
|
|
| SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
|
| - const SessionDescription* offer,
|
| - const MediaSessionOptions& session_options,
|
| + const SessionDescription* offer, const MediaSessionOptions& options,
|
| const SessionDescription* current_description) const {
|
| if (!offer) {
|
| return nullptr;
|
| @@ -1444,81 +1511,32 @@
|
| // Transport info shared by the bundle group.
|
| std::unique_ptr<TransportInfo> bundle_transport;
|
|
|
| - // 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;
|
| + 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;
|
| + }
|
| + }
|
| // 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 && session_options.bundle_enabled && offer_bundle &&
|
| + if (!added.rejected && options.bundle_enabled && offer_bundle &&
|
| offer_bundle->HasContentName(added.name)) {
|
| answer_bundle.AddContentName(added.name);
|
| bundle_transport.reset(
|
| @@ -1578,37 +1596,11 @@
|
| }
|
| }
|
|
|
| -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(
|
| +void MediaSessionDescriptionFactory::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 {
|
| @@ -1617,150 +1609,87 @@
|
| 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.
|
| + // is used.
|
| + // Add them to |used_pltypes| so the payloadtype is not reused if a new media
|
| + // type is added.
|
| if (current_description) {
|
| - MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
|
| - data_codecs, &used_pltypes);
|
| + 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);
|
| + }
|
| }
|
|
|
| // 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 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);
|
| + 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);
|
| }
|
|
|
| void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
|
| const SessionDescription* current_description,
|
| - RtpHeaderExtensions* offer_audio_extensions,
|
| - RtpHeaderExtensions* offer_video_extensions) const {
|
| + RtpHeaderExtensions* audio_extensions,
|
| + RtpHeaderExtensions* 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;
|
| - offer_audio_extensions->clear();
|
| - offer_video_extensions->clear();
|
| + audio_extensions->clear();
|
| + 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) {
|
| - 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);
|
| - }
|
| + 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);
|
| }
|
| }
|
|
|
| // Add our default RTP header extensions that are not in
|
| // |current_description|.
|
| - 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);
|
| -
|
| + FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
|
| + &all_regular_extensions, &used_ids);
|
| + FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
|
| + &all_regular_extensions, &used_ids);
|
| // TODO(jbauch): Support adding encrypted header extensions to existing
|
| // sessions.
|
| if (enable_encrypted_rtp_header_extensions_ && !current_description) {
|
| - AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
|
| - &all_encrypted_extensions, &used_ids);
|
| - AddEncryptedVersionsOfHdrExts(offer_video_extensions,
|
| - &all_encrypted_extensions, &used_ids);
|
| + AddEncryptedVersionsOfHdrExts(audio_extensions, &all_encrypted_extensions,
|
| + &used_ids);
|
| + AddEncryptedVersionsOfHdrExts(video_extensions, &all_encrypted_extensions,
|
| + &used_ids);
|
| }
|
| }
|
|
|
| @@ -1814,71 +1743,35 @@
|
| 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 MediaDescriptionOptions& media_description_options,
|
| - const MediaSessionOptions& session_options,
|
| - const ContentInfo* current_content,
|
| + const MediaSessionOptions& options,
|
| const SessionDescription* current_description,
|
| const RtpHeaderExtensions& audio_rtp_extensions,
|
| const AudioCodecs& audio_codecs,
|
| StreamParamsVec* current_streams,
|
| SessionDescription* desc) const {
|
| - // 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);
|
| - }
|
| - }
|
| + const ContentInfo* current_audio_content =
|
| + GetFirstAudioContent(current_description);
|
| + std::string content_name =
|
| + current_audio_content ? current_audio_content->name : CN_AUDIO;
|
|
|
| cricket::SecurePolicy sdes_policy =
|
| - IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
|
| - : secure();
|
| + IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
|
| + : secure();
|
|
|
| std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
|
| std::vector<std::string> crypto_suites;
|
| - GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
|
| - &crypto_suites);
|
| + GetSupportedAudioSdesCryptoSuiteNames(options.crypto_options, &crypto_suites);
|
| if (!CreateMediaContentOffer(
|
| - media_description_options.sender_options, session_options,
|
| - filtered_codecs, sdes_policy, GetCryptos(current_content),
|
| - crypto_suites, audio_rtp_extensions, current_streams, audio.get())) {
|
| + options,
|
| + audio_codecs,
|
| + sdes_policy,
|
| + GetCryptos(GetFirstAudioContentDescription(current_description)),
|
| + crypto_suites,
|
| + audio_rtp_extensions,
|
| + add_legacy_,
|
| + current_streams,
|
| + audio.get())) {
|
| return false;
|
| }
|
| audio->set_lang(lang_);
|
| @@ -1886,13 +1779,13 @@
|
| bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
|
| SetMediaProtocol(secure_transport, audio.get());
|
|
|
| - audio->set_direction(
|
| - media_description_options.direction.ToMediaContentDirection());
|
| -
|
| - 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,
|
| + auto offer_rtd =
|
| + RtpTransceiverDirection(!audio->streams().empty(), options.recv_audio);
|
| + audio->set_direction(offer_rtd.ToMediaContentDirection());
|
| +
|
| + desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
|
| + if (!AddTransportOffer(content_name,
|
| + GetTransportOptions(options, content_name),
|
| current_description, desc)) {
|
| return false;
|
| }
|
| @@ -1901,98 +1794,87 @@
|
| }
|
|
|
| bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
|
| - const MediaDescriptionOptions& media_description_options,
|
| - const MediaSessionOptions& session_options,
|
| - const ContentInfo* current_content,
|
| + const MediaSessionOptions& options,
|
| 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(current_content, current_description) ? cricket::SEC_DISABLED
|
| - : secure();
|
| + IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
|
| + : secure();
|
|
|
| std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
|
| std::vector<std::string> 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);
|
| - }
|
| - }
|
| -
|
| + GetSupportedVideoSdesCryptoSuiteNames(options.crypto_options, &crypto_suites);
|
| if (!CreateMediaContentOffer(
|
| - 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(kAutoBandwidth);
|
| + options,
|
| + video_codecs,
|
| + sdes_policy,
|
| + GetCryptos(GetFirstVideoContentDescription(current_description)),
|
| + crypto_suites,
|
| + video_rtp_extensions,
|
| + add_legacy_,
|
| + current_streams,
|
| + video.get())) {
|
| + return false;
|
| + }
|
| +
|
| + video->set_bandwidth(options.video_bandwidth);
|
|
|
| bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
|
| SetMediaProtocol(secure_transport, video.get());
|
|
|
| - video->set_direction(
|
| - media_description_options.direction.ToMediaContentDirection());
|
| -
|
| - 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,
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
|
| + if (!AddTransportOffer(content_name,
|
| + GetTransportOptions(options, content_name),
|
| current_description, desc)) {
|
| return false;
|
| }
|
| +
|
| return true;
|
| }
|
|
|
| bool MediaSessionDescriptionFactory::AddDataContentForOffer(
|
| - const MediaDescriptionOptions& media_description_options,
|
| - const MediaSessionOptions& session_options,
|
| - const ContentInfo* current_content,
|
| + const MediaSessionOptions& options,
|
| const SessionDescription* current_description,
|
| - const DataCodecs& data_codecs,
|
| + 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 = (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);
|
| - }
|
| + 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;
|
|
|
| cricket::SecurePolicy sdes_policy =
|
| - IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
|
| - : secure();
|
| + IsDtlsActive(content_name, 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
|
| @@ -2008,279 +1890,227 @@
|
| data->set_protocol(
|
| secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
|
| } else {
|
| - GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
|
| + GetSupportedDataSdesCryptoSuiteNames(options.crypto_options,
|
| &crypto_suites);
|
| }
|
|
|
| - // Even SCTP uses a "codec".
|
| if (!CreateMediaContentOffer(
|
| - media_description_options.sender_options, session_options,
|
| - data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites,
|
| - RtpHeaderExtensions(), current_streams, data.get())) {
|
| + options,
|
| + *data_codecs,
|
| + sdes_policy,
|
| + GetCryptos(GetFirstDataContentDescription(current_description)),
|
| + crypto_suites,
|
| + RtpHeaderExtensions(),
|
| + add_legacy_,
|
| + current_streams,
|
| + data.get())) {
|
| return false;
|
| }
|
|
|
| if (is_sctp) {
|
| - desc->AddContent(media_description_options.mid, NS_JINGLE_DRAFT_SCTP,
|
| - data.release());
|
| + desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
|
| } else {
|
| - data->set_bandwidth(kDataMaxBandwidth);
|
| + data->set_bandwidth(options.data_bandwidth);
|
| SetMediaProtocol(secure_transport, data.get());
|
| - desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
|
| - media_description_options.stopped, data.release());
|
| - }
|
| - if (!AddTransportOffer(media_description_options.mid,
|
| - media_description_options.transport_options,
|
| + desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
|
| + }
|
| + if (!AddTransportOffer(content_name,
|
| + GetTransportOptions(options, content_name),
|
| 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 MediaDescriptionOptions& media_description_options,
|
| - const MediaSessionOptions& session_options,
|
| - const ContentInfo* offer_content,
|
| - const SessionDescription* offer_description,
|
| - const ContentInfo* current_content,
|
| + const SessionDescription* offer,
|
| + const MediaSessionOptions& options,
|
| const SessionDescription* current_description,
|
| const TransportInfo* bundle_transport,
|
| - const AudioCodecs& audio_codecs,
|
| StreamParamsVec* current_streams,
|
| SessionDescription* answer) const {
|
| - const AudioContentDescription* offer_audio_description =
|
| - static_cast<const AudioContentDescription*>(offer_content->description);
|
| + const ContentInfo* audio_content = GetFirstAudioContent(offer);
|
| + const AudioContentDescription* offer_audio =
|
| + static_cast<const AudioContentDescription*>(audio_content->description);
|
|
|
| std::unique_ptr<TransportDescription> audio_transport(
|
| - CreateTransportAnswer(media_description_options.mid, offer_description,
|
| - media_description_options.transport_options,
|
| + CreateTransportAnswer(audio_content->name, offer,
|
| + GetTransportOptions(options, audio_content->name),
|
| current_description, bundle_transport != nullptr));
|
| if (!audio_transport) {
|
| return false;
|
| }
|
|
|
| - // 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());
|
| + // 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());
|
| auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
|
| - 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_description->HasGroup(GROUP_TYPE_BUNDLE) &&
|
| - session_options.bundle_enabled;
|
| + AudioCodecs audio_codecs = GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
|
| + if (!options.vad_enabled) {
|
| + StripCNCodecs(&audio_codecs);
|
| + }
|
| +
|
| + bool bundle_enabled =
|
| + offer->HasGroup(GROUP_TYPE_BUNDLE) && 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_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())) {
|
| + 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())) {
|
| return false; // Fails the session setup.
|
| }
|
|
|
| bool secure = bundle_transport ? bundle_transport->description.secure()
|
| : audio_transport->secure();
|
| - bool rejected = media_description_options.stopped ||
|
| - offer_content->rejected ||
|
| + bool rejected = !options.has_audio() || audio_content->rejected ||
|
| !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
|
| audio_answer->protocol(), secure);
|
| if (!rejected) {
|
| - AddTransportAnswer(media_description_options.mid, *(audio_transport.get()),
|
| - answer);
|
| + AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
|
| } else {
|
| - LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
|
| - << "' being rejected in answer.";
|
| - }
|
| -
|
| - answer->AddContent(media_description_options.mid, offer_content->type,
|
| - rejected, audio_answer.release());
|
| + // 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.";
|
| + }
|
| +
|
| + answer->AddContent(audio_content->name, audio_content->type, rejected,
|
| + audio_answer.release());
|
| return true;
|
| }
|
|
|
| bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
|
| - const MediaDescriptionOptions& media_description_options,
|
| - const MediaSessionOptions& session_options,
|
| - const ContentInfo* offer_content,
|
| - const SessionDescription* offer_description,
|
| - const ContentInfo* current_content,
|
| + const SessionDescription* offer,
|
| + const MediaSessionOptions& options,
|
| const SessionDescription* current_description,
|
| const TransportInfo* bundle_transport,
|
| - const VideoCodecs& video_codecs,
|
| StreamParamsVec* current_streams,
|
| SessionDescription* answer) const {
|
| - const VideoContentDescription* offer_video_description =
|
| - static_cast<const VideoContentDescription*>(offer_content->description);
|
| -
|
| + const ContentInfo* video_content = GetFirstVideoContent(offer);
|
| std::unique_ptr<TransportDescription> video_transport(
|
| - CreateTransportAnswer(media_description_options.mid, offer_description,
|
| - media_description_options.transport_options,
|
| + CreateTransportAnswer(video_content->name, offer,
|
| + GetTransportOptions(options, video_content->name),
|
| 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(
|
| - 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.
|
| + 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;
|
| }
|
| bool secure = bundle_transport ? bundle_transport->description.secure()
|
| : video_transport->secure();
|
| - bool rejected = media_description_options.stopped ||
|
| - offer_content->rejected ||
|
| + bool rejected = !options.has_video() || video_content->rejected ||
|
| !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
|
| video_answer->protocol(), secure);
|
| if (!rejected) {
|
| - if (!AddTransportAnswer(media_description_options.mid,
|
| - *(video_transport.get()), answer)) {
|
| + if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
|
| + answer)) {
|
| return false;
|
| }
|
| - video_answer->set_bandwidth(kAutoBandwidth);
|
| + video_answer->set_bandwidth(options.video_bandwidth);
|
| } else {
|
| - LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
|
| - << "' being rejected in answer.";
|
| - }
|
| - answer->AddContent(media_description_options.mid, offer_content->type,
|
| - rejected, video_answer.release());
|
| + // 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.";
|
| + }
|
| + answer->AddContent(video_content->name, video_content->type, rejected,
|
| + video_answer.release());
|
| return true;
|
| }
|
|
|
| bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
|
| - const MediaDescriptionOptions& media_description_options,
|
| - const MediaSessionOptions& session_options,
|
| - const ContentInfo* offer_content,
|
| - const SessionDescription* offer_description,
|
| - const ContentInfo* current_content,
|
| + const SessionDescription* offer,
|
| + const MediaSessionOptions& options,
|
| 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(media_description_options.mid, offer_description,
|
| - media_description_options.transport_options,
|
| + CreateTransportAnswer(data_content->name, offer,
|
| + GetTransportOptions(options, data_content->name),
|
| 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_description->HasGroup(GROUP_TYPE_BUNDLE) &&
|
| - session_options.bundle_enabled;
|
| + bool bundle_enabled =
|
| + offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
|
| if (!CreateMediaContentAnswer(
|
| static_cast<const DataContentDescription*>(
|
| - 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())) {
|
| + 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())) {
|
| return false; // Fails the session setup.
|
| }
|
|
|
| // Respond with sctpmap if the offer uses sctpmap.
|
| const DataContentDescription* offer_data_description =
|
| - static_cast<const DataContentDescription*>(offer_content->description);
|
| + static_cast<const DataContentDescription*>(data_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 = session_options.data_channel_type == DCT_NONE ||
|
| - media_description_options.stopped ||
|
| - offer_content->rejected ||
|
| + bool rejected = !options.has_data() || data_content->rejected ||
|
| !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
|
| data_answer->protocol(), secure);
|
| if (!rejected) {
|
| - data_answer->set_bandwidth(kDataMaxBandwidth);
|
| - if (!AddTransportAnswer(media_description_options.mid,
|
| - *(data_transport.get()), answer)) {
|
| + data_answer->set_bandwidth(options.data_bandwidth);
|
| + if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
|
| + answer)) {
|
| return false;
|
| }
|
| } else {
|
| @@ -2288,37 +2118,9 @@
|
| // 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(media_description_options.mid, offer_content->type,
|
| - rejected, data_answer.release());
|
| + answer->AddContent(data_content->name, data_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) {
|
|
|