3 * Copyright 2004 Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "talk/session/media/mediasession.h"
35 #include "talk/base/helpers.h"
36 #include "talk/base/logging.h"
37 #include "talk/base/scoped_ptr.h"
38 #include "talk/base/stringutils.h"
39 #include "talk/media/base/constants.h"
40 #include "talk/media/base/cryptoparams.h"
41 #include "talk/p2p/base/constants.h"
42 #include "talk/session/media/channelmanager.h"
43 #include "talk/session/media/srtpfilter.h"
44 #include "talk/xmpp/constants.h"
47 #include "talk/media/sctp/sctpdataengine.h"
49 static const uint32 kMaxSctpSid = 1023;
53 const char kInline[] = "inline:";
58 using talk_base::scoped_ptr;
61 // http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
63 const char kMediaProtocolAvpf[] = "RTP/AVPF";
65 const char kMediaProtocolSavpf[] = "RTP/SAVPF";
67 const char kMediaProtocolRtpPrefix[] = "RTP/";
69 const char kMediaProtocolSctp[] = "SCTP";
70 const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
72 static bool IsMediaContentOfType(const ContentInfo* content,
73 MediaType media_type) {
74 if (!IsMediaContent(content)) {
78 const MediaContentDescription* mdesc =
79 static_cast<const MediaContentDescription*>(content->description);
80 return mdesc && mdesc->type() == media_type;
83 static bool CreateCryptoParams(int tag, const std::string& cipher,
86 key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
88 if (!talk_base::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
92 out->cipher_suite = cipher;
93 out->key_params = kInline;
94 out->key_params += key;
99 static bool AddCryptoParams(const std::string& cipher_suite,
100 CryptoParamsVec *out) {
101 int size = static_cast<int>(out->size());
103 out->resize(size + 1);
104 return CreateCryptoParams(size, cipher_suite, &out->at(size));
107 void AddMediaCryptos(const CryptoParamsVec& cryptos,
108 MediaContentDescription* media) {
109 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
110 crypto != cryptos.end(); ++crypto) {
111 media->AddCrypto(*crypto);
115 bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
116 MediaContentDescription* media) {
117 CryptoParamsVec cryptos;
118 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
119 it != crypto_suites.end(); ++it) {
120 if (!AddCryptoParams(*it, &cryptos)) {
124 AddMediaCryptos(cryptos, media);
129 const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
133 return &media->cryptos();
136 bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
137 const CryptoParams& crypto,
139 for (CryptoParamsVec::const_iterator it = cryptos.begin();
140 it != cryptos.end(); ++it) {
141 if (crypto.Matches(*it)) {
149 // For audio, HMAC 32 is prefered because of the low overhead.
150 void GetSupportedAudioCryptoSuites(
151 std::vector<std::string>* crypto_suites) {
153 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_32);
154 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80);
158 void GetSupportedVideoCryptoSuites(
159 std::vector<std::string>* crypto_suites) {
160 GetSupportedDefaultCryptoSuites(crypto_suites);
163 void GetSupportedDataCryptoSuites(
164 std::vector<std::string>* crypto_suites) {
165 GetSupportedDefaultCryptoSuites(crypto_suites);
168 void GetSupportedDefaultCryptoSuites(
169 std::vector<std::string>* crypto_suites) {
171 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80);
175 // For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
176 // tolerated unless bundle is enabled because it is low overhead. Pick the
177 // crypto in the list that is supported.
178 static bool SelectCrypto(const MediaContentDescription* offer,
180 CryptoParams *crypto) {
181 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
182 const CryptoParamsVec& cryptos = offer->cryptos();
184 for (CryptoParamsVec::const_iterator i = cryptos.begin();
185 i != cryptos.end(); ++i) {
186 if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
187 (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio && !bundle)) {
188 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
194 static const StreamParams* FindFirstStreamParamsByCname(
195 const StreamParamsVec& params_vec,
196 const std::string& cname) {
197 for (StreamParamsVec::const_iterator it = params_vec.begin();
198 it != params_vec.end(); ++it) {
199 if (cname == it->cname)
205 // Generates a new CNAME or the CNAME of an already existing StreamParams
206 // if a StreamParams exist for another Stream in streams with sync_label
208 static bool GenerateCname(const StreamParamsVec& params_vec,
209 const MediaSessionOptions::Streams& streams,
210 const std::string& synch_label,
211 std::string* cname) {
212 ASSERT(cname != NULL);
216 // Check if a CNAME exist for any of the other synched streams.
217 for (MediaSessionOptions::Streams::const_iterator stream_it = streams.begin();
218 stream_it != streams.end() ; ++stream_it) {
219 if (synch_label != stream_it->sync_label)
223 // groupid is empty for StreamParams generated using
224 // MediaSessionDescriptionFactory.
225 if (GetStreamByIds(params_vec, "", stream_it->id,
227 *cname = param.cname;
231 // No other stream seems to exist that we should sync with.
232 // Generate a random string for the RTCP CNAME, as stated in RFC 6222.
233 // This string is only used for synchronization, and therefore is opaque.
235 if (!talk_base::CreateRandomString(16, cname)) {
239 } while (FindFirstStreamParamsByCname(params_vec, *cname));
244 // Generate random SSRC values that are not already present in |params_vec|.
245 // The generated values are added to |ssrcs|.
246 // |num_ssrcs| is the number of the SSRC will be generated.
247 static void GenerateSsrcs(const StreamParamsVec& params_vec,
249 std::vector<uint32>* ssrcs) {
250 for (int i = 0; i < num_ssrcs; i++) {
253 candidate = talk_base::CreateRandomNonZeroId();
254 } while (GetStreamBySsrc(params_vec, candidate, NULL) ||
255 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
256 ssrcs->push_back(candidate);
260 // Returns false if we exhaust the range of SIDs.
261 static bool GenerateSctpSid(const StreamParamsVec& params_vec,
263 if (params_vec.size() > kMaxSctpSid) {
265 "Could not generate an SCTP SID: too many SCTP streams.";
269 uint32 candidate = talk_base::CreateRandomNonZeroId() % kMaxSctpSid;
270 if (!GetStreamBySsrc(params_vec, candidate, NULL)) {
277 static bool GenerateSctpSids(const StreamParamsVec& params_vec,
278 std::vector<uint32>* sids) {
280 if (!GenerateSctpSid(params_vec, &sid)) {
281 LOG(LS_WARNING) << "Could not generated an SCTP SID.";
284 sids->push_back(sid);
288 // Finds all StreamParams of all media types and attach them to stream_params.
289 static void GetCurrentStreamParams(const SessionDescription* sdesc,
290 StreamParamsVec* stream_params) {
294 const ContentInfos& contents = sdesc->contents();
295 for (ContentInfos::const_iterator content = contents.begin();
296 content != contents.end(); ++content) {
297 if (!IsMediaContent(&*content)) {
300 const MediaContentDescription* media =
301 static_cast<const MediaContentDescription*>(
302 content->description);
303 const StreamParamsVec& streams = media->streams();
304 for (StreamParamsVec::const_iterator it = streams.begin();
305 it != streams.end(); ++it) {
306 stream_params->push_back(*it);
311 template <typename IdStruct>
314 UsedIds(int min_allowed_id, int max_allowed_id)
315 : min_allowed_id_(min_allowed_id),
316 max_allowed_id_(max_allowed_id),
317 next_id_(max_allowed_id) {
320 // Loops through all Id in |ids| and changes its id if it is
321 // already in use by another IdStruct. Call this methods with all Id
322 // in a session description to make sure no duplicate ids exists.
323 // Note that typename Id must be a type of IdStruct.
324 template <typename Id>
325 void FindAndSetIdUsed(std::vector<Id>* ids) {
326 for (typename std::vector<Id>::iterator it = ids->begin();
327 it != ids->end(); ++it) {
328 FindAndSetIdUsed(&*it);
332 // Finds and sets an unused id if the |idstruct| id is already in use.
333 void FindAndSetIdUsed(IdStruct* idstruct) {
334 const int original_id = idstruct->id;
335 int new_id = idstruct->id;
337 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
338 // If the original id is not in range - this is an id that can't be
339 // dynamically changed.
343 if (IsIdUsed(original_id)) {
344 new_id = FindUnusedId();
345 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
347 idstruct->id = new_id;
353 // Returns the first unused id in reverse order.
354 // This hopefully reduce the risk of more collisions. We want to change the
355 // default ids as little as possible.
357 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
360 ASSERT(next_id_ >= min_allowed_id_);
364 bool IsIdUsed(int new_id) {
365 return id_set_.find(new_id) != id_set_.end();
368 void SetIdUsed(int new_id) {
369 id_set_.insert(new_id);
372 const int min_allowed_id_;
373 const int max_allowed_id_;
375 std::set<int> id_set_;
378 // Helper class used for finding duplicate RTP payload types among audio, video
379 // and data codecs. When bundle is used the payload types may not collide.
380 class UsedPayloadTypes : public UsedIds<Codec> {
383 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
388 static const int kDynamicPayloadTypeMin = 96;
389 static const int kDynamicPayloadTypeMax = 127;
392 // Helper class used for finding duplicate RTP Header extension ids among
393 // audio and video extensions.
394 class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> {
396 UsedRtpHeaderExtensionIds()
397 : UsedIds<RtpHeaderExtension>(kLocalIdMin, kLocalIdMax) {
401 // Min and Max local identifier for one-byte header extensions, per RFC5285.
402 static const int kLocalIdMin = 1;
403 static const int kLocalIdMax = 14;
406 static bool IsSctp(const MediaContentDescription* desc) {
407 return ((desc->protocol() == kMediaProtocolSctp) ||
408 (desc->protocol() == kMediaProtocolDtlsSctp));
411 // Adds a StreamParams for each Stream in Streams with media type
412 // media_type to content_description.
413 // |current_params| - All currently known StreamParams of any media type.
415 static bool AddStreamParams(
416 MediaType media_type,
417 const MediaSessionOptions::Streams& streams,
418 StreamParamsVec* current_streams,
419 MediaContentDescriptionImpl<C>* content_description,
420 const bool add_legacy_stream) {
421 const bool include_rtx_stream =
422 ContainsRtxCodec(content_description->codecs());
424 if (streams.empty() && add_legacy_stream) {
425 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
426 std::vector<uint32> ssrcs;
427 if (IsSctp(content_description)) {
428 GenerateSctpSids(*current_streams, &ssrcs);
430 int num_ssrcs = include_rtx_stream ? 2 : 1;
431 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
433 if (include_rtx_stream) {
434 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
435 content_description->set_multistream(true);
437 content_description->AddLegacyStream(ssrcs[0]);
442 MediaSessionOptions::Streams::const_iterator stream_it;
443 for (stream_it = streams.begin();
444 stream_it != streams.end(); ++stream_it) {
445 if (stream_it->type != media_type)
446 continue; // Wrong media type.
449 // groupid is empty for StreamParams generated using
450 // MediaSessionDescriptionFactory.
451 if (!GetStreamByIds(*current_streams, "", stream_it->id,
453 // This is a new stream.
454 // Get a CNAME. Either new or same as one of the other synched streams.
456 if (!GenerateCname(*current_streams, streams, stream_it->sync_label,
461 std::vector<uint32> ssrcs;
462 if (IsSctp(content_description)) {
463 GenerateSctpSids(*current_streams, &ssrcs);
465 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
467 StreamParams stream_param;
468 stream_param.id = stream_it->id;
469 // Add the generated ssrc.
470 for (size_t i = 0; i < ssrcs.size(); ++i) {
471 stream_param.ssrcs.push_back(ssrcs[i]);
473 if (stream_it->num_sim_layers > 1) {
474 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
475 stream_param.ssrc_groups.push_back(group);
477 // Generate an extra ssrc for include_rtx_stream case.
478 if (include_rtx_stream) {
479 std::vector<uint32> rtx_ssrc;
480 GenerateSsrcs(*current_streams, 1, &rtx_ssrc);
481 stream_param.AddFidSsrc(ssrcs[0], rtx_ssrc[0]);
482 content_description->set_multistream(true);
484 stream_param.cname = cname;
485 stream_param.sync_label = stream_it->sync_label;
486 content_description->AddStream(stream_param);
488 // Store the new StreamParams in current_streams.
489 // This is necessary so that we can use the CNAME for other media types.
490 current_streams->push_back(stream_param);
492 content_description->AddStream(param);
498 // Updates the transport infos of the |sdesc| according to the given
499 // |bundle_group|. The transport infos of the content names within the
500 // |bundle_group| should be updated to use the ufrag and pwd of the first
501 // content within the |bundle_group|.
502 static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
503 SessionDescription* sdesc) {
504 // The bundle should not be empty.
505 if (!sdesc || !bundle_group.FirstContentName()) {
509 // We should definitely have a transport for the first content.
510 std::string selected_content_name = *bundle_group.FirstContentName();
511 const TransportInfo* selected_transport_info =
512 sdesc->GetTransportInfoByName(selected_content_name);
513 if (!selected_transport_info) {
517 // Set the other contents to use the same ICE credentials.
518 const std::string selected_ufrag =
519 selected_transport_info->description.ice_ufrag;
520 const std::string selected_pwd =
521 selected_transport_info->description.ice_pwd;
522 for (TransportInfos::iterator it =
523 sdesc->transport_infos().begin();
524 it != sdesc->transport_infos().end(); ++it) {
525 if (bundle_group.HasContentName(it->content_name) &&
526 it->content_name != selected_content_name) {
527 it->description.ice_ufrag = selected_ufrag;
528 it->description.ice_pwd = selected_pwd;
534 // Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
535 // sets it to |cryptos|.
536 static bool GetCryptosByName(const SessionDescription* sdesc,
537 const std::string& content_name,
538 CryptoParamsVec* cryptos) {
539 if (!sdesc || !cryptos) {
543 const ContentInfo* content = sdesc->GetContentByName(content_name);
544 if (!IsMediaContent(content) || !content->description) {
548 const MediaContentDescription* media_desc =
549 static_cast<const MediaContentDescription*>(content->description);
550 *cryptos = media_desc->cryptos();
554 // Predicate function used by the remove_if.
555 // Returns true if the |crypto|'s cipher_suite is not found in |filter|.
556 static bool CryptoNotFound(const CryptoParams crypto,
557 const CryptoParamsVec* filter) {
558 if (filter == NULL) {
561 for (CryptoParamsVec::const_iterator it = filter->begin();
562 it != filter->end(); ++it) {
563 if (it->cipher_suite == crypto.cipher_suite) {
570 // Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
571 // which are not available in |filter|.
572 static void PruneCryptos(const CryptoParamsVec& filter,
573 CryptoParamsVec* target_cryptos) {
574 if (!target_cryptos) {
577 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
578 target_cryptos->end(),
579 bind2nd(ptr_fun(CryptoNotFound),
581 target_cryptos->end());
584 static bool IsRtpContent(SessionDescription* sdesc,
585 const std::string& content_name) {
587 ContentInfo* content = sdesc->GetContentByName(content_name);
588 if (IsMediaContent(content)) {
589 MediaContentDescription* media_desc =
590 static_cast<MediaContentDescription*>(content->description);
594 is_rtp = media_desc->protocol().empty() ||
595 talk_base::starts_with(media_desc->protocol().data(),
596 kMediaProtocolRtpPrefix);
601 // Updates the crypto parameters of the |sdesc| according to the given
602 // |bundle_group|. The crypto parameters of all the contents within the
603 // |bundle_group| should be updated to use the common subset of the
604 // available cryptos.
605 static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
606 SessionDescription* sdesc) {
607 // The bundle should not be empty.
608 if (!sdesc || !bundle_group.FirstContentName()) {
612 bool common_cryptos_needed = false;
613 // Get the common cryptos.
614 const ContentNames& content_names = bundle_group.content_names();
615 CryptoParamsVec common_cryptos;
616 for (ContentNames::const_iterator it = content_names.begin();
617 it != content_names.end(); ++it) {
618 if (!IsRtpContent(sdesc, *it)) {
621 // The common cryptos are needed if any of the content does not have DTLS
623 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
624 common_cryptos_needed = true;
626 if (it == content_names.begin()) {
627 // Initial the common_cryptos with the first content in the bundle group.
628 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
631 if (common_cryptos.empty()) {
632 // If there's no crypto params, we should just return.
636 CryptoParamsVec cryptos;
637 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
640 PruneCryptos(cryptos, &common_cryptos);
644 if (common_cryptos.empty() && common_cryptos_needed) {
648 // Update to use the common cryptos.
649 for (ContentNames::const_iterator it = content_names.begin();
650 it != content_names.end(); ++it) {
651 if (!IsRtpContent(sdesc, *it)) {
654 ContentInfo* content = sdesc->GetContentByName(*it);
655 if (IsMediaContent(content)) {
656 MediaContentDescription* media_desc =
657 static_cast<MediaContentDescription*>(content->description);
661 media_desc->set_cryptos(common_cryptos);
668 static bool ContainsRtxCodec(const std::vector<C>& codecs) {
669 typename std::vector<C>::const_iterator it;
670 for (it = codecs.begin(); it != codecs.end(); ++it) {
671 if (IsRtxCodec(*it)) {
679 static bool IsRtxCodec(const C& codec) {
680 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
683 // Create a media content to be offered in a session-initiate,
684 // according to the given options.rtcp_mux, options.is_muc,
685 // options.streams, codecs, secure_transport, crypto, and streams. If we don't
686 // currently have crypto (in current_cryptos) and it is enabled (in
687 // secure_policy), crypto is created (according to crypto_suites). If
688 // add_legacy_stream is true, and current_streams is empty, a legacy
689 // stream is created. The created content is added to the offer.
691 static bool CreateMediaContentOffer(
692 const MediaSessionOptions& options,
693 const std::vector<C>& codecs,
694 const SecurePolicy& secure_policy,
695 const CryptoParamsVec* current_cryptos,
696 const std::vector<std::string>& crypto_suites,
697 const RtpHeaderExtensions& rtp_extensions,
698 bool add_legacy_stream,
699 StreamParamsVec* current_streams,
700 MediaContentDescriptionImpl<C>* offer) {
701 offer->AddCodecs(codecs);
704 if (secure_policy == SEC_REQUIRED) {
705 offer->set_crypto_required(CT_SDES);
707 offer->set_rtcp_mux(options.rtcp_mux_enabled);
708 offer->set_multistream(options.is_muc);
709 offer->set_rtp_header_extensions(rtp_extensions);
711 if (!AddStreamParams(
712 offer->type(), options.streams, current_streams,
713 offer, add_legacy_stream)) {
718 if (secure_policy != SEC_DISABLED) {
719 if (current_cryptos) {
720 AddMediaCryptos(*current_cryptos, offer);
722 if (offer->cryptos().empty()) {
723 if (!CreateMediaCryptos(crypto_suites, offer)) {
730 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
737 static void NegotiateCodecs(const std::vector<C>& local_codecs,
738 const std::vector<C>& offered_codecs,
739 std::vector<C>* negotiated_codecs) {
740 typename std::vector<C>::const_iterator ours;
741 for (ours = local_codecs.begin();
742 ours != local_codecs.end(); ++ours) {
743 typename std::vector<C>::const_iterator theirs;
744 for (theirs = offered_codecs.begin();
745 theirs != offered_codecs.end(); ++theirs) {
746 if (ours->Matches(*theirs)) {
747 C negotiated = *ours;
748 negotiated.IntersectFeedbackParams(*theirs);
749 if (IsRtxCodec(negotiated)) {
750 // Only negotiate RTX if kCodecParamAssociatedPayloadType has been
752 std::string apt_value;
753 if (!theirs->GetParam(kCodecParamAssociatedPayloadType, &apt_value)) {
754 LOG(LS_WARNING) << "RTX missing associated payload type.";
757 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_value);
759 negotiated.id = theirs->id;
760 negotiated_codecs->push_back(negotiated);
767 static bool FindMatchingCodec(const std::vector<C>& codecs,
768 const C& codec_to_match,
770 for (typename std::vector<C>::const_iterator it = codecs.begin();
771 it != codecs.end(); ++it) {
772 if (it->Matches(codec_to_match)) {
773 if (found_codec != NULL) {
782 // Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
783 // already exist in |offered_codecs| and ensure the payload types don't
786 static void FindCodecsToOffer(
787 const std::vector<C>& reference_codecs,
788 std::vector<C>* offered_codecs,
789 UsedPayloadTypes* used_pltypes) {
791 typedef std::map<int, C> RtxCodecReferences;
792 RtxCodecReferences new_rtx_codecs;
794 // Find all new RTX codecs.
795 for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
796 it != reference_codecs.end(); ++it) {
797 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && IsRtxCodec(*it)) {
799 int referenced_pl_type =
800 talk_base::FromString<int>(0,
801 rtx_codec.params[kCodecParamAssociatedPayloadType]);
802 new_rtx_codecs.insert(std::pair<int, C>(referenced_pl_type,
807 // Add all new codecs that are not RTX codecs.
808 for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
809 it != reference_codecs.end(); ++it) {
810 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && !IsRtxCodec(*it)) {
812 int original_payload_id = codec.id;
813 used_pltypes->FindAndSetIdUsed(&codec);
814 offered_codecs->push_back(codec);
816 // If this codec is referenced by a new RTX codec, update the reference
817 // in the RTX codec with the new payload type.
818 typename RtxCodecReferences::iterator rtx_it =
819 new_rtx_codecs.find(original_payload_id);
820 if (rtx_it != new_rtx_codecs.end()) {
821 C& rtx_codec = rtx_it->second;
822 rtx_codec.params[kCodecParamAssociatedPayloadType] =
823 talk_base::ToString(codec.id);
828 // Add all new RTX codecs.
829 for (typename RtxCodecReferences::iterator it = new_rtx_codecs.begin();
830 it != new_rtx_codecs.end(); ++it) {
831 C& rtx_codec = it->second;
832 used_pltypes->FindAndSetIdUsed(&rtx_codec);
833 offered_codecs->push_back(rtx_codec);
838 static bool FindByUri(const RtpHeaderExtensions& extensions,
839 const RtpHeaderExtension& ext_to_match,
840 RtpHeaderExtension* found_extension) {
841 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
842 it != extensions.end(); ++it) {
843 // We assume that all URIs are given in a canonical format.
844 if (it->uri == ext_to_match.uri) {
845 if (found_extension != NULL) {
846 *found_extension = *it;
854 static void FindAndSetRtpHdrExtUsed(
855 const RtpHeaderExtensions& reference_extensions,
856 RtpHeaderExtensions* offered_extensions,
857 const RtpHeaderExtensions& other_extensions,
858 UsedRtpHeaderExtensionIds* used_extensions) {
859 for (RtpHeaderExtensions::const_iterator it = reference_extensions.begin();
860 it != reference_extensions.end(); ++it) {
861 if (!FindByUri(*offered_extensions, *it, NULL)) {
862 RtpHeaderExtension ext;
863 if (!FindByUri(other_extensions, *it, &ext)) {
865 used_extensions->FindAndSetIdUsed(&ext);
867 offered_extensions->push_back(ext);
872 static void NegotiateRtpHeaderExtensions(
873 const RtpHeaderExtensions& local_extensions,
874 const RtpHeaderExtensions& offered_extensions,
875 RtpHeaderExtensions* negotiated_extenstions) {
876 RtpHeaderExtensions::const_iterator ours;
877 for (ours = local_extensions.begin();
878 ours != local_extensions.end(); ++ours) {
879 RtpHeaderExtension theirs;
880 if (FindByUri(offered_extensions, *ours, &theirs)) {
881 // We respond with their RTP header extension id.
882 negotiated_extenstions->push_back(theirs);
887 static void StripCNCodecs(AudioCodecs* audio_codecs) {
888 AudioCodecs::iterator iter = audio_codecs->begin();
889 while (iter != audio_codecs->end()) {
890 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
891 iter = audio_codecs->erase(iter);
898 // Create a media content to be answered in a session-accept,
899 // according to the given options.rtcp_mux, options.streams, codecs,
900 // crypto, and streams. If we don't currently have crypto (in
901 // current_cryptos) and it is enabled (in secure_policy), crypto is
902 // created (according to crypto_suites). If add_legacy_stream is
903 // true, and current_streams is empty, a legacy stream is created.
904 // The codecs, rtcp_mux, and crypto are all negotiated with the offer
905 // from the incoming session-initiate. If the negotiation fails, this
906 // method returns false. The created content is added to the offer.
908 static bool CreateMediaContentAnswer(
909 const MediaContentDescriptionImpl<C>* offer,
910 const MediaSessionOptions& options,
911 const std::vector<C>& local_codecs,
912 const SecurePolicy& sdes_policy,
913 const CryptoParamsVec* current_cryptos,
914 const RtpHeaderExtensions& local_rtp_extenstions,
915 StreamParamsVec* current_streams,
916 bool add_legacy_stream,
918 MediaContentDescriptionImpl<C>* answer) {
919 std::vector<C> negotiated_codecs;
920 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
921 answer->AddCodecs(negotiated_codecs);
922 answer->SortCodecs();
923 answer->set_protocol(offer->protocol());
924 RtpHeaderExtensions negotiated_rtp_extensions;
925 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
926 offer->rtp_header_extensions(),
927 &negotiated_rtp_extensions);
928 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
930 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
932 if (sdes_policy != SEC_DISABLED) {
934 if (SelectCrypto(offer, bundle_enabled, &crypto)) {
935 if (current_cryptos) {
936 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
938 answer->AddCrypto(crypto);
942 if (answer->cryptos().empty() &&
943 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
947 if (!AddStreamParams(
948 answer->type(), options.streams, current_streams,
949 answer, add_legacy_stream)) {
950 return false; // Something went seriously wrong.
953 // Make sure the answer media content direction is per default set as
954 // described in RFC3264 section 6.1.
955 switch (offer->direction()) {
957 answer->set_direction(MD_INACTIVE);
960 answer->set_direction(MD_RECVONLY);
963 answer->set_direction(MD_SENDONLY);
966 answer->set_direction(MD_SENDRECV);
975 static bool IsMediaProtocolSupported(MediaType type,
976 const std::string& protocol) {
977 // Data channels can have a protocol of SCTP or SCTP/DTLS.
978 if (type == MEDIA_TYPE_DATA &&
979 (protocol == kMediaProtocolSctp ||
980 protocol == kMediaProtocolDtlsSctp)) {
983 // Since not all applications serialize and deserialize the media protocol,
984 // we will have to accept |protocol| to be empty.
985 return protocol == kMediaProtocolAvpf || protocol == kMediaProtocolSavpf ||
989 static void SetMediaProtocol(bool secure_transport,
990 MediaContentDescription* desc) {
991 if (!desc->cryptos().empty() || secure_transport)
992 desc->set_protocol(kMediaProtocolSavpf);
994 desc->set_protocol(kMediaProtocolAvpf);
997 // Gets the TransportInfo of the given |content_name| from the
998 // |current_description|. If doesn't exist, returns a new one.
999 static const TransportDescription* GetTransportDescription(
1000 const std::string& content_name,
1001 const SessionDescription* current_description) {
1002 const TransportDescription* desc = NULL;
1003 if (current_description) {
1004 const TransportInfo* info =
1005 current_description->GetTransportInfoByName(content_name);
1007 desc = &info->description;
1013 // Gets the current DTLS state from the transport description.
1014 static bool IsDtlsActive(
1015 const std::string& content_name,
1016 const SessionDescription* current_description) {
1017 if (!current_description)
1020 const ContentInfo* content =
1021 current_description->GetContentByName(content_name);
1025 const TransportDescription* current_tdesc =
1026 GetTransportDescription(content_name, current_description);
1030 return current_tdesc->secure();
1033 std::string MediaTypeToString(MediaType type) {
1034 std::string type_str;
1036 case MEDIA_TYPE_AUDIO:
1039 case MEDIA_TYPE_VIDEO:
1042 case MEDIA_TYPE_DATA:
1052 void MediaSessionOptions::AddStream(MediaType type,
1053 const std::string& id,
1054 const std::string& sync_label) {
1055 AddStreamInternal(type, id, sync_label, 1);
1058 void MediaSessionOptions::AddVideoStream(
1059 const std::string& id,
1060 const std::string& sync_label,
1061 int num_sim_layers) {
1062 AddStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
1065 void MediaSessionOptions::AddStreamInternal(
1067 const std::string& id,
1068 const std::string& sync_label,
1069 int num_sim_layers) {
1070 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
1072 if (type == MEDIA_TYPE_VIDEO)
1074 else if (type == MEDIA_TYPE_AUDIO)
1076 // If we haven't already set the data_channel_type, and we add a
1077 // stream, we assume it's an RTP data stream.
1078 else if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
1079 data_channel_type = DCT_RTP;
1082 void MediaSessionOptions::RemoveStream(MediaType type,
1083 const std::string& id) {
1084 Streams::iterator stream_it = streams.begin();
1085 for (; stream_it != streams.end(); ++stream_it) {
1086 if (stream_it->type == type && stream_it->id == id) {
1087 streams.erase(stream_it);
1094 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1095 const TransportDescriptionFactory* transport_desc_factory)
1096 : secure_(SEC_DISABLED),
1098 transport_desc_factory_(transport_desc_factory) {
1101 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1102 ChannelManager* channel_manager,
1103 const TransportDescriptionFactory* transport_desc_factory)
1104 : secure_(SEC_DISABLED),
1106 transport_desc_factory_(transport_desc_factory) {
1107 channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
1108 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1109 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1110 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1111 channel_manager->GetSupportedDataCodecs(&data_codecs_);
1114 SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1115 const MediaSessionOptions& options,
1116 const SessionDescription* current_description) const {
1117 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1119 scoped_ptr<SessionDescription> offer(new SessionDescription());
1121 StreamParamsVec current_streams;
1122 GetCurrentStreamParams(current_description, ¤t_streams);
1124 AudioCodecs audio_codecs;
1125 VideoCodecs video_codecs;
1126 DataCodecs data_codecs;
1127 GetCodecsToOffer(current_description, &audio_codecs, &video_codecs,
1130 if (!options.vad_enabled) {
1131 // If application doesn't want CN codecs in offer.
1132 StripCNCodecs(&audio_codecs);
1135 RtpHeaderExtensions audio_rtp_extensions;
1136 RtpHeaderExtensions video_rtp_extensions;
1137 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1138 &video_rtp_extensions);
1141 if (options.has_audio) {
1142 cricket::SecurePolicy sdes_policy =
1143 IsDtlsActive(CN_AUDIO, current_description) ?
1144 cricket::SEC_DISABLED : secure();
1146 scoped_ptr<AudioContentDescription> audio(new AudioContentDescription());
1147 std::vector<std::string> crypto_suites;
1148 GetSupportedAudioCryptoSuites(&crypto_suites);
1149 if (!CreateMediaContentOffer(
1153 GetCryptos(GetFirstAudioContentDescription(current_description)),
1155 audio_rtp_extensions,
1162 audio->set_lang(lang_);
1163 SetMediaProtocol(secure_transport, audio.get());
1164 offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio.release());
1165 if (!AddTransportOffer(CN_AUDIO, options.transport_options,
1166 current_description, offer.get())) {
1172 if (options.has_video) {
1173 cricket::SecurePolicy sdes_policy =
1174 IsDtlsActive(CN_VIDEO, current_description) ?
1175 cricket::SEC_DISABLED : secure();
1177 scoped_ptr<VideoContentDescription> video(new VideoContentDescription());
1178 std::vector<std::string> crypto_suites;
1179 GetSupportedVideoCryptoSuites(&crypto_suites);
1180 if (!CreateMediaContentOffer(
1184 GetCryptos(GetFirstVideoContentDescription(current_description)),
1186 video_rtp_extensions,
1193 video->set_bandwidth(options.video_bandwidth);
1194 SetMediaProtocol(secure_transport, video.get());
1195 offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video.release());
1196 if (!AddTransportOffer(CN_VIDEO, options.transport_options,
1197 current_description, offer.get())) {
1203 if (options.has_data()) {
1204 scoped_ptr<DataContentDescription> data(new DataContentDescription());
1205 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1207 cricket::SecurePolicy sdes_policy =
1208 IsDtlsActive(CN_DATA, current_description) ?
1209 cricket::SEC_DISABLED : secure();
1210 std::vector<std::string> crypto_suites;
1212 // SDES doesn't make sense for SCTP, so we disable it, and we only
1213 // get SDES crypto suites for RTP-based data channels.
1214 sdes_policy = cricket::SEC_DISABLED;
1215 // Unlike SetMediaProtocol below, we need to set the protocol
1216 // before we call CreateMediaContentOffer. Otherwise,
1217 // CreateMediaContentOffer won't know this is SCTP and will
1218 // generate SSRCs rather than SIDs.
1220 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1222 GetSupportedDataCryptoSuites(&crypto_suites);
1225 if (!CreateMediaContentOffer(
1229 GetCryptos(GetFirstDataContentDescription(current_description)),
1231 RtpHeaderExtensions(),
1239 offer->AddContent(CN_DATA, NS_JINGLE_DRAFT_SCTP, data.release());
1241 data->set_bandwidth(options.data_bandwidth);
1242 SetMediaProtocol(secure_transport, data.get());
1243 offer->AddContent(CN_DATA, NS_JINGLE_RTP, data.release());
1245 if (!AddTransportOffer(CN_DATA, options.transport_options,
1246 current_description, offer.get())) {
1251 // Bundle the contents together, if we've been asked to do so, and update any
1252 // parameters that need to be tweaked for BUNDLE.
1253 if (options.bundle_enabled) {
1254 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1255 for (ContentInfos::const_iterator content = offer->contents().begin();
1256 content != offer->contents().end(); ++content) {
1257 offer_bundle.AddContentName(content->name);
1259 offer->AddGroup(offer_bundle);
1260 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1261 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1264 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1265 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1270 return offer.release();
1273 SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1274 const SessionDescription* offer, const MediaSessionOptions& options,
1275 const SessionDescription* current_description) const {
1276 // The answer contains the intersection of the codecs in the offer with the
1277 // codecs we support, ordered by our local preference. As indicated by
1278 // XEP-0167, we retain the same payload ids from the offer in the answer.
1279 scoped_ptr<SessionDescription> answer(new SessionDescription());
1281 StreamParamsVec current_streams;
1282 GetCurrentStreamParams(current_description, ¤t_streams);
1284 bool bundle_enabled =
1285 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1288 const ContentInfo* audio_content = GetFirstAudioContent(offer);
1289 if (audio_content) {
1290 scoped_ptr<TransportDescription> audio_transport(
1291 CreateTransportAnswer(audio_content->name, offer,
1292 options.transport_options,
1293 current_description));
1294 if (!audio_transport) {
1298 AudioCodecs audio_codecs = audio_codecs_;
1299 if (!options.vad_enabled) {
1300 StripCNCodecs(&audio_codecs);
1303 scoped_ptr<AudioContentDescription> audio_answer(
1304 new AudioContentDescription());
1305 // Do not require or create SDES cryptos if DTLS is used.
1306 cricket::SecurePolicy sdes_policy =
1307 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1308 if (!CreateMediaContentAnswer(
1309 static_cast<const AudioContentDescription*>(
1310 audio_content->description),
1314 GetCryptos(GetFirstAudioContentDescription(current_description)),
1315 audio_rtp_extensions_,
1319 audio_answer.get())) {
1320 return NULL; // Fails the session setup.
1323 bool rejected = !options.has_audio || audio_content->rejected ||
1324 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1325 audio_answer->protocol());
1327 AddTransportAnswer(audio_content->name, *(audio_transport.get()),
1331 // The answer MUST contain the same number of m-lines as the offer.
1332 LOG(LS_INFO) << "Audio is not supported in the answer.";
1335 answer->AddContent(audio_content->name, audio_content->type, rejected,
1336 audio_answer.release());
1338 LOG(LS_INFO) << "Audio is not available in the offer.";
1342 const ContentInfo* video_content = GetFirstVideoContent(offer);
1343 if (video_content) {
1344 scoped_ptr<TransportDescription> video_transport(
1345 CreateTransportAnswer(video_content->name, offer,
1346 options.transport_options,
1347 current_description));
1348 if (!video_transport) {
1352 scoped_ptr<VideoContentDescription> video_answer(
1353 new VideoContentDescription());
1354 // Do not require or create SDES cryptos if DTLS is used.
1355 cricket::SecurePolicy sdes_policy =
1356 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1357 if (!CreateMediaContentAnswer(
1358 static_cast<const VideoContentDescription*>(
1359 video_content->description),
1363 GetCryptos(GetFirstVideoContentDescription(current_description)),
1364 video_rtp_extensions_,
1368 video_answer.get())) {
1371 bool rejected = !options.has_video || video_content->rejected ||
1372 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, video_answer->protocol());
1374 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1378 video_answer->set_bandwidth(options.video_bandwidth);
1381 // The answer MUST contain the same number of m-lines as the offer.
1382 LOG(LS_INFO) << "Video is not supported in the answer.";
1384 answer->AddContent(video_content->name, video_content->type, rejected,
1385 video_answer.release());
1387 LOG(LS_INFO) << "Video is not available in the offer.";
1391 const ContentInfo* data_content = GetFirstDataContent(offer);
1393 scoped_ptr<TransportDescription> data_transport(
1394 CreateTransportAnswer(data_content->name, offer,
1395 options.transport_options,
1396 current_description));
1397 if (!data_transport) {
1400 scoped_ptr<DataContentDescription> data_answer(
1401 new DataContentDescription());
1402 // Do not require or create SDES cryptos if DTLS is used.
1403 cricket::SecurePolicy sdes_policy =
1404 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1405 if (!CreateMediaContentAnswer(
1406 static_cast<const DataContentDescription*>(
1407 data_content->description),
1411 GetCryptos(GetFirstDataContentDescription(current_description)),
1412 RtpHeaderExtensions(),
1416 data_answer.get())) {
1417 return NULL; // Fails the session setup.
1420 bool rejected = !options.has_data() || data_content->rejected ||
1421 !IsMediaProtocolSupported(MEDIA_TYPE_DATA, data_answer->protocol());
1423 data_answer->set_bandwidth(options.data_bandwidth);
1424 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1430 // The answer MUST contain the same number of m-lines as the offer.
1431 LOG(LS_INFO) << "Data is not supported in the answer.";
1433 answer->AddContent(data_content->name, data_content->type, rejected,
1434 data_answer.release());
1436 LOG(LS_INFO) << "Data is not available in the offer.";
1439 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1440 // group in the answer with the appropriate content names.
1441 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1442 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1443 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1444 for (ContentInfos::const_iterator content = answer->contents().begin();
1445 content != answer->contents().end(); ++content) {
1446 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1447 answer_bundle.AddContentName(content->name);
1450 if (answer_bundle.FirstContentName()) {
1451 answer->AddGroup(answer_bundle);
1453 // Share the same ICE credentials and crypto params across all contents,
1454 // as BUNDLE requires.
1455 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1456 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1460 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1461 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1467 return answer.release();
1470 void MediaSessionDescriptionFactory::GetCodecsToOffer(
1471 const SessionDescription* current_description,
1472 AudioCodecs* audio_codecs,
1473 VideoCodecs* video_codecs,
1474 DataCodecs* data_codecs) const {
1475 UsedPayloadTypes used_pltypes;
1476 audio_codecs->clear();
1477 video_codecs->clear();
1478 data_codecs->clear();
1481 // First - get all codecs from the current description if the media type
1483 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1485 if (current_description) {
1486 const AudioContentDescription* audio =
1487 GetFirstAudioContentDescription(current_description);
1489 *audio_codecs = audio->codecs();
1490 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1492 const VideoContentDescription* video =
1493 GetFirstVideoContentDescription(current_description);
1495 *video_codecs = video->codecs();
1496 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1498 const DataContentDescription* data =
1499 GetFirstDataContentDescription(current_description);
1501 *data_codecs = data->codecs();
1502 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1506 // Add our codecs that are not in |current_description|.
1507 FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes);
1508 FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1509 FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1512 void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1513 const SessionDescription* current_description,
1514 RtpHeaderExtensions* audio_extensions,
1515 RtpHeaderExtensions* video_extensions) const {
1516 // All header extensions allocated from the same range to avoid potential
1517 // issues when using BUNDLE.
1518 UsedRtpHeaderExtensionIds used_ids;
1519 audio_extensions->clear();
1520 video_extensions->clear();
1522 // First - get all extensions from the current description if the media type
1524 // Add them to |used_ids| so the local ids are not reused if a new media
1526 if (current_description) {
1527 const AudioContentDescription* audio =
1528 GetFirstAudioContentDescription(current_description);
1530 *audio_extensions = audio->rtp_header_extensions();
1531 used_ids.FindAndSetIdUsed(audio_extensions);
1533 const VideoContentDescription* video =
1534 GetFirstVideoContentDescription(current_description);
1536 *video_extensions = video->rtp_header_extensions();
1537 used_ids.FindAndSetIdUsed(video_extensions);
1541 // Add our default RTP header extensions that are not in
1542 // |current_description|.
1543 FindAndSetRtpHdrExtUsed(audio_rtp_header_extensions(), audio_extensions,
1544 *video_extensions, &used_ids);
1545 FindAndSetRtpHdrExtUsed(video_rtp_header_extensions(), video_extensions,
1546 *audio_extensions, &used_ids);
1549 bool MediaSessionDescriptionFactory::AddTransportOffer(
1550 const std::string& content_name,
1551 const TransportOptions& transport_options,
1552 const SessionDescription* current_desc,
1553 SessionDescription* offer_desc) const {
1554 if (!transport_desc_factory_)
1556 const TransportDescription* current_tdesc =
1557 GetTransportDescription(content_name, current_desc);
1558 talk_base::scoped_ptr<TransportDescription> new_tdesc(
1559 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1560 bool ret = (new_tdesc.get() != NULL &&
1561 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1564 << "Failed to AddTransportOffer, content name=" << content_name;
1569 TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1570 const std::string& content_name,
1571 const SessionDescription* offer_desc,
1572 const TransportOptions& transport_options,
1573 const SessionDescription* current_desc) const {
1574 if (!transport_desc_factory_)
1576 const TransportDescription* offer_tdesc =
1577 GetTransportDescription(content_name, offer_desc);
1578 const TransportDescription* current_tdesc =
1579 GetTransportDescription(content_name, current_desc);
1581 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1585 bool MediaSessionDescriptionFactory::AddTransportAnswer(
1586 const std::string& content_name,
1587 const TransportDescription& transport_desc,
1588 SessionDescription* answer_desc) const {
1589 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1592 << "Failed to AddTransportAnswer, content name=" << content_name;
1598 bool IsMediaContent(const ContentInfo* content) {
1600 (content->type == NS_JINGLE_RTP ||
1601 content->type == NS_JINGLE_DRAFT_SCTP));
1604 bool IsAudioContent(const ContentInfo* content) {
1605 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
1608 bool IsVideoContent(const ContentInfo* content) {
1609 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
1612 bool IsDataContent(const ContentInfo* content) {
1613 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
1616 static const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
1617 MediaType media_type) {
1618 for (ContentInfos::const_iterator content = contents.begin();
1619 content != contents.end(); content++) {
1620 if (IsMediaContentOfType(&*content, media_type)) {
1627 const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
1628 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
1631 const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
1632 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
1635 const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
1636 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
1639 static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
1640 MediaType media_type) {
1644 return GetFirstMediaContent(sdesc->contents(), media_type);
1647 const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
1648 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
1651 const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
1652 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
1655 const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
1656 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
1659 const MediaContentDescription* GetFirstMediaContentDescription(
1660 const SessionDescription* sdesc, MediaType media_type) {
1661 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
1662 const ContentDescription* description = content ? content->description : NULL;
1663 return static_cast<const MediaContentDescription*>(description);
1666 const AudioContentDescription* GetFirstAudioContentDescription(
1667 const SessionDescription* sdesc) {
1668 return static_cast<const AudioContentDescription*>(
1669 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
1672 const VideoContentDescription* GetFirstVideoContentDescription(
1673 const SessionDescription* sdesc) {
1674 return static_cast<const VideoContentDescription*>(
1675 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
1678 const DataContentDescription* GetFirstDataContentDescription(
1679 const SessionDescription* sdesc) {
1680 return static_cast<const DataContentDescription*>(
1681 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
1684 bool GetMediaChannelNameFromComponent(
1685 int component, MediaType media_type, std::string* channel_name) {
1686 if (media_type == MEDIA_TYPE_AUDIO) {
1687 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1688 *channel_name = GICE_CHANNEL_NAME_RTP;
1690 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1691 *channel_name = GICE_CHANNEL_NAME_RTCP;
1694 } else if (media_type == MEDIA_TYPE_VIDEO) {
1695 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1696 *channel_name = GICE_CHANNEL_NAME_VIDEO_RTP;
1698 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1699 *channel_name = GICE_CHANNEL_NAME_VIDEO_RTCP;
1702 } else if (media_type == MEDIA_TYPE_DATA) {
1703 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1704 *channel_name = GICE_CHANNEL_NAME_DATA_RTP;
1706 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1707 *channel_name = GICE_CHANNEL_NAME_DATA_RTCP;
1715 bool GetMediaComponentFromChannelName(
1716 const std::string& channel_name, int* component) {
1717 if (channel_name == GICE_CHANNEL_NAME_RTP ||
1718 channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
1719 channel_name == GICE_CHANNEL_NAME_DATA_RTP) {
1720 *component = ICE_CANDIDATE_COMPONENT_RTP;
1722 } else if (channel_name == GICE_CHANNEL_NAME_RTCP ||
1723 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP ||
1724 channel_name == GICE_CHANNEL_NAME_DATA_RTP) {
1725 *component = ICE_CANDIDATE_COMPONENT_RTCP;
1732 bool GetMediaTypeFromChannelName(
1733 const std::string& channel_name, MediaType* media_type) {
1734 if (channel_name == GICE_CHANNEL_NAME_RTP ||
1735 channel_name == GICE_CHANNEL_NAME_RTCP) {
1736 *media_type = MEDIA_TYPE_AUDIO;
1738 } else if (channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
1739 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP) {
1740 *media_type = MEDIA_TYPE_VIDEO;
1742 } else if (channel_name == GICE_CHANNEL_NAME_DATA_RTP ||
1743 channel_name == GICE_CHANNEL_NAME_DATA_RTCP) {
1744 *media_type = MEDIA_TYPE_DATA;
1751 } // namespace cricket