| Index: webrtc/pc/mediasession.cc
|
| diff --git a/webrtc/pc/mediasession.cc b/webrtc/pc/mediasession.cc
|
| index 2f973deb7f6476a223d2b8713fa66906d18ba777..709e5c15debf25b164127c955b4dcd7cb6921075 100644
|
| --- a/webrtc/pc/mediasession.cc
|
| +++ b/webrtc/pc/mediasession.cc
|
| @@ -159,11 +159,12 @@ bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
|
| }
|
| #endif
|
|
|
| -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,
|
| @@ -332,28 +333,34 @@ class UsedIds {
|
| void FindAndSetIdUsed(std::vector<Id>* ids) {
|
| for (typename std::vector<Id>::iterator it = ids->begin();
|
| it != ids->end(); ++it) {
|
| - FindAndSetIdUsed(&*it);
|
| + FindAndSetIdUsed(&*it, true);
|
| }
|
| }
|
|
|
| // Finds and sets an unused id if the |idstruct| id is already in use.
|
| - void FindAndSetIdUsed(IdStruct* idstruct) {
|
| + // If |can_reassign_id| is false, the ID of |idstruct| won't be reassigned,
|
| + // and ID conflicts will simply result in false being returned.
|
| + bool FindAndSetIdUsed(IdStruct* idstruct, bool can_reassign_id) {
|
| const int original_id = idstruct->id;
|
| int new_id = idstruct->id;
|
|
|
| if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
|
| // If the original id is not in range - this is an id that can't be
|
| // dynamically changed.
|
| - return;
|
| + return true;
|
| }
|
|
|
| if (IsIdUsed(original_id)) {
|
| + if (!can_reassign_id) {
|
| + return false;
|
| + }
|
| new_id = FindUnusedId();
|
| LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
|
| << " to " << new_id;
|
| idstruct->id = new_id;
|
| }
|
| SetIdUsed(new_id);
|
| + return true;
|
| }
|
|
|
| private:
|
| @@ -390,7 +397,6 @@ class UsedPayloadTypes : public UsedIds<Codec> {
|
| : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
|
| }
|
|
|
| -
|
| private:
|
| static const int kDynamicPayloadTypeMin = 96;
|
| static const int kDynamicPayloadTypeMax = 127;
|
| @@ -414,15 +420,15 @@ static bool IsSctp(const MediaContentDescription* desc) {
|
| (desc->protocol() == kMediaProtocolDtlsSctp));
|
| }
|
|
|
| -// 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.
|
| +// Adds a StreamParams for each SenderOptions in |sender_options| to
|
| +// content_description.
|
| +// |current_params| - All currently known StreamParams.
|
| 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)) {
|
| return true;
|
| @@ -431,45 +437,25 @@ 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) {
|
| - // 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.
|
| -
|
| - const StreamParams* param =
|
| - GetStreamByIds(*current_streams, "", stream_it->id);
|
| + for (const SenderOptions& sender : sender_options) {
|
| // groupid is empty for StreamParams generated using
|
| // MediaSessionDescriptionFactory.
|
| + const StreamParams* param =
|
| + GetStreamByIds(*current_streams, "", 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);
|
| }
|
| @@ -499,8 +485,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.
|
| @@ -718,46 +704,35 @@ static bool IsFlexfecCodec(const C& codec) {
|
| return stricmp(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
|
| +// 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). If
|
| -// add_legacy_stream is true, and current_streams is empty, a legacy
|
| -// stream is created. The created content is added to the offer.
|
| +// 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;
|
| }
|
|
|
| @@ -867,23 +842,54 @@ static bool FindMatchingCodec(const std::vector<C>& codecs1,
|
| return false;
|
| }
|
|
|
| -// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
|
| -// already exist in |offered_codecs| and ensure the payload types don't
|
| -// collide.
|
| +// Find the codec in |codec_list| that |rtx_codec| is associated with.
|
| template <class C>
|
| -static void FindCodecsToOffer(
|
| - const std::vector<C>& reference_codecs,
|
| - std::vector<C>* offered_codecs,
|
| - UsedPayloadTypes* used_pltypes) {
|
| +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. If |can_modify_codecs| is false, a codec is only added if it
|
| +// can be added without changing any parameters (namely payload type,
|
| +// associated payload type).
|
| +template <class C>
|
| +static void MergeCodecs(const std::vector<C>& reference_codecs,
|
| + bool can_modify_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) &&
|
| !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
|
| reference_codec, nullptr)) {
|
| C codec = reference_codec;
|
| - used_pltypes->FindAndSetIdUsed(&codec);
|
| - offered_codecs->push_back(codec);
|
| + if (used_pltypes->FindAndSetIdUsed(&codec, can_modify_codecs)) {
|
| + offered_codecs->push_back(codec);
|
| + }
|
| }
|
| }
|
|
|
| @@ -893,30 +899,9 @@ 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;
|
| }
|
|
|
| @@ -930,10 +915,15 @@ static void FindCodecsToOffer(
|
| continue;
|
| }
|
|
|
| + if (!can_modify_codecs && matching_codec.id != associated_codec->id) {
|
| + continue;
|
| + }
|
| +
|
| rtx_codec.params[kCodecParamAssociatedPayloadType] =
|
| rtc::ToString(matching_codec.id);
|
| - used_pltypes->FindAndSetIdUsed(&rtx_codec);
|
| - offered_codecs->push_back(rtx_codec);
|
| + if (used_pltypes->FindAndSetIdUsed(&rtx_codec, can_modify_codecs)) {
|
| + offered_codecs->push_back(rtx_codec);
|
| + }
|
| }
|
| }
|
| }
|
| @@ -954,40 +944,28 @@ static bool FindByUri(const RtpHeaderExtensions& extensions,
|
| return false;
|
| }
|
|
|
| -// Iterates through |offered_extensions|, adding each one to |all_extensions|
|
| -// and |used_ids|, and resolving ID conflicts. If an offered extension has the
|
| -// same URI as one in |all_extensions|, it will re-use the same ID and won't be
|
| -// treated as a conflict.
|
| -static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
|
| - RtpHeaderExtensions* all_extensions,
|
| - UsedRtpHeaderExtensionIds* used_ids) {
|
| - for (auto& extension : *offered_extensions) {
|
| - webrtc::RtpExtension existing;
|
| - if (FindByUri(*all_extensions, extension, &existing)) {
|
| - extension.id = existing.id;
|
| - } else {
|
| - used_ids->FindAndSetIdUsed(&extension);
|
| - all_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 |all_extensions|, and
|
| +// if the extension is in |all_extensions| its ID is used. If
|
| +// |can_modify_extensions| is false, a extension is only added if it can be
|
| +// added without changing the ID.
|
| +static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
|
| + bool can_modify_extension,
|
| + RtpHeaderExtensions* offered_extensions,
|
| + RtpHeaderExtensions* all_extensions,
|
| + UsedRtpHeaderExtensionIds* used_ids) {
|
| for (auto reference_extension : reference_extensions) {
|
| if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
|
| webrtc::RtpExtension existing;
|
| if (FindByUri(*all_extensions, reference_extension, &existing)) {
|
| offered_extensions->push_back(existing);
|
| } else {
|
| - used_ids->FindAndSetIdUsed(&reference_extension);
|
| - all_extensions->push_back(reference_extension);
|
| - offered_extensions->push_back(reference_extension);
|
| + webrtc::RtpExtension extension = reference_extension;
|
| + if (used_ids->FindAndSetIdUsed(&extension, can_modify_extension)) {
|
| + all_extensions->push_back(extension);
|
| + offered_extensions->push_back(extension);
|
| + }
|
| }
|
| }
|
| }
|
| @@ -1019,25 +997,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
|
| + 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,
|
| StreamParamsVec* current_streams,
|
| - bool add_legacy_stream,
|
| bool bundle_enabled,
|
| MediaContentDescriptionImpl<C>* answer) {
|
| std::vector<C> negotiated_codecs;
|
| @@ -1050,14 +1030,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);
|
| }
|
| @@ -1069,29 +1050,16 @@ 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;
|
| }
|
|
|
| @@ -1173,21 +1141,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();
|
| }
|
| @@ -1234,67 +1198,54 @@ 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;
|
| - }
|
| - }
|
| - ASSERT(false);
|
| +bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
|
| + return std::find_if(media_description_options.begin(),
|
| + media_description_options.end(),
|
| + [type](const MediaDescriptionOptions& t) {
|
| + return t.type == type;
|
| + }) != media_description_options.end();
|
| }
|
|
|
| -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->GetSupportedAudioSendCodecs(&audio_send_codecs_);
|
| @@ -1302,8 +1253,23 @@ MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
|
| 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_);
|
| + // Precompute the intersection and union of audio send/recv codecs.
|
| + for (const AudioCodec& recv : audio_recv_codecs_) {
|
| + all_audio_codecs_.push_back(recv);
|
| + if (FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
|
| + recv, nullptr)) {
|
| + audio_sendrecv_codecs_.push_back(recv);
|
| + }
|
| + }
|
| + for (const AudioCodec& send : audio_send_codecs_) {
|
| + 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));
|
| + all_audio_codecs_.push_back(send);
|
| + }
|
| + }
|
| }
|
|
|
| const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
|
| @@ -1333,102 +1299,93 @@ void MediaSessionDescriptionFactory::set_audio_codecs(
|
| }
|
|
|
| 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.
|
| 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 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 types and mids must match.
|
| + 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 (!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))) {
|
| - 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 {
|
| - ASSERT(false);
|
| - }
|
| + 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())) {
|
| @@ -1445,8 +1402,10 @@ SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
|
| }
|
|
|
| SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
|
| - const SessionDescription* offer, const MediaSessionOptions& options,
|
| + const SessionDescription* offer,
|
| + const MediaSessionOptions& session_options,
|
| const SessionDescription* current_description) const {
|
| + RTC_DCHECK(offer);
|
| // The answer contains the intersection of the codecs in the offer with the
|
| // codecs we support. As indicated by XEP-0167, we retain the same payload ids
|
| // from the offer in the answer.
|
| @@ -1455,32 +1414,82 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
|
| StreamParamsVec current_streams;
|
| GetCurrentStreamParams(current_description, ¤t_streams);
|
|
|
| - if (offer) {
|
| - 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,
|
| - ¤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.
|
| + 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.
|
| + 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,
|
| + answer_audio_codecs, ¤t_streams, answer.get())) {
|
| + return nullptr;
|
| }
|
| - } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
|
| - if (!AddVideoContentForAnswer(offer, options, current_description,
|
| - ¤t_streams, answer.get())) {
|
| - return NULL;
|
| + break;
|
| + case MEDIA_TYPE_VIDEO:
|
| + if (!AddVideoContentForAnswer(
|
| + media_description_options, session_options, offer_content,
|
| + offer, current_content, current_description,
|
| + answer_video_codecs, ¤t_streams, answer.get())) {
|
| + return nullptr;
|
| }
|
| - } else {
|
| - ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
|
| - if (!AddDataContentForAnswer(offer, options, current_description,
|
| + break;
|
| + case MEDIA_TYPE_DATA:
|
| + if (!AddDataContentForAnswer(media_description_options, session_options,
|
| + offer_content, offer, current_content,
|
| + current_description, answer_data_codecs,
|
| ¤t_streams, answer.get())) {
|
| - return NULL;
|
| + return nullptr;
|
| }
|
| - }
|
| + break;
|
| + default:
|
| + RTC_NOTREACHED();
|
| }
|
| + ++msection_index;
|
| }
|
|
|
| // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
|
| // group in the answer with the appropriate content names.
|
| - if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
|
| + if (offer->HasGroup(GROUP_TYPE_BUNDLE) && session_options.bundle_enabled) {
|
| const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
|
| ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
|
| for (ContentInfos::const_iterator content = answer->contents().begin();
|
| @@ -1541,11 +1550,40 @@ const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
|
| }
|
| }
|
|
|
| -void MediaSessionDescriptionFactory::GetCodecsToOffer(
|
| +void MergeCodecsFromDescription(const SessionDescription* description,
|
| + bool can_modify_codecs,
|
| + AudioCodecs* audio_codecs,
|
| + VideoCodecs* video_codecs,
|
| + DataCodecs* data_codecs,
|
| + UsedPayloadTypes* used_pltypes) {
|
| + for (const ContentInfo& content : description->contents()) {
|
| + if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
|
| + const AudioContentDescription* audio =
|
| + static_cast<AudioContentDescription*>(content.description);
|
| + MergeCodecs<AudioCodec>(audio->codecs(), can_modify_codecs, audio_codecs,
|
| + used_pltypes);
|
| + } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
|
| + const VideoContentDescription* video =
|
| + static_cast<VideoContentDescription*>(content.description);
|
| + MergeCodecs<VideoCodec>(video->codecs(), can_modify_codecs, video_codecs,
|
| + used_pltypes);
|
| + } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
|
| + const DataContentDescription* data =
|
| + static_cast<DataContentDescription*>(content.description);
|
| + MergeCodecs<DataCodec>(data->codecs(), can_modify_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 {
|
| @@ -1554,77 +1592,167 @@ 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 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.
|
| + bool can_modify_codecs = false;
|
| 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, can_modify_codecs,
|
| + audio_codecs, video_codecs, data_codecs,
|
| + &used_pltypes);
|
| }
|
|
|
| // Add our codecs that are not in |current_description|.
|
| - FindCodecsToOffer<AudioCodec>(supported_audio_codecs, audio_codecs,
|
| - &used_pltypes);
|
| - FindCodecsToOffer<VideoCodec>(supported_video_codecs, video_codecs,
|
| - &used_pltypes);
|
| - FindCodecsToOffer<DataCodec>(supported_data_codecs, data_codecs,
|
| + // We can modify our reference codecs (payload type, associated payload type,
|
| + // etc.) since they're not part of a negotiated session description.
|
| + can_modify_codecs = true;
|
| + MergeCodecs<AudioCodec>(all_audio_codecs_, can_modify_codecs, audio_codecs,
|
| + &used_pltypes);
|
| + MergeCodecs<VideoCodec>(video_codecs_, can_modify_codecs, video_codecs,
|
| + &used_pltypes);
|
| + MergeCodecs<DataCodec>(data_codecs_, can_modify_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.
|
| + bool can_modify_codecs = false;
|
| + if (current_description) {
|
| + MergeCodecsFromDescription(current_description, can_modify_codecs,
|
| + audio_codecs, video_codecs, data_codecs,
|
| &used_pltypes);
|
| + }
|
| +
|
| + // First, 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|. Do two passes, first adding codecs with payload types that
|
| + // match the offer, then adding remaining codecs with remapped payload types.
|
| + //
|
| + // The two passes are needed so that a payload type isn't remapped to
|
| + // conflict with another codec. For example, PT "96" is in current
|
| + // description, and a remote offer has "96" and "97". If "96" were remapped
|
| + // in the first pass (to "97", since that's the next number), that's now not
|
| + // available for "97", causing "97" to be remapped to "98" and a payload type
|
| + // to be wasted.
|
| + MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, can_modify_codecs,
|
| + audio_codecs, &used_pltypes);
|
| + MergeCodecs<VideoCodec>(filtered_offered_video_codecs, can_modify_codecs,
|
| + video_codecs, &used_pltypes);
|
| + MergeCodecs<DataCodec>(filtered_offered_data_codecs, can_modify_codecs,
|
| + data_codecs, &used_pltypes);
|
| + can_modify_codecs = true;
|
| + MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, can_modify_codecs,
|
| + audio_codecs, &used_pltypes);
|
| + MergeCodecs<VideoCodec>(filtered_offered_video_codecs, can_modify_codecs,
|
| + video_codecs, &used_pltypes);
|
| + MergeCodecs<DataCodec>(filtered_offered_data_codecs, can_modify_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_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.
|
| + // is used. Add them to |used_ids| so the local ids are not reused if a new
|
| + // media type is added.
|
| + bool can_modify_extensions = false;
|
| if (current_description) {
|
| - const AudioContentDescription* audio =
|
| - GetFirstAudioContentDescription(current_description);
|
| - if (audio) {
|
| - *audio_extensions = audio->rtp_header_extensions();
|
| - FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
|
| - }
|
| - const VideoContentDescription* video =
|
| - GetFirstVideoContentDescription(current_description);
|
| - if (video) {
|
| - *video_extensions = video->rtp_header_extensions();
|
| - FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
|
| + for (const ContentInfo& content : current_description->contents()) {
|
| + if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
|
| + continue;
|
| + }
|
| + if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
|
| + const AudioContentDescription* audio =
|
| + static_cast<const AudioContentDescription*>(content.description);
|
| + MergeRtpHdrExts(audio->rtp_header_extensions(), can_modify_extensions,
|
| + offer_audio_extensions, &all_extensions, &used_ids);
|
| + } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
|
| + const VideoContentDescription* video =
|
| + static_cast<const VideoContentDescription*>(content.description);
|
| + MergeRtpHdrExts(video->rtp_header_extensions(), can_modify_extensions,
|
| + offer_video_extensions, &all_extensions, &used_ids);
|
| + }
|
| }
|
| }
|
| + // TODO: Do this for answer too?
|
|
|
| // Add our default RTP header extensions that are not in
|
| // |current_description|.
|
| - FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
|
| - &all_extensions, &used_ids);
|
| - FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
|
| - &all_extensions, &used_ids);
|
| + can_modify_extensions = true;
|
| + MergeRtpHdrExts(audio_rtp_header_extensions(), can_modify_extensions,
|
| + offer_audio_extensions, &all_extensions, &used_ids);
|
| + MergeRtpHdrExts(video_rtp_header_extensions(), can_modify_extensions,
|
| + offer_video_extensions, &all_extensions, &used_ids);
|
| }
|
|
|
| bool MediaSessionDescriptionFactory::AddTransportOffer(
|
| @@ -1677,34 +1805,38 @@ 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.
|
| + const AudioCodecs& supported_audio_codecs =
|
| + GetAudioCodecsForOffer(media_description_options.direction);
|
| + AudioCodecs filtered_codecs;
|
| + for (const AudioCodec& codec : audio_codecs) {
|
| + if (FindMatchingCodec<AudioCodec>(audio_codecs, supported_audio_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<AudioContentDescription> audio(new AudioContentDescription());
|
| std::vector<std::string> crypto_suites;
|
| - GetSupportedAudioCryptoSuiteNames(options.crypto_options, &crypto_suites);
|
| + GetSupportedAudioCryptoSuiteNames(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_);
|
| @@ -1712,13 +1844,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;
|
| }
|
| @@ -1727,87 +1859,72 @@ 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;
|
| - GetSupportedVideoCryptoSuiteNames(options.crypto_options, &crypto_suites);
|
| + GetSupportedVideoCryptoSuiteNames(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);
|
|
|
| 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
|
| @@ -1820,31 +1937,29 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer(
|
| data->set_protocol(
|
| secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
|
| } else {
|
| - GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites);
|
| + GetSupportedDataCryptoSuiteNames(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;
|
| }
|
| @@ -1852,175 +1967,173 @@ 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 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), current_description));
|
| + media_description_options.mid, offer_description,
|
| + media_description_options.transport_options, current_description));
|
| 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);
|
| + const AudioCodecs& supported_audio_codecs =
|
| + GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
|
| + AudioCodecs filtered_codecs;
|
| + for (const AudioCodec& codec : audio_codecs) {
|
| + if (FindMatchingCodec<AudioCodec>(audio_codecs, supported_audio_codecs,
|
| + codec, nullptr) &&
|
| + FindMatchingCodec<AudioCodec>(audio_codecs, supported_audio_codecs,
|
| + codec, nullptr)) {
|
| + filtered_codecs.push_back(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_,
|
| - 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_,
|
| + current_streams, bundle_enabled, audio_answer.get())) {
|
| return false; // Fails the session setup.
|
| }
|
|
|
| - bool rejected = !options.has_audio() || audio_content->rejected ||
|
| - !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
|
| - audio_answer->protocol(),
|
| + bool rejected =
|
| + media_description_options.stopped || offer_content->rejected ||
|
| + !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO, audio_answer->protocol(),
|
| audio_transport->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 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), current_description));
|
| + media_description_options.mid, offer_description,
|
| + media_description_options.transport_options, current_description));
|
| 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_,
|
| - 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_,
|
| + current_streams, bundle_enabled, video_answer.get())) {
|
| + return false; // Fails the session setup.
|
| }
|
| - bool rejected = !options.has_video() || video_content->rejected ||
|
| - !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
|
| - video_answer->protocol(),
|
| +
|
| + bool rejected =
|
| + media_description_options.stopped || offer_content->rejected ||
|
| + !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, video_answer->protocol(),
|
| video_transport->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 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), current_description));
|
| + media_description_options.mid, offer_description,
|
| + media_description_options.transport_options, current_description));
|
| 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(),
|
| - 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(),
|
| + current_streams, bundle_enabled, data_answer.get())) {
|
| return false; // Fails the session setup.
|
| }
|
|
|
| - bool rejected = !options.has_data() || data_content->rejected ||
|
| - !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
|
| - data_answer->protocol(),
|
| + bool rejected =
|
| + media_description_options.stopped || offer_content->rejected ||
|
| + !IsMediaProtocolSupported(MEDIA_TYPE_DATA, data_answer->protocol(),
|
| data_transport->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 {
|
| @@ -2028,8 +2141,8 @@ 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;
|
| }
|
|
|
|
|