1/*
2 * libjingle
3 * Copyright 2004 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
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.
15 *
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.
26 */
27
28#include "talk/session/media/mediasession.h"
29
30#include <functional>
31#include <map>
32#include <set>
33#include <utility>
34
35#include "talk/media/base/constants.h"
36#include "talk/media/base/cryptoparams.h"
37#include "talk/p2p/base/constants.h"
38#include "talk/session/media/channelmanager.h"
39#include "talk/session/media/srtpfilter.h"
40#include "talk/xmpp/constants.h"
41#include "webrtc/base/helpers.h"
42#include "webrtc/base/logging.h"
43#include "webrtc/base/scoped_ptr.h"
44#include "webrtc/base/stringutils.h"
45
46#ifdef HAVE_SCTP
47#include "talk/media/sctp/sctpdataengine.h"
48#else
49static const uint32 kMaxSctpSid = 1023;
50#endif
51
52namespace {
53const char kInline[] = "inline:";
54}
55
56namespace cricket {
57
58using rtc::scoped_ptr;
59
60// RTP Profile names
61// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
62// RFC4585
63const char kMediaProtocolAvpf[] = "RTP/AVPF";
64// RFC5124
65const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
66
67// This should be replaced by "UDP/TLS/RTP/SAVPF", but we need to support it for
68// now to be compatible with previous Chrome versions.
69const char kMediaProtocolSavpf[] = "RTP/SAVPF";
70
71const char kMediaProtocolRtpPrefix[] = "RTP/";
72
73const char kMediaProtocolSctp[] = "SCTP";
74const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
75
76static bool IsMediaContentOfType(const ContentInfo* content,
77                                 MediaType media_type) {
78  if (!IsMediaContent(content)) {
79    return false;
80  }
81
82  const MediaContentDescription* mdesc =
83      static_cast<const MediaContentDescription*>(content->description);
84  return mdesc && mdesc->type() == media_type;
85}
86
87static bool CreateCryptoParams(int tag, const std::string& cipher,
88                               CryptoParams *out) {
89  std::string key;
90  key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
91
92  if (!rtc::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
93    return false;
94  }
95  out->tag = tag;
96  out->cipher_suite = cipher;
97  out->key_params = kInline;
98  out->key_params += key;
99  return true;
100}
101
102#ifdef HAVE_SRTP
103static bool AddCryptoParams(const std::string& cipher_suite,
104                            CryptoParamsVec *out) {
105  int size = static_cast<int>(out->size());
106
107  out->resize(size + 1);
108  return CreateCryptoParams(size, cipher_suite, &out->at(size));
109}
110
111void AddMediaCryptos(const CryptoParamsVec& cryptos,
112                     MediaContentDescription* media) {
113  for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
114       crypto != cryptos.end(); ++crypto) {
115    media->AddCrypto(*crypto);
116  }
117}
118
119bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
120                        MediaContentDescription* media) {
121  CryptoParamsVec cryptos;
122  for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
123       it != crypto_suites.end(); ++it) {
124    if (!AddCryptoParams(*it, &cryptos)) {
125      return false;
126    }
127  }
128  AddMediaCryptos(cryptos, media);
129  return true;
130}
131#endif
132
133const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
134  if (!media) {
135    return NULL;
136  }
137  return &media->cryptos();
138}
139
140bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
141                        const CryptoParams& crypto,
142                        CryptoParams* out) {
143  for (CryptoParamsVec::const_iterator it = cryptos.begin();
144       it != cryptos.end(); ++it) {
145    if (crypto.Matches(*it)) {
146      *out = *it;
147      return true;
148    }
149  }
150  return false;
151}
152
153// For audio, HMAC 32 is prefered because of the low overhead.
154void GetSupportedAudioCryptoSuites(
155    std::vector<std::string>* crypto_suites) {
156#ifdef HAVE_SRTP
157  crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_32);
158  crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80);
159#endif
160}
161
162void GetSupportedVideoCryptoSuites(
163    std::vector<std::string>* crypto_suites) {
164  GetSupportedDefaultCryptoSuites(crypto_suites);
165}
166
167void GetSupportedDataCryptoSuites(
168    std::vector<std::string>* crypto_suites) {
169  GetSupportedDefaultCryptoSuites(crypto_suites);
170}
171
172void GetSupportedDefaultCryptoSuites(
173    std::vector<std::string>* crypto_suites) {
174#ifdef HAVE_SRTP
175  crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80);
176#endif
177}
178
179// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
180// tolerated unless bundle is enabled because it is low overhead. Pick the
181// crypto in the list that is supported.
182static bool SelectCrypto(const MediaContentDescription* offer,
183                         bool bundle,
184                         CryptoParams *crypto) {
185  bool audio = offer->type() == MEDIA_TYPE_AUDIO;
186  const CryptoParamsVec& cryptos = offer->cryptos();
187
188  for (CryptoParamsVec::const_iterator i = cryptos.begin();
189       i != cryptos.end(); ++i) {
190    if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
191        (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio && !bundle)) {
192      return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
193    }
194  }
195  return false;
196}
197
198static const StreamParams* FindFirstStreamParamsByCname(
199    const StreamParamsVec& params_vec,
200    const std::string& cname) {
201  for (StreamParamsVec::const_iterator it = params_vec.begin();
202       it != params_vec.end(); ++it) {
203    if (cname == it->cname)
204      return &*it;
205  }
206  return NULL;
207}
208
209// Generates a new CNAME or the CNAME of an already existing StreamParams
210// if a StreamParams exist for another Stream in streams with sync_label
211// sync_label.
212static bool GenerateCname(const StreamParamsVec& params_vec,
213                          const MediaSessionOptions::Streams& streams,
214                          const std::string& synch_label,
215                          std::string* cname) {
216  ASSERT(cname != NULL);
217  if (!cname)
218    return false;
219
220  // Check if a CNAME exist for any of the other synched streams.
221  for (MediaSessionOptions::Streams::const_iterator stream_it = streams.begin();
222       stream_it != streams.end() ; ++stream_it) {
223    if (synch_label != stream_it->sync_label)
224      continue;
225
226    StreamParams param;
227    // groupid is empty for StreamParams generated using
228    // MediaSessionDescriptionFactory.
229    if (GetStreamByIds(params_vec, "", stream_it->id,
230                       &param)) {
231      *cname = param.cname;
232      return true;
233    }
234  }
235  // No other stream seems to exist that we should sync with.
236  // Generate a random string for the RTCP CNAME, as stated in RFC 6222.
237  // This string is only used for synchronization, and therefore is opaque.
238  do {
239    if (!rtc::CreateRandomString(16, cname)) {
240      ASSERT(false);
241      return false;
242    }
243  } while (FindFirstStreamParamsByCname(params_vec, *cname));
244
245  return true;
246}
247
248// Generate random SSRC values that are not already present in |params_vec|.
249// The generated values are added to |ssrcs|.
250// |num_ssrcs| is the number of the SSRC will be generated.
251static void GenerateSsrcs(const StreamParamsVec& params_vec,
252                          int num_ssrcs,
253                          std::vector<uint32>* ssrcs) {
254  for (int i = 0; i < num_ssrcs; i++) {
255    uint32 candidate;
256    do {
257      candidate = rtc::CreateRandomNonZeroId();
258    } while (GetStreamBySsrc(params_vec, candidate, NULL) ||
259             std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
260    ssrcs->push_back(candidate);
261  }
262}
263
264// Returns false if we exhaust the range of SIDs.
265static bool GenerateSctpSid(const StreamParamsVec& params_vec,
266                            uint32* sid) {
267  if (params_vec.size() > kMaxSctpSid) {
268    LOG(LS_WARNING) <<
269        "Could not generate an SCTP SID: too many SCTP streams.";
270    return false;
271  }
272  while (true) {
273    uint32 candidate = rtc::CreateRandomNonZeroId() % kMaxSctpSid;
274    if (!GetStreamBySsrc(params_vec, candidate, NULL)) {
275      *sid = candidate;
276      return true;
277    }
278  }
279}
280
281static bool GenerateSctpSids(const StreamParamsVec& params_vec,
282                             std::vector<uint32>* sids) {
283  uint32 sid;
284  if (!GenerateSctpSid(params_vec, &sid)) {
285    LOG(LS_WARNING) << "Could not generated an SCTP SID.";
286    return false;
287  }
288  sids->push_back(sid);
289  return true;
290}
291
292// Finds all StreamParams of all media types and attach them to stream_params.
293static void GetCurrentStreamParams(const SessionDescription* sdesc,
294                                   StreamParamsVec* stream_params) {
295  if (!sdesc)
296    return;
297
298  const ContentInfos& contents = sdesc->contents();
299  for (ContentInfos::const_iterator content = contents.begin();
300       content != contents.end(); ++content) {
301    if (!IsMediaContent(&*content)) {
302      continue;
303    }
304    const MediaContentDescription* media =
305        static_cast<const MediaContentDescription*>(
306            content->description);
307    const StreamParamsVec& streams = media->streams();
308    for (StreamParamsVec::const_iterator it = streams.begin();
309         it != streams.end(); ++it) {
310      stream_params->push_back(*it);
311    }
312  }
313}
314
315// Filters the data codecs for the data channel type.
316void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
317  // Filter RTP codec for SCTP and vice versa.
318  int codec_id = sctp ? kGoogleRtpDataCodecId : kGoogleSctpDataCodecId;
319  for (std::vector<DataCodec>::iterator iter = codecs->begin();
320       iter != codecs->end();) {
321    if (iter->id == codec_id) {
322      iter = codecs->erase(iter);
323    } else {
324      ++iter;
325    }
326  }
327}
328
329template <typename IdStruct>
330class UsedIds {
331 public:
332  UsedIds(int min_allowed_id, int max_allowed_id)
333      : min_allowed_id_(min_allowed_id),
334        max_allowed_id_(max_allowed_id),
335        next_id_(max_allowed_id) {
336  }
337
338  // Loops through all Id in |ids| and changes its id if it is
339  // already in use by another IdStruct. Call this methods with all Id
340  // in a session description to make sure no duplicate ids exists.
341  // Note that typename Id must be a type of IdStruct.
342  template <typename Id>
343  void FindAndSetIdUsed(std::vector<Id>* ids) {
344    for (typename std::vector<Id>::iterator it = ids->begin();
345         it != ids->end(); ++it) {
346      FindAndSetIdUsed(&*it);
347    }
348  }
349
350  // Finds and sets an unused id if the |idstruct| id is already in use.
351  void FindAndSetIdUsed(IdStruct* idstruct) {
352    const int original_id = idstruct->id;
353    int new_id = idstruct->id;
354
355    if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
356      // If the original id is not in range - this is an id that can't be
357      // dynamically changed.
358      return;
359    }
360
361    if (IsIdUsed(original_id)) {
362      new_id = FindUnusedId();
363      LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
364          << " to " << new_id;
365      idstruct->id = new_id;
366    }
367    SetIdUsed(new_id);
368  }
369
370 private:
371  // Returns the first unused id in reverse order.
372  // This hopefully reduce the risk of more collisions. We want to change the
373  // default ids as little as possible.
374  int FindUnusedId() {
375    while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
376      --next_id_;
377    }
378    ASSERT(next_id_ >= min_allowed_id_);
379    return next_id_;
380  }
381
382  bool IsIdUsed(int new_id) {
383    return id_set_.find(new_id) != id_set_.end();
384  }
385
386  void SetIdUsed(int new_id) {
387    id_set_.insert(new_id);
388  }
389
390  const int min_allowed_id_;
391  const int max_allowed_id_;
392  int next_id_;
393  std::set<int> id_set_;
394};
395
396// Helper class used for finding duplicate RTP payload types among audio, video
397// and data codecs. When bundle is used the payload types may not collide.
398class UsedPayloadTypes : public UsedIds<Codec> {
399 public:
400  UsedPayloadTypes()
401      : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
402  }
403
404
405 private:
406  static const int kDynamicPayloadTypeMin = 96;
407  static const int kDynamicPayloadTypeMax = 127;
408};
409
410// Helper class used for finding duplicate RTP Header extension ids among
411// audio and video extensions.
412class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> {
413 public:
414  UsedRtpHeaderExtensionIds()
415      : UsedIds<RtpHeaderExtension>(kLocalIdMin, kLocalIdMax) {
416  }
417
418 private:
419  // Min and Max local identifier for one-byte header extensions, per RFC5285.
420  static const int kLocalIdMin = 1;
421  static const int kLocalIdMax = 14;
422};
423
424static bool IsSctp(const MediaContentDescription* desc) {
425  return ((desc->protocol() == kMediaProtocolSctp) ||
426          (desc->protocol() == kMediaProtocolDtlsSctp));
427}
428
429// Adds a StreamParams for each Stream in Streams with media type
430// media_type to content_description.
431// |current_params| - All currently known StreamParams of any media type.
432template <class C>
433static bool AddStreamParams(
434    MediaType media_type,
435    const MediaSessionOptions::Streams& streams,
436    StreamParamsVec* current_streams,
437    MediaContentDescriptionImpl<C>* content_description,
438    const bool add_legacy_stream) {
439  const bool include_rtx_stream =
440    ContainsRtxCodec(content_description->codecs());
441
442  if (streams.empty() && add_legacy_stream) {
443    // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
444    std::vector<uint32> ssrcs;
445    if (IsSctp(content_description)) {
446      GenerateSctpSids(*current_streams, &ssrcs);
447    } else {
448      int num_ssrcs = include_rtx_stream ? 2 : 1;
449      GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
450    }
451    if (include_rtx_stream) {
452      content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
453      content_description->set_multistream(true);
454    } else {
455      content_description->AddLegacyStream(ssrcs[0]);
456    }
457    return true;
458  }
459
460  MediaSessionOptions::Streams::const_iterator stream_it;
461  for (stream_it = streams.begin();
462       stream_it != streams.end(); ++stream_it) {
463    if (stream_it->type != media_type)
464      continue;  // Wrong media type.
465
466    StreamParams param;
467    // groupid is empty for StreamParams generated using
468    // MediaSessionDescriptionFactory.
469    if (!GetStreamByIds(*current_streams, "", stream_it->id,
470                        &param)) {
471      // This is a new stream.
472      // Get a CNAME. Either new or same as one of the other synched streams.
473      std::string cname;
474      if (!GenerateCname(*current_streams, streams, stream_it->sync_label,
475                         &cname)) {
476        return false;
477      }
478
479      std::vector<uint32> ssrcs;
480      if (IsSctp(content_description)) {
481        GenerateSctpSids(*current_streams, &ssrcs);
482      } else {
483        GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
484      }
485      StreamParams stream_param;
486      stream_param.id = stream_it->id;
487      // Add the generated ssrc.
488      for (size_t i = 0; i < ssrcs.size(); ++i) {
489        stream_param.ssrcs.push_back(ssrcs[i]);
490      }
491      if (stream_it->num_sim_layers > 1) {
492        SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
493        stream_param.ssrc_groups.push_back(group);
494      }
495      // Generate an extra ssrc for include_rtx_stream case.
496      if (include_rtx_stream) {
497        std::vector<uint32> rtx_ssrc;
498        GenerateSsrcs(*current_streams, 1, &rtx_ssrc);
499        stream_param.AddFidSsrc(ssrcs[0], rtx_ssrc[0]);
500        content_description->set_multistream(true);
501      }
502      stream_param.cname = cname;
503      stream_param.sync_label = stream_it->sync_label;
504      content_description->AddStream(stream_param);
505
506      // Store the new StreamParams in current_streams.
507      // This is necessary so that we can use the CNAME for other media types.
508      current_streams->push_back(stream_param);
509    } else {
510      content_description->AddStream(param);
511    }
512  }
513  return true;
514}
515
516// Updates the transport infos of the |sdesc| according to the given
517// |bundle_group|. The transport infos of the content names within the
518// |bundle_group| should be updated to use the ufrag and pwd of the first
519// content within the |bundle_group|.
520static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
521                                         SessionDescription* sdesc) {
522  // The bundle should not be empty.
523  if (!sdesc || !bundle_group.FirstContentName()) {
524    return false;
525  }
526
527  // We should definitely have a transport for the first content.
528  std::string selected_content_name = *bundle_group.FirstContentName();
529  const TransportInfo* selected_transport_info =
530      sdesc->GetTransportInfoByName(selected_content_name);
531  if (!selected_transport_info) {
532    return false;
533  }
534
535  // Set the other contents to use the same ICE credentials.
536  const std::string selected_ufrag =
537      selected_transport_info->description.ice_ufrag;
538  const std::string selected_pwd =
539      selected_transport_info->description.ice_pwd;
540  for (TransportInfos::iterator it =
541           sdesc->transport_infos().begin();
542       it != sdesc->transport_infos().end(); ++it) {
543    if (bundle_group.HasContentName(it->content_name) &&
544        it->content_name != selected_content_name) {
545      it->description.ice_ufrag = selected_ufrag;
546      it->description.ice_pwd = selected_pwd;
547    }
548  }
549  return true;
550}
551
552// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
553// sets it to |cryptos|.
554static bool GetCryptosByName(const SessionDescription* sdesc,
555                             const std::string& content_name,
556                             CryptoParamsVec* cryptos) {
557  if (!sdesc || !cryptos) {
558    return false;
559  }
560
561  const ContentInfo* content = sdesc->GetContentByName(content_name);
562  if (!IsMediaContent(content) || !content->description) {
563    return false;
564  }
565
566  const MediaContentDescription* media_desc =
567      static_cast<const MediaContentDescription*>(content->description);
568  *cryptos = media_desc->cryptos();
569  return true;
570}
571
572// Predicate function used by the remove_if.
573// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
574static bool CryptoNotFound(const CryptoParams crypto,
575                           const CryptoParamsVec* filter) {
576  if (filter == NULL) {
577    return true;
578  }
579  for (CryptoParamsVec::const_iterator it = filter->begin();
580       it != filter->end(); ++it) {
581    if (it->cipher_suite == crypto.cipher_suite) {
582      return false;
583    }
584  }
585  return true;
586}
587
588// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
589// which are not available in |filter|.
590static void PruneCryptos(const CryptoParamsVec& filter,
591                         CryptoParamsVec* target_cryptos) {
592  if (!target_cryptos) {
593    return;
594  }
595  target_cryptos->erase(std::remove_if(target_cryptos->begin(),
596                                       target_cryptos->end(),
597                                       bind2nd(ptr_fun(CryptoNotFound),
598                                               &filter)),
599                        target_cryptos->end());
600}
601
602static bool IsRtpContent(SessionDescription* sdesc,
603                         const std::string& content_name) {
604  bool is_rtp = false;
605  ContentInfo* content = sdesc->GetContentByName(content_name);
606  if (IsMediaContent(content)) {
607    MediaContentDescription* media_desc =
608        static_cast<MediaContentDescription*>(content->description);
609    if (!media_desc) {
610      return false;
611    }
612    is_rtp = media_desc->protocol().empty() ||
613             rtc::starts_with(media_desc->protocol().data(),
614                                    kMediaProtocolRtpPrefix);
615  }
616  return is_rtp;
617}
618
619// Updates the crypto parameters of the |sdesc| according to the given
620// |bundle_group|. The crypto parameters of all the contents within the
621// |bundle_group| should be updated to use the common subset of the
622// available cryptos.
623static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
624                                        SessionDescription* sdesc) {
625  // The bundle should not be empty.
626  if (!sdesc || !bundle_group.FirstContentName()) {
627    return false;
628  }
629
630  bool common_cryptos_needed = false;
631  // Get the common cryptos.
632  const ContentNames& content_names = bundle_group.content_names();
633  CryptoParamsVec common_cryptos;
634  for (ContentNames::const_iterator it = content_names.begin();
635       it != content_names.end(); ++it) {
636    if (!IsRtpContent(sdesc, *it)) {
637      continue;
638    }
639    // The common cryptos are needed if any of the content does not have DTLS
640    // enabled.
641    if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
642      common_cryptos_needed = true;
643    }
644    if (it == content_names.begin()) {
645      // Initial the common_cryptos with the first content in the bundle group.
646      if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
647        return false;
648      }
649      if (common_cryptos.empty()) {
650        // If there's no crypto params, we should just return.
651        return true;
652      }
653    } else {
654      CryptoParamsVec cryptos;
655      if (!GetCryptosByName(sdesc, *it, &cryptos)) {
656        return false;
657      }
658      PruneCryptos(cryptos, &common_cryptos);
659    }
660  }
661
662  if (common_cryptos.empty() && common_cryptos_needed) {
663    return false;
664  }
665
666  // Update to use the common cryptos.
667  for (ContentNames::const_iterator it = content_names.begin();
668       it != content_names.end(); ++it) {
669    if (!IsRtpContent(sdesc, *it)) {
670      continue;
671    }
672    ContentInfo* content = sdesc->GetContentByName(*it);
673    if (IsMediaContent(content)) {
674      MediaContentDescription* media_desc =
675          static_cast<MediaContentDescription*>(content->description);
676      if (!media_desc) {
677        return false;
678      }
679      media_desc->set_cryptos(common_cryptos);
680    }
681  }
682  return true;
683}
684
685template <class C>
686static bool ContainsRtxCodec(const std::vector<C>& codecs) {
687  typename std::vector<C>::const_iterator it;
688  for (it = codecs.begin(); it != codecs.end(); ++it) {
689    if (IsRtxCodec(*it)) {
690      return true;
691    }
692  }
693  return false;
694}
695
696template <class C>
697static bool IsRtxCodec(const C& codec) {
698  return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
699}
700
701// Create a media content to be offered in a session-initiate,
702// according to the given options.rtcp_mux, options.is_muc,
703// options.streams, codecs, secure_transport, crypto, and streams.  If we don't
704// currently have crypto (in current_cryptos) and it is enabled (in
705// secure_policy), crypto is created (according to crypto_suites).  If
706// add_legacy_stream is true, and current_streams is empty, a legacy
707// stream is created.  The created content is added to the offer.
708template <class C>
709static bool CreateMediaContentOffer(
710    const MediaSessionOptions& options,
711    const std::vector<C>& codecs,
712    const SecurePolicy& secure_policy,
713    const CryptoParamsVec* current_cryptos,
714    const std::vector<std::string>& crypto_suites,
715    const RtpHeaderExtensions& rtp_extensions,
716    bool add_legacy_stream,
717    StreamParamsVec* current_streams,
718    MediaContentDescriptionImpl<C>* offer) {
719  offer->AddCodecs(codecs);
720  offer->SortCodecs();
721
722  if (secure_policy == SEC_REQUIRED) {
723    offer->set_crypto_required(CT_SDES);
724  }
725  offer->set_rtcp_mux(options.rtcp_mux_enabled);
726  offer->set_multistream(options.is_muc);
727  offer->set_rtp_header_extensions(rtp_extensions);
728
729  if (!AddStreamParams(
730          offer->type(), options.streams, current_streams,
731          offer, add_legacy_stream)) {
732    return false;
733  }
734
735#ifdef HAVE_SRTP
736  if (secure_policy != SEC_DISABLED) {
737    if (current_cryptos) {
738      AddMediaCryptos(*current_cryptos, offer);
739    }
740    if (offer->cryptos().empty()) {
741      if (!CreateMediaCryptos(crypto_suites, offer)) {
742        return false;
743      }
744    }
745  }
746#endif
747
748  if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
749    return false;
750  }
751  return true;
752}
753
754template <class C>
755static void NegotiateCodecs(const std::vector<C>& local_codecs,
756                            const std::vector<C>& offered_codecs,
757                            std::vector<C>* negotiated_codecs) {
758  typename std::vector<C>::const_iterator ours;
759  for (ours = local_codecs.begin();
760       ours != local_codecs.end(); ++ours) {
761    typename std::vector<C>::const_iterator theirs;
762    for (theirs = offered_codecs.begin();
763         theirs != offered_codecs.end(); ++theirs) {
764      if (ours->Matches(*theirs)) {
765        C negotiated = *ours;
766        negotiated.IntersectFeedbackParams(*theirs);
767        if (IsRtxCodec(negotiated)) {
768          // Only negotiate RTX if kCodecParamAssociatedPayloadType has been
769          // set.
770          std::string apt_value;
771          if (!theirs->GetParam(kCodecParamAssociatedPayloadType, &apt_value)) {
772            LOG(LS_WARNING) << "RTX missing associated payload type.";
773            continue;
774          }
775          negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_value);
776        }
777        negotiated.id = theirs->id;
778        // RFC3264: Although the answerer MAY list the formats in their desired
779        // order of preference, it is RECOMMENDED that unless there is a
780        // specific reason, the answerer list formats in the same relative order
781        // they were present in the offer.
782        negotiated.preference = theirs->preference;
783        negotiated_codecs->push_back(negotiated);
784      }
785    }
786  }
787}
788
789template <class C>
790static bool FindMatchingCodec(const std::vector<C>& codecs,
791                              const C& codec_to_match,
792                              C* found_codec) {
793  for (typename std::vector<C>::const_iterator it = codecs.begin();
794       it  != codecs.end(); ++it) {
795    if (it->Matches(codec_to_match)) {
796      if (found_codec != NULL) {
797        *found_codec= *it;
798      }
799      return true;
800    }
801  }
802  return false;
803}
804
805// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
806// already exist in |offered_codecs| and ensure the payload types don't
807// collide.
808template <class C>
809static void FindCodecsToOffer(
810    const std::vector<C>& reference_codecs,
811    std::vector<C>* offered_codecs,
812    UsedPayloadTypes* used_pltypes) {
813
814  typedef std::map<int, C> RtxCodecReferences;
815  RtxCodecReferences new_rtx_codecs;
816
817  // Find all new RTX codecs.
818  for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
819       it != reference_codecs.end(); ++it) {
820    if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && IsRtxCodec(*it)) {
821      C rtx_codec = *it;
822      int referenced_pl_type =
823          rtc::FromString<int>(0,
824              rtx_codec.params[kCodecParamAssociatedPayloadType]);
825      new_rtx_codecs.insert(std::pair<int, C>(referenced_pl_type,
826                                              rtx_codec));
827    }
828  }
829
830  // Add all new codecs that are not RTX codecs.
831  for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
832       it != reference_codecs.end(); ++it) {
833    if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && !IsRtxCodec(*it)) {
834      C codec = *it;
835      int original_payload_id = codec.id;
836      used_pltypes->FindAndSetIdUsed(&codec);
837      offered_codecs->push_back(codec);
838
839      // If this codec is referenced by a new RTX codec, update the reference
840      // in the RTX codec with the new payload type.
841      typename RtxCodecReferences::iterator rtx_it =
842          new_rtx_codecs.find(original_payload_id);
843      if (rtx_it != new_rtx_codecs.end()) {
844        C& rtx_codec = rtx_it->second;
845        rtx_codec.params[kCodecParamAssociatedPayloadType] =
846            rtc::ToString(codec.id);
847      }
848    }
849  }
850
851  // Add all new RTX codecs.
852  for (typename RtxCodecReferences::iterator it = new_rtx_codecs.begin();
853       it != new_rtx_codecs.end(); ++it) {
854    C& rtx_codec = it->second;
855    used_pltypes->FindAndSetIdUsed(&rtx_codec);
856    offered_codecs->push_back(rtx_codec);
857  }
858}
859
860
861static bool FindByUri(const RtpHeaderExtensions& extensions,
862                      const RtpHeaderExtension& ext_to_match,
863                      RtpHeaderExtension* found_extension) {
864  for (RtpHeaderExtensions::const_iterator it = extensions.begin();
865       it  != extensions.end(); ++it) {
866    // We assume that all URIs are given in a canonical format.
867    if (it->uri == ext_to_match.uri) {
868      if (found_extension != NULL) {
869        *found_extension = *it;
870      }
871      return true;
872    }
873  }
874  return false;
875}
876
877static void FindAndSetRtpHdrExtUsed(
878  const RtpHeaderExtensions& reference_extensions,
879  RtpHeaderExtensions* offered_extensions,
880  const RtpHeaderExtensions& other_extensions,
881  UsedRtpHeaderExtensionIds* used_extensions) {
882  for (RtpHeaderExtensions::const_iterator it = reference_extensions.begin();
883      it != reference_extensions.end(); ++it) {
884    if (!FindByUri(*offered_extensions, *it, NULL)) {
885      RtpHeaderExtension ext;
886      if (!FindByUri(other_extensions, *it, &ext)) {
887        ext = *it;
888        used_extensions->FindAndSetIdUsed(&ext);
889      }
890      offered_extensions->push_back(ext);
891    }
892  }
893}
894
895static void NegotiateRtpHeaderExtensions(
896    const RtpHeaderExtensions& local_extensions,
897    const RtpHeaderExtensions& offered_extensions,
898    RtpHeaderExtensions* negotiated_extenstions) {
899  RtpHeaderExtensions::const_iterator ours;
900  for (ours = local_extensions.begin();
901       ours != local_extensions.end(); ++ours) {
902    RtpHeaderExtension theirs;
903    if (FindByUri(offered_extensions, *ours, &theirs)) {
904      // We respond with their RTP header extension id.
905      negotiated_extenstions->push_back(theirs);
906    }
907  }
908}
909
910static void StripCNCodecs(AudioCodecs* audio_codecs) {
911  AudioCodecs::iterator iter = audio_codecs->begin();
912  while (iter != audio_codecs->end()) {
913    if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
914      iter = audio_codecs->erase(iter);
915    } else {
916      ++iter;
917    }
918  }
919}
920
921// Create a media content to be answered in a session-accept,
922// according to the given options.rtcp_mux, options.streams, codecs,
923// crypto, and streams.  If we don't currently have crypto (in
924// current_cryptos) and it is enabled (in secure_policy), crypto is
925// created (according to crypto_suites).  If add_legacy_stream is
926// true, and current_streams is empty, a legacy stream is created.
927// The codecs, rtcp_mux, and crypto are all negotiated with the offer
928// from the incoming session-initiate.  If the negotiation fails, this
929// method returns false.  The created content is added to the offer.
930template <class C>
931static bool CreateMediaContentAnswer(
932    const MediaContentDescriptionImpl<C>* offer,
933    const MediaSessionOptions& options,
934    const std::vector<C>& local_codecs,
935    const SecurePolicy& sdes_policy,
936    const CryptoParamsVec* current_cryptos,
937    const RtpHeaderExtensions& local_rtp_extenstions,
938    StreamParamsVec* current_streams,
939    bool add_legacy_stream,
940    bool bundle_enabled,
941    MediaContentDescriptionImpl<C>* answer) {
942  std::vector<C> negotiated_codecs;
943  NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
944  answer->AddCodecs(negotiated_codecs);
945  answer->SortCodecs();
946  answer->set_protocol(offer->protocol());
947  RtpHeaderExtensions negotiated_rtp_extensions;
948  NegotiateRtpHeaderExtensions(local_rtp_extenstions,
949                               offer->rtp_header_extensions(),
950                               &negotiated_rtp_extensions);
951  answer->set_rtp_header_extensions(negotiated_rtp_extensions);
952
953  answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
954
955  if (sdes_policy != SEC_DISABLED) {
956    CryptoParams crypto;
957    if (SelectCrypto(offer, bundle_enabled, &crypto)) {
958      if (current_cryptos) {
959        FindMatchingCrypto(*current_cryptos, crypto, &crypto);
960      }
961      answer->AddCrypto(crypto);
962    }
963  }
964
965  if (answer->cryptos().empty() &&
966      (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
967    return false;
968  }
969
970  if (!AddStreamParams(
971          answer->type(), options.streams, current_streams,
972          answer, add_legacy_stream)) {
973    return false;  // Something went seriously wrong.
974  }
975
976  // Make sure the answer media content direction is per default set as
977  // described in RFC3264 section 6.1.
978  switch (offer->direction()) {
979    case MD_INACTIVE:
980      answer->set_direction(MD_INACTIVE);
981      break;
982    case MD_SENDONLY:
983      answer->set_direction(MD_RECVONLY);
984      break;
985    case MD_RECVONLY:
986      answer->set_direction(MD_SENDONLY);
987      break;
988    case MD_SENDRECV:
989      answer->set_direction(MD_SENDRECV);
990      break;
991    default:
992      break;
993  }
994
995  return true;
996}
997
998static bool IsMediaProtocolSupported(MediaType type,
999                                     const std::string& protocol,
1000                                     bool secure_transport) {
1001  // Data channels can have a protocol of SCTP or SCTP/DTLS.
1002  if (type == MEDIA_TYPE_DATA &&
1003      ((protocol == kMediaProtocolSctp && !secure_transport)||
1004       (protocol == kMediaProtocolDtlsSctp && secure_transport))) {
1005    return true;
1006  }
1007
1008  // Since not all applications serialize and deserialize the media protocol,
1009  // we will have to accept |protocol| to be empty.
1010  return protocol == kMediaProtocolAvpf || protocol.empty() ||
1011      protocol == kMediaProtocolSavpf ||
1012      (protocol == kMediaProtocolDtlsSavpf && secure_transport);
1013}
1014
1015static void SetMediaProtocol(bool secure_transport,
1016                             MediaContentDescription* desc) {
1017  if (!desc->cryptos().empty() || secure_transport)
1018    desc->set_protocol(kMediaProtocolSavpf);
1019  else
1020    desc->set_protocol(kMediaProtocolAvpf);
1021}
1022
1023// Gets the TransportInfo of the given |content_name| from the
1024// |current_description|. If doesn't exist, returns a new one.
1025static const TransportDescription* GetTransportDescription(
1026    const std::string& content_name,
1027    const SessionDescription* current_description) {
1028  const TransportDescription* desc = NULL;
1029  if (current_description) {
1030    const TransportInfo* info =
1031        current_description->GetTransportInfoByName(content_name);
1032    if (info) {
1033      desc = &info->description;
1034    }
1035  }
1036  return desc;
1037}
1038
1039// Gets the current DTLS state from the transport description.
1040static bool IsDtlsActive(
1041    const std::string& content_name,
1042    const SessionDescription* current_description) {
1043  if (!current_description)
1044    return false;
1045
1046  const ContentInfo* content =
1047      current_description->GetContentByName(content_name);
1048  if (!content)
1049    return false;
1050
1051  const TransportDescription* current_tdesc =
1052      GetTransportDescription(content_name, current_description);
1053  if (!current_tdesc)
1054    return false;
1055
1056  return current_tdesc->secure();
1057}
1058
1059std::string MediaTypeToString(MediaType type) {
1060  std::string type_str;
1061  switch (type) {
1062    case MEDIA_TYPE_AUDIO:
1063      type_str = "audio";
1064      break;
1065    case MEDIA_TYPE_VIDEO:
1066      type_str = "video";
1067      break;
1068    case MEDIA_TYPE_DATA:
1069      type_str = "data";
1070      break;
1071    default:
1072      ASSERT(false);
1073      break;
1074  }
1075  return type_str;
1076}
1077
1078void MediaSessionOptions::AddStream(MediaType type,
1079                                    const std::string& id,
1080                                    const std::string& sync_label) {
1081  AddStreamInternal(type, id, sync_label, 1);
1082}
1083
1084void MediaSessionOptions::AddVideoStream(
1085    const std::string& id,
1086    const std::string& sync_label,
1087    int num_sim_layers) {
1088  AddStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
1089}
1090
1091void MediaSessionOptions::AddStreamInternal(
1092    MediaType type,
1093    const std::string& id,
1094    const std::string& sync_label,
1095    int num_sim_layers) {
1096  streams.push_back(Stream(type, id, sync_label, num_sim_layers));
1097
1098  if (type == MEDIA_TYPE_VIDEO)
1099    has_video = true;
1100  else if (type == MEDIA_TYPE_AUDIO)
1101    has_audio = true;
1102  // If we haven't already set the data_channel_type, and we add a
1103  // stream, we assume it's an RTP data stream.
1104  else if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
1105    data_channel_type = DCT_RTP;
1106}
1107
1108void MediaSessionOptions::RemoveStream(MediaType type,
1109                                       const std::string& id) {
1110  Streams::iterator stream_it = streams.begin();
1111  for (; stream_it != streams.end(); ++stream_it) {
1112    if (stream_it->type == type && stream_it->id == id) {
1113      streams.erase(stream_it);
1114      return;
1115    }
1116  }
1117  ASSERT(false);
1118}
1119
1120MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1121    const TransportDescriptionFactory* transport_desc_factory)
1122    : secure_(SEC_DISABLED),
1123      add_legacy_(true),
1124      transport_desc_factory_(transport_desc_factory) {
1125}
1126
1127MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1128    ChannelManager* channel_manager,
1129    const TransportDescriptionFactory* transport_desc_factory)
1130    : secure_(SEC_DISABLED),
1131      add_legacy_(true),
1132      transport_desc_factory_(transport_desc_factory) {
1133  channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
1134  channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1135  channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1136  channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1137  channel_manager->GetSupportedDataCodecs(&data_codecs_);
1138}
1139
1140SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1141    const MediaSessionOptions& options,
1142    const SessionDescription* current_description) const {
1143  scoped_ptr<SessionDescription> offer(new SessionDescription());
1144
1145  StreamParamsVec current_streams;
1146  GetCurrentStreamParams(current_description, &current_streams);
1147
1148  AudioCodecs audio_codecs;
1149  VideoCodecs video_codecs;
1150  DataCodecs data_codecs;
1151  GetCodecsToOffer(current_description, &audio_codecs, &video_codecs,
1152                   &data_codecs);
1153
1154  if (!options.vad_enabled) {
1155    // If application doesn't want CN codecs in offer.
1156    StripCNCodecs(&audio_codecs);
1157  }
1158
1159  RtpHeaderExtensions audio_rtp_extensions;
1160  RtpHeaderExtensions video_rtp_extensions;
1161  GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1162                       &video_rtp_extensions);
1163
1164  bool audio_added = false;
1165  bool video_added = false;
1166  bool data_added = false;
1167
1168  // Iterate through the contents of |current_description| to maintain the order
1169  // of the m-lines in the new offer.
1170  if (current_description) {
1171    ContentInfos::const_iterator it = current_description->contents().begin();
1172    for (; it != current_description->contents().end(); ++it) {
1173      if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1174        if (!AddAudioContentForOffer(options, current_description,
1175                                     audio_rtp_extensions, audio_codecs,
1176                                     &current_streams, offer.get())) {
1177          return NULL;
1178        }
1179        audio_added = true;
1180      } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1181        if (!AddVideoContentForOffer(options, current_description,
1182                                     video_rtp_extensions, video_codecs,
1183                                     &current_streams, offer.get())) {
1184          return NULL;
1185        }
1186        video_added = true;
1187      } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
1188        if (!AddDataContentForOffer(options, current_description, &data_codecs,
1189                                    &current_streams, offer.get())) {
1190          return NULL;
1191        }
1192        data_added = true;
1193      } else {
1194        ASSERT(false);
1195      }
1196    }
1197  }
1198  // Append contents that are not in |current_description|.
1199  if (!audio_added && options.has_audio &&
1200      !AddAudioContentForOffer(options, current_description,
1201                               audio_rtp_extensions, audio_codecs,
1202                               &current_streams, offer.get())) {
1203    return NULL;
1204  }
1205  if (!video_added && options.has_video &&
1206      !AddVideoContentForOffer(options, current_description,
1207                               video_rtp_extensions, video_codecs,
1208                               &current_streams, offer.get())) {
1209    return NULL;
1210  }
1211  if (!data_added && options.has_data() &&
1212      !AddDataContentForOffer(options, current_description, &data_codecs,
1213                              &current_streams, offer.get())) {
1214    return NULL;
1215  }
1216
1217  // Bundle the contents together, if we've been asked to do so, and update any
1218  // parameters that need to be tweaked for BUNDLE.
1219  if (options.bundle_enabled) {
1220    ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1221    for (ContentInfos::const_iterator content = offer->contents().begin();
1222       content != offer->contents().end(); ++content) {
1223      offer_bundle.AddContentName(content->name);
1224    }
1225    offer->AddGroup(offer_bundle);
1226    if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1227      LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1228      return NULL;
1229    }
1230    if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1231      LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1232      return NULL;
1233    }
1234  }
1235
1236  return offer.release();
1237}
1238
1239SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1240    const SessionDescription* offer, const MediaSessionOptions& options,
1241    const SessionDescription* current_description) const {
1242  // The answer contains the intersection of the codecs in the offer with the
1243  // codecs we support, ordered by our local preference. As indicated by
1244  // XEP-0167, we retain the same payload ids from the offer in the answer.
1245  scoped_ptr<SessionDescription> answer(new SessionDescription());
1246
1247  StreamParamsVec current_streams;
1248  GetCurrentStreamParams(current_description, &current_streams);
1249
1250  if (offer) {
1251    ContentInfos::const_iterator it = offer->contents().begin();
1252    for (; it != offer->contents().end(); ++it) {
1253      if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1254        if (!AddAudioContentForAnswer(offer, options, current_description,
1255                                  &current_streams, answer.get())) {
1256          return NULL;
1257        }
1258      } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1259        if (!AddVideoContentForAnswer(offer, options, current_description,
1260                                      &current_streams, answer.get())) {
1261          return NULL;
1262        }
1263      } else {
1264        ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1265        if (!AddDataContentForAnswer(offer, options, current_description,
1266                                     &current_streams, answer.get())) {
1267          return NULL;
1268        }
1269      }
1270    }
1271  }
1272
1273  // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1274  // group in the answer with the appropriate content names.
1275  if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1276    const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1277    ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1278    for (ContentInfos::const_iterator content = answer->contents().begin();
1279       content != answer->contents().end(); ++content) {
1280      if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1281        answer_bundle.AddContentName(content->name);
1282      }
1283    }
1284    if (answer_bundle.FirstContentName()) {
1285      answer->AddGroup(answer_bundle);
1286
1287      // Share the same ICE credentials and crypto params across all contents,
1288      // as BUNDLE requires.
1289      if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1290        LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1291        return NULL;
1292      }
1293
1294      if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1295        LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1296        return NULL;
1297      }
1298    }
1299  }
1300
1301  return answer.release();
1302}
1303
1304void MediaSessionDescriptionFactory::GetCodecsToOffer(
1305    const SessionDescription* current_description,
1306    AudioCodecs* audio_codecs,
1307    VideoCodecs* video_codecs,
1308    DataCodecs* data_codecs) const {
1309  UsedPayloadTypes used_pltypes;
1310  audio_codecs->clear();
1311  video_codecs->clear();
1312  data_codecs->clear();
1313
1314
1315  // First - get all codecs from the current description if the media type
1316  // is used.
1317  // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1318  // type is added.
1319  if (current_description) {
1320    const AudioContentDescription* audio =
1321        GetFirstAudioContentDescription(current_description);
1322    if (audio) {
1323      *audio_codecs = audio->codecs();
1324      used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1325    }
1326    const VideoContentDescription* video =
1327        GetFirstVideoContentDescription(current_description);
1328    if (video) {
1329      *video_codecs = video->codecs();
1330      used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1331    }
1332    const DataContentDescription* data =
1333        GetFirstDataContentDescription(current_description);
1334    if (data) {
1335      *data_codecs = data->codecs();
1336      used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1337    }
1338  }
1339
1340  // Add our codecs that are not in |current_description|.
1341  FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes);
1342  FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1343  FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1344}
1345
1346void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1347    const SessionDescription* current_description,
1348    RtpHeaderExtensions* audio_extensions,
1349    RtpHeaderExtensions* video_extensions) const {
1350  // All header extensions allocated from the same range to avoid potential
1351  // issues when using BUNDLE.
1352  UsedRtpHeaderExtensionIds used_ids;
1353  audio_extensions->clear();
1354  video_extensions->clear();
1355
1356  // First - get all extensions from the current description if the media type
1357  // is used.
1358  // Add them to |used_ids| so the local ids are not reused if a new media
1359  // type is added.
1360  if (current_description) {
1361    const AudioContentDescription* audio =
1362        GetFirstAudioContentDescription(current_description);
1363    if (audio) {
1364      *audio_extensions = audio->rtp_header_extensions();
1365      used_ids.FindAndSetIdUsed(audio_extensions);
1366    }
1367    const VideoContentDescription* video =
1368        GetFirstVideoContentDescription(current_description);
1369    if (video) {
1370      *video_extensions = video->rtp_header_extensions();
1371      used_ids.FindAndSetIdUsed(video_extensions);
1372    }
1373  }
1374
1375  // Add our default RTP header extensions that are not in
1376  // |current_description|.
1377  FindAndSetRtpHdrExtUsed(audio_rtp_header_extensions(), audio_extensions,
1378                          *video_extensions, &used_ids);
1379  FindAndSetRtpHdrExtUsed(video_rtp_header_extensions(), video_extensions,
1380                          *audio_extensions, &used_ids);
1381}
1382
1383bool MediaSessionDescriptionFactory::AddTransportOffer(
1384  const std::string& content_name,
1385  const TransportOptions& transport_options,
1386  const SessionDescription* current_desc,
1387  SessionDescription* offer_desc) const {
1388  if (!transport_desc_factory_)
1389     return false;
1390  const TransportDescription* current_tdesc =
1391      GetTransportDescription(content_name, current_desc);
1392  rtc::scoped_ptr<TransportDescription> new_tdesc(
1393      transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1394  bool ret = (new_tdesc.get() != NULL &&
1395      offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1396  if (!ret) {
1397    LOG(LS_ERROR)
1398        << "Failed to AddTransportOffer, content name=" << content_name;
1399  }
1400  return ret;
1401}
1402
1403TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1404    const std::string& content_name,
1405    const SessionDescription* offer_desc,
1406    const TransportOptions& transport_options,
1407    const SessionDescription* current_desc) const {
1408  if (!transport_desc_factory_)
1409    return NULL;
1410  const TransportDescription* offer_tdesc =
1411      GetTransportDescription(content_name, offer_desc);
1412  const TransportDescription* current_tdesc =
1413      GetTransportDescription(content_name, current_desc);
1414  return
1415      transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1416                                            current_tdesc);
1417}
1418
1419bool MediaSessionDescriptionFactory::AddTransportAnswer(
1420    const std::string& content_name,
1421    const TransportDescription& transport_desc,
1422    SessionDescription* answer_desc) const {
1423  if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1424                                                   transport_desc))) {
1425    LOG(LS_ERROR)
1426        << "Failed to AddTransportAnswer, content name=" << content_name;
1427    return false;
1428  }
1429  return true;
1430}
1431
1432bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1433    const MediaSessionOptions& options,
1434    const SessionDescription* current_description,
1435    const RtpHeaderExtensions& audio_rtp_extensions,
1436    const AudioCodecs& audio_codecs,
1437    StreamParamsVec* current_streams,
1438    SessionDescription* desc) const {
1439  cricket::SecurePolicy sdes_policy =
1440      IsDtlsActive(CN_AUDIO, current_description) ?
1441          cricket::SEC_DISABLED : secure();
1442
1443  scoped_ptr<AudioContentDescription> audio(new AudioContentDescription());
1444  std::vector<std::string> crypto_suites;
1445  GetSupportedAudioCryptoSuites(&crypto_suites);
1446  if (!CreateMediaContentOffer(
1447          options,
1448          audio_codecs,
1449          sdes_policy,
1450          GetCryptos(GetFirstAudioContentDescription(current_description)),
1451          crypto_suites,
1452          audio_rtp_extensions,
1453          add_legacy_,
1454          current_streams,
1455          audio.get())) {
1456    return false;
1457  }
1458  audio->set_lang(lang_);
1459
1460  bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1461  SetMediaProtocol(secure_transport, audio.get());
1462
1463  desc->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio.release());
1464  if (!AddTransportOffer(CN_AUDIO, options.transport_options,
1465                         current_description, desc)) {
1466    return false;
1467  }
1468
1469  return true;
1470}
1471
1472bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1473    const MediaSessionOptions& options,
1474    const SessionDescription* current_description,
1475    const RtpHeaderExtensions& video_rtp_extensions,
1476    const VideoCodecs& video_codecs,
1477    StreamParamsVec* current_streams,
1478    SessionDescription* desc) const {
1479  cricket::SecurePolicy sdes_policy =
1480      IsDtlsActive(CN_VIDEO, current_description) ?
1481          cricket::SEC_DISABLED : secure();
1482
1483  scoped_ptr<VideoContentDescription> video(new VideoContentDescription());
1484  std::vector<std::string> crypto_suites;
1485  GetSupportedVideoCryptoSuites(&crypto_suites);
1486  if (!CreateMediaContentOffer(
1487          options,
1488          video_codecs,
1489          sdes_policy,
1490          GetCryptos(GetFirstVideoContentDescription(current_description)),
1491          crypto_suites,
1492          video_rtp_extensions,
1493          add_legacy_,
1494          current_streams,
1495          video.get())) {
1496    return false;
1497  }
1498
1499  video->set_bandwidth(options.video_bandwidth);
1500
1501  bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1502  SetMediaProtocol(secure_transport, video.get());
1503  desc->AddContent(CN_VIDEO, NS_JINGLE_RTP, video.release());
1504  if (!AddTransportOffer(CN_VIDEO, options.transport_options,
1505                         current_description, desc)) {
1506    return false;
1507  }
1508
1509  return true;
1510}
1511
1512bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1513    const MediaSessionOptions& options,
1514    const SessionDescription* current_description,
1515    DataCodecs* data_codecs,
1516    StreamParamsVec* current_streams,
1517    SessionDescription* desc) const {
1518  bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1519
1520  scoped_ptr<DataContentDescription> data(new DataContentDescription());
1521  bool is_sctp = (options.data_channel_type == DCT_SCTP);
1522
1523  FilterDataCodecs(data_codecs, is_sctp);
1524
1525  cricket::SecurePolicy sdes_policy =
1526      IsDtlsActive(CN_DATA, current_description) ?
1527          cricket::SEC_DISABLED : secure();
1528  std::vector<std::string> crypto_suites;
1529  if (is_sctp) {
1530    // SDES doesn't make sense for SCTP, so we disable it, and we only
1531    // get SDES crypto suites for RTP-based data channels.
1532    sdes_policy = cricket::SEC_DISABLED;
1533    // Unlike SetMediaProtocol below, we need to set the protocol
1534    // before we call CreateMediaContentOffer.  Otherwise,
1535    // CreateMediaContentOffer won't know this is SCTP and will
1536    // generate SSRCs rather than SIDs.
1537    data->set_protocol(
1538        secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1539  } else {
1540    GetSupportedDataCryptoSuites(&crypto_suites);
1541  }
1542
1543  if (!CreateMediaContentOffer(
1544          options,
1545          *data_codecs,
1546          sdes_policy,
1547          GetCryptos(GetFirstDataContentDescription(current_description)),
1548          crypto_suites,
1549          RtpHeaderExtensions(),
1550          add_legacy_,
1551          current_streams,
1552          data.get())) {
1553    return false;
1554  }
1555
1556  if (is_sctp) {
1557    desc->AddContent(CN_DATA, NS_JINGLE_DRAFT_SCTP, data.release());
1558  } else {
1559    data->set_bandwidth(options.data_bandwidth);
1560    SetMediaProtocol(secure_transport, data.get());
1561    desc->AddContent(CN_DATA, NS_JINGLE_RTP, data.release());
1562  }
1563  if (!AddTransportOffer(CN_DATA, options.transport_options,
1564                         current_description, desc)) {
1565    return false;
1566  }
1567  return true;
1568}
1569
1570bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1571    const SessionDescription* offer,
1572    const MediaSessionOptions& options,
1573    const SessionDescription* current_description,
1574    StreamParamsVec* current_streams,
1575    SessionDescription* answer) const {
1576  const ContentInfo* audio_content = GetFirstAudioContent(offer);
1577
1578  scoped_ptr<TransportDescription> audio_transport(
1579      CreateTransportAnswer(audio_content->name, offer,
1580                            options.transport_options,
1581                            current_description));
1582  if (!audio_transport) {
1583    return false;
1584  }
1585
1586  AudioCodecs audio_codecs = audio_codecs_;
1587  if (!options.vad_enabled) {
1588    StripCNCodecs(&audio_codecs);
1589  }
1590
1591  bool bundle_enabled =
1592      offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1593  scoped_ptr<AudioContentDescription> audio_answer(
1594      new AudioContentDescription());
1595  // Do not require or create SDES cryptos if DTLS is used.
1596  cricket::SecurePolicy sdes_policy =
1597      audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1598  if (!CreateMediaContentAnswer(
1599          static_cast<const AudioContentDescription*>(
1600              audio_content->description),
1601          options,
1602          audio_codecs,
1603          sdes_policy,
1604          GetCryptos(GetFirstAudioContentDescription(current_description)),
1605          audio_rtp_extensions_,
1606          current_streams,
1607          add_legacy_,
1608          bundle_enabled,
1609          audio_answer.get())) {
1610    return false;  // Fails the session setup.
1611  }
1612
1613  bool rejected = !options.has_audio || audio_content->rejected ||
1614      !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1615                                audio_answer->protocol(),
1616                                audio_transport->secure());
1617  if (!rejected) {
1618    AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1619  } else {
1620    // RFC 3264
1621    // The answer MUST contain the same number of m-lines as the offer.
1622    LOG(LS_INFO) << "Audio is not supported in the answer.";
1623  }
1624
1625  answer->AddContent(audio_content->name, audio_content->type, rejected,
1626                     audio_answer.release());
1627  return true;
1628}
1629
1630bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1631    const SessionDescription* offer,
1632    const MediaSessionOptions& options,
1633    const SessionDescription* current_description,
1634    StreamParamsVec* current_streams,
1635    SessionDescription* answer) const {
1636  const ContentInfo* video_content = GetFirstVideoContent(offer);
1637  scoped_ptr<TransportDescription> video_transport(
1638      CreateTransportAnswer(video_content->name, offer,
1639                            options.transport_options,
1640                            current_description));
1641  if (!video_transport) {
1642    return false;
1643  }
1644
1645  scoped_ptr<VideoContentDescription> video_answer(
1646      new VideoContentDescription());
1647  // Do not require or create SDES cryptos if DTLS is used.
1648  cricket::SecurePolicy sdes_policy =
1649      video_transport->secure() ? cricket::SEC_DISABLED : secure();
1650  bool bundle_enabled =
1651      offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1652  if (!CreateMediaContentAnswer(
1653          static_cast<const VideoContentDescription*>(
1654              video_content->description),
1655          options,
1656          video_codecs_,
1657          sdes_policy,
1658          GetCryptos(GetFirstVideoContentDescription(current_description)),
1659          video_rtp_extensions_,
1660          current_streams,
1661          add_legacy_,
1662          bundle_enabled,
1663          video_answer.get())) {
1664    return false;
1665  }
1666  bool rejected = !options.has_video || video_content->rejected ||
1667      !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1668                                video_answer->protocol(),
1669                                video_transport->secure());
1670  if (!rejected) {
1671    if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1672                            answer)) {
1673      return false;
1674    }
1675    video_answer->set_bandwidth(options.video_bandwidth);
1676  } else {
1677    // RFC 3264
1678    // The answer MUST contain the same number of m-lines as the offer.
1679    LOG(LS_INFO) << "Video is not supported in the answer.";
1680  }
1681  answer->AddContent(video_content->name, video_content->type, rejected,
1682                     video_answer.release());
1683  return true;
1684}
1685
1686bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1687    const SessionDescription* offer,
1688    const MediaSessionOptions& options,
1689    const SessionDescription* current_description,
1690    StreamParamsVec* current_streams,
1691    SessionDescription* answer) const {
1692  const ContentInfo* data_content = GetFirstDataContent(offer);
1693  scoped_ptr<TransportDescription> data_transport(
1694      CreateTransportAnswer(data_content->name, offer,
1695                            options.transport_options,
1696                            current_description));
1697  if (!data_transport) {
1698    return false;
1699  }
1700  bool is_sctp = (options.data_channel_type == DCT_SCTP);
1701  std::vector<DataCodec> data_codecs(data_codecs_);
1702  FilterDataCodecs(&data_codecs, is_sctp);
1703
1704  scoped_ptr<DataContentDescription> data_answer(
1705      new DataContentDescription());
1706  // Do not require or create SDES cryptos if DTLS is used.
1707  cricket::SecurePolicy sdes_policy =
1708      data_transport->secure() ? cricket::SEC_DISABLED : secure();
1709  bool bundle_enabled =
1710      offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1711  if (!CreateMediaContentAnswer(
1712          static_cast<const DataContentDescription*>(
1713              data_content->description),
1714          options,
1715          data_codecs_,
1716          sdes_policy,
1717          GetCryptos(GetFirstDataContentDescription(current_description)),
1718          RtpHeaderExtensions(),
1719          current_streams,
1720          add_legacy_,
1721          bundle_enabled,
1722          data_answer.get())) {
1723    return false;  // Fails the session setup.
1724  }
1725
1726  bool rejected = !options.has_data() || data_content->rejected ||
1727      !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
1728                                data_answer->protocol(),
1729                                data_transport->secure());
1730  if (!rejected) {
1731    data_answer->set_bandwidth(options.data_bandwidth);
1732    if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1733                            answer)) {
1734      return false;
1735    }
1736  } else {
1737    // RFC 3264
1738    // The answer MUST contain the same number of m-lines as the offer.
1739    LOG(LS_INFO) << "Data is not supported in the answer.";
1740  }
1741  answer->AddContent(data_content->name, data_content->type, rejected,
1742                     data_answer.release());
1743  return true;
1744}
1745
1746bool IsMediaContent(const ContentInfo* content) {
1747  return (content &&
1748          (content->type == NS_JINGLE_RTP ||
1749           content->type == NS_JINGLE_DRAFT_SCTP));
1750}
1751
1752bool IsAudioContent(const ContentInfo* content) {
1753  return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
1754}
1755
1756bool IsVideoContent(const ContentInfo* content) {
1757  return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
1758}
1759
1760bool IsDataContent(const ContentInfo* content) {
1761  return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
1762}
1763
1764static const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
1765                                               MediaType media_type) {
1766  for (ContentInfos::const_iterator content = contents.begin();
1767       content != contents.end(); content++) {
1768    if (IsMediaContentOfType(&*content, media_type)) {
1769      return &*content;
1770    }
1771  }
1772  return NULL;
1773}
1774
1775const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
1776  return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
1777}
1778
1779const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
1780  return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
1781}
1782
1783const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
1784  return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
1785}
1786
1787static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
1788                                               MediaType media_type) {
1789  if (sdesc == NULL)
1790    return NULL;
1791
1792  return GetFirstMediaContent(sdesc->contents(), media_type);
1793}
1794
1795const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
1796  return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
1797}
1798
1799const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
1800  return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
1801}
1802
1803const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
1804  return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
1805}
1806
1807const MediaContentDescription* GetFirstMediaContentDescription(
1808    const SessionDescription* sdesc, MediaType media_type) {
1809  const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
1810  const ContentDescription* description = content ? content->description : NULL;
1811  return static_cast<const MediaContentDescription*>(description);
1812}
1813
1814const AudioContentDescription* GetFirstAudioContentDescription(
1815    const SessionDescription* sdesc) {
1816  return static_cast<const AudioContentDescription*>(
1817      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
1818}
1819
1820const VideoContentDescription* GetFirstVideoContentDescription(
1821    const SessionDescription* sdesc) {
1822  return static_cast<const VideoContentDescription*>(
1823      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
1824}
1825
1826const DataContentDescription* GetFirstDataContentDescription(
1827    const SessionDescription* sdesc) {
1828  return static_cast<const DataContentDescription*>(
1829      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
1830}
1831
1832bool GetMediaChannelNameFromComponent(
1833    int component, MediaType media_type, std::string* channel_name) {
1834  if (media_type == MEDIA_TYPE_AUDIO) {
1835    if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1836      *channel_name = GICE_CHANNEL_NAME_RTP;
1837      return true;
1838    } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1839      *channel_name = GICE_CHANNEL_NAME_RTCP;
1840      return true;
1841    }
1842  } else if (media_type == MEDIA_TYPE_VIDEO) {
1843    if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1844      *channel_name = GICE_CHANNEL_NAME_VIDEO_RTP;
1845      return true;
1846    } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1847      *channel_name = GICE_CHANNEL_NAME_VIDEO_RTCP;
1848      return true;
1849    }
1850  } else if (media_type == MEDIA_TYPE_DATA) {
1851    if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1852      *channel_name = GICE_CHANNEL_NAME_DATA_RTP;
1853      return true;
1854    } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1855      *channel_name = GICE_CHANNEL_NAME_DATA_RTCP;
1856      return true;
1857    }
1858  }
1859
1860  return false;
1861}
1862
1863bool GetMediaComponentFromChannelName(
1864    const std::string& channel_name, int* component) {
1865  if (channel_name == GICE_CHANNEL_NAME_RTP ||
1866      channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
1867      channel_name == GICE_CHANNEL_NAME_DATA_RTP) {
1868    *component = ICE_CANDIDATE_COMPONENT_RTP;
1869    return true;
1870  } else if (channel_name == GICE_CHANNEL_NAME_RTCP ||
1871             channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP ||
1872             channel_name == GICE_CHANNEL_NAME_DATA_RTP) {
1873    *component = ICE_CANDIDATE_COMPONENT_RTCP;
1874    return true;
1875  }
1876
1877  return false;
1878}
1879
1880bool GetMediaTypeFromChannelName(
1881    const std::string& channel_name, MediaType* media_type) {
1882  if (channel_name == GICE_CHANNEL_NAME_RTP ||
1883      channel_name == GICE_CHANNEL_NAME_RTCP) {
1884    *media_type = MEDIA_TYPE_AUDIO;
1885    return true;
1886  } else if (channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
1887             channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP) {
1888    *media_type = MEDIA_TYPE_VIDEO;
1889    return true;
1890  } else if (channel_name == GICE_CHANNEL_NAME_DATA_RTP ||
1891             channel_name == GICE_CHANNEL_NAME_DATA_RTCP) {
1892    *media_type = MEDIA_TYPE_DATA;
1893    return true;
1894  }
1895
1896  return false;
1897}
1898
1899}  // namespace cricket
1900