| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2004 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2004 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include "webrtc/pc/mediasession.h" | 11 #include "webrtc/pc/mediasession.h" |
| 12 | 12 |
| 13 #include <algorithm> // For std::find_if, std::sort. | 13 #include <algorithm> // For std::find_if, std::sort. |
| 14 #include <functional> | 14 #include <functional> |
| 15 #include <map> | 15 #include <map> |
| 16 #include <memory> | 16 #include <memory> |
| 17 #include <set> | 17 #include <set> |
| 18 #include <unordered_map> | 18 #include <unordered_map> |
| 19 #include <utility> | 19 #include <utility> |
| 20 | 20 |
| 21 #include "webrtc/base/base64.h" |
| 21 #include "webrtc/base/helpers.h" | 22 #include "webrtc/base/helpers.h" |
| 22 #include "webrtc/base/logging.h" | 23 #include "webrtc/base/logging.h" |
| 23 #include "webrtc/base/stringutils.h" | 24 #include "webrtc/base/stringutils.h" |
| 24 #include "webrtc/media/base/cryptoparams.h" | 25 #include "webrtc/media/base/cryptoparams.h" |
| 25 #include "webrtc/media/base/mediaconstants.h" | 26 #include "webrtc/media/base/mediaconstants.h" |
| 26 #include "webrtc/p2p/base/p2pconstants.h" | 27 #include "webrtc/p2p/base/p2pconstants.h" |
| 27 #include "webrtc/pc/channelmanager.h" | 28 #include "webrtc/pc/channelmanager.h" |
| 28 #include "webrtc/pc/srtpfilter.h" | 29 #include "webrtc/pc/srtpfilter.h" |
| 29 | 30 |
| 30 #ifdef HAVE_SCTP | 31 #ifdef HAVE_SCTP |
| 31 #include "webrtc/media/sctp/sctpdataengine.h" | 32 #include "webrtc/media/sctp/sctpdataengine.h" |
| 32 #else | 33 #else |
| 33 static const uint32_t kMaxSctpSid = 1023; | 34 static const uint32_t kMaxSctpSid = 1023; |
| 34 #endif | 35 #endif |
| 35 | 36 |
| 36 namespace { | 37 namespace { |
| 37 const char kInline[] = "inline:"; | 38 const char kInline[] = "inline:"; |
| 38 | 39 |
| 39 void GetSupportedCryptoSuiteNames(void (*func)(std::vector<int>*), | 40 void GetSupportedCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&, |
| 41 std::vector<int>*), |
| 42 const rtc::CryptoOptions& crypto_options, |
| 40 std::vector<std::string>* names) { | 43 std::vector<std::string>* names) { |
| 41 #ifdef HAVE_SRTP | 44 #ifdef HAVE_SRTP |
| 42 std::vector<int> crypto_suites; | 45 std::vector<int> crypto_suites; |
| 43 func(&crypto_suites); | 46 func(crypto_options, &crypto_suites); |
| 44 for (const auto crypto : crypto_suites) { | 47 for (const auto crypto : crypto_suites) { |
| 45 names->push_back(rtc::SrtpCryptoSuiteToName(crypto)); | 48 names->push_back(rtc::SrtpCryptoSuiteToName(crypto)); |
| 46 } | 49 } |
| 47 #endif | 50 #endif |
| 48 } | 51 } |
| 49 } // namespace | 52 } // namespace |
| 50 | 53 |
| 51 namespace cricket { | 54 namespace cricket { |
| 52 | 55 |
| 53 // RTP Profile names | 56 // RTP Profile names |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 100 return false; | 103 return false; |
| 101 } | 104 } |
| 102 | 105 |
| 103 const MediaContentDescription* mdesc = | 106 const MediaContentDescription* mdesc = |
| 104 static_cast<const MediaContentDescription*>(content->description); | 107 static_cast<const MediaContentDescription*>(content->description); |
| 105 return mdesc && mdesc->type() == media_type; | 108 return mdesc && mdesc->type() == media_type; |
| 106 } | 109 } |
| 107 | 110 |
| 108 static bool CreateCryptoParams(int tag, const std::string& cipher, | 111 static bool CreateCryptoParams(int tag, const std::string& cipher, |
| 109 CryptoParams *out) { | 112 CryptoParams *out) { |
| 110 std::string key; | 113 int key_len; |
| 111 key.reserve(SRTP_MASTER_KEY_BASE64_LEN); | 114 int salt_len; |
| 112 | 115 if (!rtc::GetSrtpKeyAndSaltLengths( |
| 113 if (!rtc::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) { | 116 rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) { |
| 114 return false; | 117 return false; |
| 115 } | 118 } |
| 119 |
| 120 int master_key_len = key_len + salt_len; |
| 121 std::string master_key; |
| 122 if (!rtc::CreateRandomData(master_key_len, &master_key)) { |
| 123 return false; |
| 124 } |
| 125 |
| 126 RTC_CHECK_EQ(static_cast<size_t>(master_key_len), master_key.size()); |
| 127 std::string key = rtc::Base64::Encode(master_key); |
| 128 |
| 116 out->tag = tag; | 129 out->tag = tag; |
| 117 out->cipher_suite = cipher; | 130 out->cipher_suite = cipher; |
| 118 out->key_params = kInline; | 131 out->key_params = kInline; |
| 119 out->key_params += key; | 132 out->key_params += key; |
| 120 return true; | 133 return true; |
| 121 } | 134 } |
| 122 | 135 |
| 123 #ifdef HAVE_SRTP | 136 #ifdef HAVE_SRTP |
| 124 static bool AddCryptoParams(const std::string& cipher_suite, | 137 static bool AddCryptoParams(const std::string& cipher_suite, |
| 125 CryptoParamsVec *out) { | 138 CryptoParamsVec *out) { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 for (CryptoParamsVec::const_iterator it = cryptos.begin(); | 177 for (CryptoParamsVec::const_iterator it = cryptos.begin(); |
| 165 it != cryptos.end(); ++it) { | 178 it != cryptos.end(); ++it) { |
| 166 if (crypto.Matches(*it)) { | 179 if (crypto.Matches(*it)) { |
| 167 *out = *it; | 180 *out = *it; |
| 168 return true; | 181 return true; |
| 169 } | 182 } |
| 170 } | 183 } |
| 171 return false; | 184 return false; |
| 172 } | 185 } |
| 173 | 186 |
| 174 // For audio, HMAC 32 is prefered because of the low overhead. | 187 // For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead. |
| 175 void GetSupportedAudioCryptoSuites(std::vector<int>* crypto_suites) { | 188 void GetSupportedAudioCryptoSuites(const rtc::CryptoOptions& crypto_options, |
| 189 std::vector<int>* crypto_suites) { |
| 176 #ifdef HAVE_SRTP | 190 #ifdef HAVE_SRTP |
| 191 if (crypto_options.enable_gcm_crypto_suites) { |
| 192 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM); |
| 193 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM); |
| 194 } |
| 177 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32); | 195 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32); |
| 178 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80); | 196 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80); |
| 179 #endif | 197 #endif |
| 180 } | 198 } |
| 181 | 199 |
| 182 void GetSupportedAudioCryptoSuiteNames( | 200 void GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions& crypto_options, |
| 183 std::vector<std::string>* crypto_suite_names) { | 201 std::vector<std::string>* crypto_suite_names) { |
| 184 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites, | 202 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites, |
| 185 crypto_suite_names); | 203 crypto_options, crypto_suite_names); |
| 186 } | 204 } |
| 187 | 205 |
| 188 void GetSupportedVideoCryptoSuites(std::vector<int>* crypto_suites) { | 206 void GetSupportedVideoCryptoSuites(const rtc::CryptoOptions& crypto_options, |
| 189 GetDefaultSrtpCryptoSuites(crypto_suites); | 207 std::vector<int>* crypto_suites) { |
| 208 GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites); |
| 190 } | 209 } |
| 191 | 210 |
| 192 void GetSupportedVideoCryptoSuiteNames( | 211 void GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions& crypto_options, |
| 193 std::vector<std::string>* crypto_suite_names) { | 212 std::vector<std::string>* crypto_suite_names) { |
| 194 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites, | 213 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites, |
| 195 crypto_suite_names); | 214 crypto_options, crypto_suite_names); |
| 196 } | 215 } |
| 197 | 216 |
| 198 void GetSupportedDataCryptoSuites(std::vector<int>* crypto_suites) { | 217 void GetSupportedDataCryptoSuites(const rtc::CryptoOptions& crypto_options, |
| 199 GetDefaultSrtpCryptoSuites(crypto_suites); | 218 std::vector<int>* crypto_suites) { |
| 219 GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites); |
| 200 } | 220 } |
| 201 | 221 |
| 202 void GetSupportedDataCryptoSuiteNames( | 222 void GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions& crypto_options, |
| 203 std::vector<std::string>* crypto_suite_names) { | 223 std::vector<std::string>* crypto_suite_names) { |
| 204 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites, | 224 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites, |
| 205 crypto_suite_names); | 225 crypto_options, crypto_suite_names); |
| 206 } | 226 } |
| 207 | 227 |
| 208 void GetDefaultSrtpCryptoSuites(std::vector<int>* crypto_suites) { | 228 void GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions& crypto_options, |
| 229 std::vector<int>* crypto_suites) { |
| 209 #ifdef HAVE_SRTP | 230 #ifdef HAVE_SRTP |
| 231 if (crypto_options.enable_gcm_crypto_suites) { |
| 232 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM); |
| 233 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM); |
| 234 } |
| 210 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80); | 235 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80); |
| 211 #endif | 236 #endif |
| 212 } | 237 } |
| 213 | 238 |
| 214 void GetDefaultSrtpCryptoSuiteNames( | 239 void GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions& crypto_options, |
| 215 std::vector<std::string>* crypto_suite_names) { | 240 std::vector<std::string>* crypto_suite_names) { |
| 216 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites, crypto_suite_names); | 241 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites, |
| 242 crypto_options, crypto_suite_names); |
| 217 } | 243 } |
| 218 | 244 |
| 219 // For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is | 245 // Support any GCM cipher (if enabled through options). For video support only |
| 220 // tolerated unless bundle is enabled because it is low overhead. Pick the | 246 // 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled |
| 221 // crypto in the list that is supported. | 247 // because it is low overhead. |
| 248 // Pick the crypto in the list that is supported. |
| 222 static bool SelectCrypto(const MediaContentDescription* offer, | 249 static bool SelectCrypto(const MediaContentDescription* offer, |
| 223 bool bundle, | 250 bool bundle, |
| 251 const rtc::CryptoOptions& crypto_options, |
| 224 CryptoParams *crypto) { | 252 CryptoParams *crypto) { |
| 225 bool audio = offer->type() == MEDIA_TYPE_AUDIO; | 253 bool audio = offer->type() == MEDIA_TYPE_AUDIO; |
| 226 const CryptoParamsVec& cryptos = offer->cryptos(); | 254 const CryptoParamsVec& cryptos = offer->cryptos(); |
| 227 | 255 |
| 228 for (CryptoParamsVec::const_iterator i = cryptos.begin(); | 256 for (CryptoParamsVec::const_iterator i = cryptos.begin(); |
| 229 i != cryptos.end(); ++i) { | 257 i != cryptos.end(); ++i) { |
| 230 if (rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite || | 258 if ((crypto_options.enable_gcm_crypto_suites && |
| 259 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) || |
| 260 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite || |
| 231 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio && | 261 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio && |
| 232 !bundle)) { | 262 !bundle)) { |
| 233 return CreateCryptoParams(i->tag, i->cipher_suite, crypto); | 263 return CreateCryptoParams(i->tag, i->cipher_suite, crypto); |
| 234 } | 264 } |
| 235 } | 265 } |
| 236 return false; | 266 return false; |
| 237 } | 267 } |
| 238 | 268 |
| 239 // Generate random SSRC values that are not already present in |params_vec|. | 269 // Generate random SSRC values that are not already present in |params_vec|. |
| 240 // The generated values are added to |ssrcs|. | 270 // The generated values are added to |ssrcs|. |
| (...skipping 786 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1027 &negotiated_rtp_extensions); | 1057 &negotiated_rtp_extensions); |
| 1028 answer->set_rtp_header_extensions(negotiated_rtp_extensions); | 1058 answer->set_rtp_header_extensions(negotiated_rtp_extensions); |
| 1029 | 1059 |
| 1030 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux()); | 1060 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux()); |
| 1031 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) { | 1061 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) { |
| 1032 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size()); | 1062 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size()); |
| 1033 } | 1063 } |
| 1034 | 1064 |
| 1035 if (sdes_policy != SEC_DISABLED) { | 1065 if (sdes_policy != SEC_DISABLED) { |
| 1036 CryptoParams crypto; | 1066 CryptoParams crypto; |
| 1037 if (SelectCrypto(offer, bundle_enabled, &crypto)) { | 1067 if (SelectCrypto(offer, bundle_enabled, options.crypto_options, &crypto)) { |
| 1038 if (current_cryptos) { | 1068 if (current_cryptos) { |
| 1039 FindMatchingCrypto(*current_cryptos, crypto, &crypto); | 1069 FindMatchingCrypto(*current_cryptos, crypto, &crypto); |
| 1040 } | 1070 } |
| 1041 answer->AddCrypto(crypto); | 1071 answer->AddCrypto(crypto); |
| 1042 } | 1072 } |
| 1043 } | 1073 } |
| 1044 | 1074 |
| 1045 if (answer->cryptos().empty() && | 1075 if (answer->cryptos().empty() && |
| 1046 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) { | 1076 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) { |
| 1047 return false; | 1077 return false; |
| (...skipping 617 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1665 GetFirstAudioContent(current_description); | 1695 GetFirstAudioContent(current_description); |
| 1666 std::string content_name = | 1696 std::string content_name = |
| 1667 current_audio_content ? current_audio_content->name : CN_AUDIO; | 1697 current_audio_content ? current_audio_content->name : CN_AUDIO; |
| 1668 | 1698 |
| 1669 cricket::SecurePolicy sdes_policy = | 1699 cricket::SecurePolicy sdes_policy = |
| 1670 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED | 1700 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED |
| 1671 : secure(); | 1701 : secure(); |
| 1672 | 1702 |
| 1673 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription()); | 1703 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription()); |
| 1674 std::vector<std::string> crypto_suites; | 1704 std::vector<std::string> crypto_suites; |
| 1675 GetSupportedAudioCryptoSuiteNames(&crypto_suites); | 1705 GetSupportedAudioCryptoSuiteNames(options.crypto_options, &crypto_suites); |
| 1676 if (!CreateMediaContentOffer( | 1706 if (!CreateMediaContentOffer( |
| 1677 options, | 1707 options, |
| 1678 audio_codecs, | 1708 audio_codecs, |
| 1679 sdes_policy, | 1709 sdes_policy, |
| 1680 GetCryptos(GetFirstAudioContentDescription(current_description)), | 1710 GetCryptos(GetFirstAudioContentDescription(current_description)), |
| 1681 crypto_suites, | 1711 crypto_suites, |
| 1682 audio_rtp_extensions, | 1712 audio_rtp_extensions, |
| 1683 add_legacy_, | 1713 add_legacy_, |
| 1684 current_streams, | 1714 current_streams, |
| 1685 audio.get())) { | 1715 audio.get())) { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1715 GetFirstVideoContent(current_description); | 1745 GetFirstVideoContent(current_description); |
| 1716 std::string content_name = | 1746 std::string content_name = |
| 1717 current_video_content ? current_video_content->name : CN_VIDEO; | 1747 current_video_content ? current_video_content->name : CN_VIDEO; |
| 1718 | 1748 |
| 1719 cricket::SecurePolicy sdes_policy = | 1749 cricket::SecurePolicy sdes_policy = |
| 1720 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED | 1750 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED |
| 1721 : secure(); | 1751 : secure(); |
| 1722 | 1752 |
| 1723 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription()); | 1753 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription()); |
| 1724 std::vector<std::string> crypto_suites; | 1754 std::vector<std::string> crypto_suites; |
| 1725 GetSupportedVideoCryptoSuiteNames(&crypto_suites); | 1755 GetSupportedVideoCryptoSuiteNames(options.crypto_options, &crypto_suites); |
| 1726 if (!CreateMediaContentOffer( | 1756 if (!CreateMediaContentOffer( |
| 1727 options, | 1757 options, |
| 1728 video_codecs, | 1758 video_codecs, |
| 1729 sdes_policy, | 1759 sdes_policy, |
| 1730 GetCryptos(GetFirstVideoContentDescription(current_description)), | 1760 GetCryptos(GetFirstVideoContentDescription(current_description)), |
| 1731 crypto_suites, | 1761 crypto_suites, |
| 1732 video_rtp_extensions, | 1762 video_rtp_extensions, |
| 1733 add_legacy_, | 1763 add_legacy_, |
| 1734 current_streams, | 1764 current_streams, |
| 1735 video.get())) { | 1765 video.get())) { |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1791 // SDES doesn't make sense for SCTP, so we disable it, and we only | 1821 // SDES doesn't make sense for SCTP, so we disable it, and we only |
| 1792 // get SDES crypto suites for RTP-based data channels. | 1822 // get SDES crypto suites for RTP-based data channels. |
| 1793 sdes_policy = cricket::SEC_DISABLED; | 1823 sdes_policy = cricket::SEC_DISABLED; |
| 1794 // Unlike SetMediaProtocol below, we need to set the protocol | 1824 // Unlike SetMediaProtocol below, we need to set the protocol |
| 1795 // before we call CreateMediaContentOffer. Otherwise, | 1825 // before we call CreateMediaContentOffer. Otherwise, |
| 1796 // CreateMediaContentOffer won't know this is SCTP and will | 1826 // CreateMediaContentOffer won't know this is SCTP and will |
| 1797 // generate SSRCs rather than SIDs. | 1827 // generate SSRCs rather than SIDs. |
| 1798 data->set_protocol( | 1828 data->set_protocol( |
| 1799 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp); | 1829 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp); |
| 1800 } else { | 1830 } else { |
| 1801 GetSupportedDataCryptoSuiteNames(&crypto_suites); | 1831 GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites); |
| 1802 } | 1832 } |
| 1803 | 1833 |
| 1804 if (!CreateMediaContentOffer( | 1834 if (!CreateMediaContentOffer( |
| 1805 options, | 1835 options, |
| 1806 *data_codecs, | 1836 *data_codecs, |
| 1807 sdes_policy, | 1837 sdes_policy, |
| 1808 GetCryptos(GetFirstDataContentDescription(current_description)), | 1838 GetCryptos(GetFirstDataContentDescription(current_description)), |
| 1809 crypto_suites, | 1839 crypto_suites, |
| 1810 RtpHeaderExtensions(), | 1840 RtpHeaderExtensions(), |
| 1811 add_legacy_, | 1841 add_legacy_, |
| (...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2164 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO)); | 2194 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO)); |
| 2165 } | 2195 } |
| 2166 | 2196 |
| 2167 DataContentDescription* GetFirstDataContentDescription( | 2197 DataContentDescription* GetFirstDataContentDescription( |
| 2168 SessionDescription* sdesc) { | 2198 SessionDescription* sdesc) { |
| 2169 return static_cast<DataContentDescription*>( | 2199 return static_cast<DataContentDescription*>( |
| 2170 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA)); | 2200 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA)); |
| 2171 } | 2201 } |
| 2172 | 2202 |
| 2173 } // namespace cricket | 2203 } // namespace cricket |
| OLD | NEW |