1/*
2 * libjingle
3 * Copyright 2004--2005, 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 <string>
29
30#include "talk/session/phone/mediasessionclient.h"
31
32#include "talk/base/helpers.h"
33#include "talk/base/logging.h"
34#include "talk/base/stringutils.h"
35#include "talk/base/stringencode.h"
36#include "talk/p2p/base/constants.h"
37#include "talk/p2p/base/parsing.h"
38#include "talk/session/phone/cryptoparams.h"
39#include "talk/session/phone/srtpfilter.h"
40#include "talk/xmpp/constants.h"
41#include "talk/xmllite/qname.h"
42#include "talk/xmllite/xmlconstants.h"
43
44using namespace talk_base;
45
46namespace {
47const std::string kInline = "inline:";
48}
49
50namespace cricket {
51
52typedef std::vector<CryptoParams> CryptoParamsVec;
53
54MediaSessionClient::MediaSessionClient(
55    const buzz::Jid& jid, SessionManager *manager)
56    : jid_(jid), session_manager_(manager), focus_call_(NULL),
57      channel_manager_(new ChannelManager(session_manager_->worker_thread())),
58      secure_(SEC_DISABLED) {
59  Construct();
60}
61
62MediaSessionClient::MediaSessionClient(
63    const buzz::Jid& jid, SessionManager *manager,
64    MediaEngine* media_engine, DeviceManager* device_manager)
65    : jid_(jid), session_manager_(manager), focus_call_(NULL),
66      channel_manager_(new ChannelManager(
67          media_engine, device_manager, session_manager_->worker_thread())),
68      secure_(SEC_DISABLED) {
69  Construct();
70}
71
72
73void MediaSessionClient::Construct() {
74  // Register ourselves as the handler of phone and video sessions.
75  session_manager_->AddClient(NS_JINGLE_RTP, this);
76  // Forward device notifications.
77  SignalDevicesChange.repeat(channel_manager_->SignalDevicesChange);
78  // Bring up the channel manager.
79  // In previous versions of ChannelManager, this was done automatically
80  // in the constructor.
81  channel_manager_->Init();
82}
83
84MediaSessionClient::~MediaSessionClient() {
85  // Destroy all calls
86  std::map<uint32, Call *>::iterator it;
87  while (calls_.begin() != calls_.end()) {
88    std::map<uint32, Call *>::iterator it = calls_.begin();
89    DestroyCall((*it).second);
90  }
91
92  // Delete channel manager. This will wait for the channels to exit
93  delete channel_manager_;
94
95  // Remove ourselves from the client map.
96  session_manager_->RemoveClient(NS_JINGLE_RTP);
97}
98
99bool CreateCryptoParams(int tag, const std::string& cipher, CryptoParams *out) {
100  std::string key;
101  key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
102
103  if (!CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
104    return false;
105  }
106  out->tag = tag;
107  out->cipher_suite = cipher;
108  out->key_params = kInline + key;
109  return true;
110}
111
112bool AddCryptoParams(const std::string& cipher_suite, CryptoParamsVec *out) {
113  int size = out->size();
114
115  out->resize(size + 1);
116  return CreateCryptoParams(size, cipher_suite, &out->at(size));
117}
118
119// For audio, HMAC 32 is prefered because of the low overhead.
120bool GetSupportedAudioCryptos(CryptoParamsVec* cryptos) {
121#ifdef HAVE_SRTP
122  return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_32, cryptos) &&
123      AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
124#else
125  return false;
126#endif
127}
128
129bool GetSupportedVideoCryptos(CryptoParamsVec* cryptos) {
130#ifdef HAVE_SRTP
131  return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
132#else
133  return false;
134#endif
135}
136
137SessionDescription* MediaSessionClient::CreateOffer(
138    const CallOptions& options) {
139  SessionDescription* offer = new SessionDescription();
140  AudioContentDescription* audio = new AudioContentDescription();
141
142
143  AudioCodecs audio_codecs;
144  channel_manager_->GetSupportedAudioCodecs(&audio_codecs);
145  for (AudioCodecs::const_iterator codec = audio_codecs.begin();
146       codec != audio_codecs.end(); ++codec) {
147    audio->AddCodec(*codec);
148  }
149  if (options.is_muc) {
150    audio->set_ssrc(0);
151  }
152  audio->SortCodecs();
153
154  if (secure() != SEC_DISABLED) {
155    CryptoParamsVec audio_cryptos;
156    if (GetSupportedAudioCryptos(&audio_cryptos)) {
157      for (CryptoParamsVec::const_iterator crypto = audio_cryptos.begin();
158           crypto != audio_cryptos.end(); ++crypto) {
159        audio->AddCrypto(*crypto);
160      }
161    }
162    if (secure() == SEC_REQUIRED) {
163      if (audio->cryptos().empty()) {
164        return NULL;  // Abort, crypto required but none found.
165      }
166      audio->set_crypto_required(true);
167    }
168  }
169
170  offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio);
171
172  // add video codecs, if this is a video call
173  if (options.is_video) {
174    VideoContentDescription* video = new VideoContentDescription();
175    VideoCodecs video_codecs;
176    channel_manager_->GetSupportedVideoCodecs(&video_codecs);
177    for (VideoCodecs::const_iterator codec = video_codecs.begin();
178         codec != video_codecs.end(); ++codec) {
179      video->AddCodec(*codec);
180    }
181    if (options.is_muc) {
182      video->set_ssrc(0);
183    }
184    video->set_bandwidth(options.video_bandwidth);
185    video->SortCodecs();
186
187    if (secure() != SEC_DISABLED) {
188      CryptoParamsVec video_cryptos;
189      if (GetSupportedVideoCryptos(&video_cryptos)) {
190        for (CryptoParamsVec::const_iterator crypto = video_cryptos.begin();
191             crypto != video_cryptos.end(); ++crypto) {
192          video->AddCrypto(*crypto);
193        }
194      }
195      if (secure() == SEC_REQUIRED) {
196        if (video->cryptos().empty()) {
197          return NULL;  // Abort, crypto required but none found.
198        }
199        video->set_crypto_required(true);
200      }
201    }
202
203    offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video);
204  }
205
206  return offer;
207}
208
209const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
210                                        MediaType media_type) {
211  if (sdesc == NULL)
212    return NULL;
213
214  const ContentInfos& contents = sdesc->contents();
215  for (ContentInfos::const_iterator content = contents.begin();
216       content != contents.end(); content++) {
217    if (content->type == NS_JINGLE_RTP) {
218      const MediaContentDescription* media =
219          static_cast<const MediaContentDescription*>(content->description);
220      if (media->type() == media_type) {
221        return &*content;
222      }
223    }
224  }
225  return NULL;
226}
227
228const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
229  return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
230}
231
232const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
233  return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
234}
235
236// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
237// tolerated because it is low overhead. Pick the crypto in the list
238// that is supported.
239bool SelectCrypto(const MediaContentDescription* offer, CryptoParams *crypto) {
240  bool audio = offer->type() == MEDIA_TYPE_AUDIO;
241  const CryptoParamsVec& cryptos = offer->cryptos();
242
243  for (CryptoParamsVec::const_iterator i = cryptos.begin();
244       i != cryptos.end(); ++i) {
245    if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
246        (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio)) {
247      return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
248    }
249  }
250  return false;
251}
252
253SessionDescription* MediaSessionClient::CreateAnswer(
254    const SessionDescription* offer, const CallOptions& options) {
255  // The answer contains the intersection of the codecs in the offer with the
256  // codecs we support, ordered by our local preference. As indicated by
257  // XEP-0167, we retain the same payload ids from the offer in the answer.
258  SessionDescription* accept = new SessionDescription();
259
260  const ContentInfo* audio_content = GetFirstAudioContent(offer);
261  if (audio_content) {
262    const AudioContentDescription* audio_offer =
263        static_cast<const AudioContentDescription*>(audio_content->description);
264    AudioContentDescription* audio_accept = new AudioContentDescription();
265    AudioCodecs audio_codecs;
266    channel_manager_->GetSupportedAudioCodecs(&audio_codecs);
267    for (AudioCodecs::const_iterator ours = audio_codecs.begin();
268        ours != audio_codecs.end(); ++ours) {
269      for (AudioCodecs::const_iterator theirs = audio_offer->codecs().begin();
270          theirs != audio_offer->codecs().end(); ++theirs) {
271        if (ours->Matches(*theirs)) {
272          AudioCodec negotiated(*ours);
273          negotiated.id = theirs->id;
274          audio_accept->AddCodec(negotiated);
275        }
276      }
277    }
278
279    audio_accept->SortCodecs();
280
281    if (secure() != SEC_DISABLED) {
282      CryptoParams crypto;
283
284      if (SelectCrypto(audio_offer, &crypto)) {
285        audio_accept->AddCrypto(crypto);
286      }
287    }
288
289    if (audio_accept->cryptos().empty() &&
290        (audio_offer->crypto_required() || secure() == SEC_REQUIRED)) {
291      return NULL;  // Fails the session setup.
292    }
293    accept->AddContent(audio_content->name, audio_content->type, audio_accept);
294  }
295
296  const ContentInfo* video_content = GetFirstVideoContent(offer);
297  if (video_content) {
298    const VideoContentDescription* video_offer =
299        static_cast<const VideoContentDescription*>(video_content->description);
300    VideoContentDescription* video_accept = new VideoContentDescription();
301    VideoCodecs video_codecs;
302    channel_manager_->GetSupportedVideoCodecs(&video_codecs);
303    for (VideoCodecs::const_iterator ours = video_codecs.begin();
304        ours != video_codecs.end(); ++ours) {
305      for (VideoCodecs::const_iterator theirs = video_offer->codecs().begin();
306          theirs != video_offer->codecs().end(); ++theirs) {
307        if (ours->Matches(*theirs)) {
308          VideoCodec negotiated(*ours);
309          negotiated.id = theirs->id;
310          video_accept->AddCodec(negotiated);
311        }
312      }
313    }
314
315    video_accept->set_bandwidth(options.video_bandwidth);
316    video_accept->SortCodecs();
317
318    if (secure() != SEC_DISABLED) {
319      CryptoParams crypto;
320
321      if (SelectCrypto(video_offer, &crypto)) {
322        video_accept->AddCrypto(crypto);
323      }
324    }
325
326    if (video_accept->cryptos().empty() &&
327        (video_offer->crypto_required() || secure() == SEC_REQUIRED)) {
328      return NULL;  // Fails the session setup.
329    }
330    accept->AddContent(video_content->name, video_content->type, video_accept);
331  }
332
333  return accept;
334}
335
336Call *MediaSessionClient::CreateCall() {
337  Call *call = new Call(this);
338  calls_[call->id()] = call;
339  SignalCallCreate(call);
340  return call;
341}
342
343void MediaSessionClient::OnSessionCreate(Session *session,
344                                         bool received_initiate) {
345  if (received_initiate) {
346    session->SignalState.connect(this, &MediaSessionClient::OnSessionState);
347  }
348}
349
350void MediaSessionClient::OnSessionState(BaseSession* base_session,
351                                        BaseSession::State state) {
352  // MediaSessionClient can only be used with a Session*, so it's
353  // safe to cast here.
354  Session* session = static_cast<Session*>(base_session);
355
356  if (state == Session::STATE_RECEIVEDINITIATE) {
357    // The creation of the call must happen after the session has
358    // processed the initiate message because we need the
359    // remote_description to know what content names to use in the
360    // call.
361
362    // If our accept would have no codecs, then we must reject this call.
363    const SessionDescription* offer = session->remote_description();
364    const SessionDescription* accept = CreateAnswer(offer, CallOptions());
365    const ContentInfo* audio_content = GetFirstAudioContent(accept);
366    const AudioContentDescription* audio_accept = (!audio_content) ? NULL :
367        static_cast<const AudioContentDescription*>(audio_content->description);
368
369    // For some reason, we need to create the call even when we
370    // reject.
371    Call *call = CreateCall();
372    session_map_[session->id()] = call;
373    call->IncomingSession(session, offer);
374
375    if (!audio_accept || audio_accept->codecs().size() == 0) {
376      session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
377    }
378    delete accept;
379  }
380}
381
382void MediaSessionClient::DestroyCall(Call *call) {
383  // Change focus away, signal destruction
384
385  if (call == focus_call_)
386    SetFocus(NULL);
387  SignalCallDestroy(call);
388
389  // Remove it from calls_ map and delete
390
391  std::map<uint32, Call *>::iterator it = calls_.find(call->id());
392  if (it != calls_.end())
393    calls_.erase(it);
394
395  delete call;
396}
397
398void MediaSessionClient::OnSessionDestroy(Session *session) {
399  // Find the call this session is in, remove it
400
401  std::map<std::string, Call *>::iterator it = session_map_.find(session->id());
402  ASSERT(it != session_map_.end());
403  if (it != session_map_.end()) {
404    Call *call = (*it).second;
405    session_map_.erase(it);
406    call->RemoveSession(session);
407  }
408}
409
410Call *MediaSessionClient::GetFocus() {
411  return focus_call_;
412}
413
414void MediaSessionClient::SetFocus(Call *call) {
415  Call *old_focus_call = focus_call_;
416  if (focus_call_ != call) {
417    if (focus_call_ != NULL)
418      focus_call_->EnableChannels(false);
419    focus_call_ = call;
420    if (focus_call_ != NULL)
421      focus_call_->EnableChannels(true);
422    SignalFocus(focus_call_, old_focus_call);
423  }
424}
425
426void MediaSessionClient::JoinCalls(Call *call_to_join, Call *call) {
427  // Move all sessions from call to call_to_join, delete call.
428  // If call_to_join has focus, added sessions should have enabled channels.
429
430  if (focus_call_ == call)
431    SetFocus(NULL);
432  call_to_join->Join(call, focus_call_ == call_to_join);
433  DestroyCall(call);
434}
435
436Session *MediaSessionClient::CreateSession(Call *call) {
437  const std::string& type = NS_JINGLE_RTP;
438  Session *session = session_manager_->CreateSession(jid().Str(), type);
439  session_map_[session->id()] = call;
440  return session;
441}
442
443bool ParseGingleAudioCodec(const buzz::XmlElement* element, AudioCodec* out) {
444  int id = GetXmlAttr(element, QN_ID, -1);
445  if (id < 0)
446    return false;
447
448  std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
449  int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0);
450  int bitrate = GetXmlAttr(element, QN_BITRATE, 0);
451  int channels = GetXmlAttr(element, QN_CHANNELS, 1);
452  *out = AudioCodec(id, name, clockrate, bitrate, channels, 0);
453  return true;
454}
455
456bool ParseGingleVideoCodec(const buzz::XmlElement* element, VideoCodec* out) {
457  int id = GetXmlAttr(element, QN_ID, -1);
458  if (id < 0)
459    return false;
460
461  std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
462  int width = GetXmlAttr(element, QN_WIDTH, 0);
463  int height = GetXmlAttr(element, QN_HEIGHT, 0);
464  int framerate = GetXmlAttr(element, QN_FRAMERATE, 0);
465
466  *out = VideoCodec(id, name, width, height, framerate, 0);
467  return true;
468}
469
470void ParseGingleSsrc(const buzz::XmlElement* parent_elem,
471                     const buzz::QName& name,
472                     MediaContentDescription* content) {
473  const buzz::XmlElement* ssrc_elem = parent_elem->FirstNamed(name);
474  if (ssrc_elem) {
475    content->set_ssrc(strtoul(ssrc_elem->BodyText().c_str(), NULL, 10));
476  }
477}
478
479bool ParseCryptoParams(const buzz::XmlElement* element,
480                       CryptoParams* out,
481                       ParseError* error) {
482  if (!element->HasAttr(QN_CRYPTO_SUITE)) {
483    return BadParse("crypto: crypto-suite attribute missing ", error);
484  } else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) {
485    return BadParse("crypto: key-params attribute missing ", error);
486  } else if (!element->HasAttr(QN_CRYPTO_TAG)) {
487    return BadParse("crypto: tag attribute missing ", error);
488  }
489
490  const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE);
491  const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS);
492  const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0);
493  const std::string& session_params =
494      element->Attr(QN_CRYPTO_SESSION_PARAMS);  // Optional.
495
496  *out = CryptoParams(tag, crypto_suite, key_params, session_params);
497  return true;
498}
499
500
501// Parse the first encryption element found with a matching 'usage'
502// element.
503// <usage/> is specific to Gingle. In Jingle, <crypto/> is already
504// scoped to a content.
505// Return false if there was an encryption element and it could not be
506// parsed.
507bool ParseGingleEncryption(const buzz::XmlElement* desc,
508                           const buzz::QName& usage,
509                           MediaContentDescription* media,
510                           ParseError* error) {
511  for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION);
512       encryption != NULL;
513       encryption = encryption->NextNamed(QN_ENCRYPTION)) {
514    if (encryption->FirstNamed(usage) != NULL) {
515      media->set_crypto_required(
516          GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
517      for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
518           crypto != NULL;
519           crypto = crypto->NextNamed(QN_CRYPTO)) {
520        CryptoParams params;
521        if (!ParseCryptoParams(crypto, &params, error)) {
522          return false;
523        }
524        media->AddCrypto(params);
525      }
526      break;
527    }
528  }
529  return true;
530}
531
532void ParseBandwidth(const buzz::XmlElement* parent_elem,
533                    MediaContentDescription* media) {
534  const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
535  int bandwidth_kbps;
536  if (bw_elem && FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
537    if (bandwidth_kbps >= 0) {
538      media->set_bandwidth(bandwidth_kbps * 1000);
539    }
540  }
541}
542
543bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
544                             const ContentDescription** content,
545                             ParseError* error) {
546  AudioContentDescription* audio = new AudioContentDescription();
547
548  if (content_elem->FirstElement()) {
549    for (const buzz::XmlElement* codec_elem =
550             content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE);
551         codec_elem != NULL;
552         codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) {
553      AudioCodec codec;
554      if (ParseGingleAudioCodec(codec_elem, &codec)) {
555        audio->AddCodec(codec);
556      }
557    }
558  } else {
559    // For backward compatibility, we can assume the other client is
560    // an old version of Talk if it has no audio payload types at all.
561    audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1));
562    audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0));
563  }
564
565  ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio);
566
567  if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
568                             audio, error)) {
569    return false;
570  }
571
572  *content = audio;
573  return true;
574}
575
576bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
577                             const ContentDescription** content,
578                             ParseError* error) {
579  VideoContentDescription* video = new VideoContentDescription();
580
581  for (const buzz::XmlElement* codec_elem =
582           content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE);
583       codec_elem != NULL;
584       codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) {
585    VideoCodec codec;
586    if (ParseGingleVideoCodec(codec_elem, &codec)) {
587      video->AddCodec(codec);
588    }
589  }
590
591  ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
592  ParseBandwidth(content_elem, video);
593
594  if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
595                             video, error)) {
596    return false;
597  }
598
599  *content = video;
600  return true;
601}
602
603void ParsePayloadTypeParameters(const buzz::XmlElement* element,
604                                std::map<std::string, std::string>* paramap) {
605  for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
606       param != NULL; param = param->NextNamed(QN_PARAMETER)) {
607    std::string name  = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
608                                   buzz::STR_EMPTY);
609    std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
610                                   buzz::STR_EMPTY);
611    if (!name.empty() && !value.empty()) {
612      paramap->insert(make_pair(name, value));
613    }
614  }
615}
616
617int FindWithDefault(const std::map<std::string, std::string>& map,
618                    const std::string& key, const int def) {
619  std::map<std::string, std::string>::const_iterator iter = map.find(key);
620  return (iter == map.end()) ? def : atoi(iter->second.c_str());
621}
622
623
624// Parse the first encryption element found.
625// Return false if there was an encryption element and it could not be
626// parsed.
627bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
628                           MediaContentDescription* media,
629                           ParseError* error) {
630  const buzz::XmlElement* encryption =
631          content_elem->FirstNamed(QN_ENCRYPTION);
632  if (encryption == NULL) {
633      return true;
634  }
635
636  media->set_crypto_required(
637      GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
638
639  for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
640       crypto != NULL;
641       crypto = crypto->NextNamed(QN_CRYPTO)) {
642    CryptoParams params;
643    if (!ParseCryptoParams(crypto, &params, error)) {
644      return false;
645    }
646    media->AddCrypto(params);
647  }
648  return true;
649}
650
651bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
652  int id = GetXmlAttr(elem, QN_ID, -1);
653  if (id < 0)
654    return false;
655
656  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
657  int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
658  int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
659
660  std::map<std::string, std::string> paramap;
661  ParsePayloadTypeParameters(elem, &paramap);
662  int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
663
664  *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
665  return true;
666}
667
668bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
669  int id = GetXmlAttr(elem, QN_ID, -1);
670  if (id < 0)
671    return false;
672
673  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
674
675  std::map<std::string, std::string> paramap;
676  ParsePayloadTypeParameters(elem, &paramap);
677  int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
678  int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
679  int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
680
681  *codec = VideoCodec(id, name, width, height, framerate, 0);
682  return true;
683}
684
685bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
686                             const ContentDescription** content,
687                             ParseError* error) {
688  AudioContentDescription* audio = new AudioContentDescription();
689
690  for (const buzz::XmlElement* payload_elem =
691           content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
692      payload_elem != NULL;
693      payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
694    AudioCodec codec;
695    if (ParseJingleAudioCodec(payload_elem, &codec)) {
696      audio->AddCodec(codec);
697    }
698  }
699
700  if (!ParseJingleEncryption(content_elem, audio, error)) {
701    return false;
702  }
703  // TODO: Figure out how to integrate SSRC into Jingle.
704  *content = audio;
705  return true;
706}
707
708bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
709                             const ContentDescription** content,
710                             ParseError* error) {
711  VideoContentDescription* video = new VideoContentDescription();
712
713  for (const buzz::XmlElement* payload_elem =
714           content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
715      payload_elem != NULL;
716      payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
717    VideoCodec codec;
718    if (ParseJingleVideoCodec(payload_elem, &codec)) {
719      video->AddCodec(codec);
720    }
721  }
722
723  ParseBandwidth(content_elem, video);
724
725  if (!ParseJingleEncryption(content_elem, video, error)) {
726    return false;
727  }
728  // TODO: Figure out how to integrate SSRC into Jingle.
729  *content = video;
730  return true;
731}
732
733bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
734                                     const buzz::XmlElement* content_elem,
735                                     const ContentDescription** content,
736                                     ParseError* error) {
737  if (protocol == PROTOCOL_GINGLE) {
738    const std::string& content_type = content_elem->Name().Namespace();
739    if (NS_GINGLE_AUDIO == content_type) {
740      return ParseGingleAudioContent(content_elem, content, error);
741    } else if (NS_GINGLE_VIDEO == content_type) {
742      return ParseGingleVideoContent(content_elem, content, error);
743    } else {
744      return BadParse("Unknown content type: " + content_type, error);
745    }
746  } else {
747    std::string media;
748    if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
749      return false;
750
751    if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
752      return ParseJingleAudioContent(content_elem, content, error);
753    } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
754      return ParseJingleVideoContent(content_elem, content, error);
755    } else {
756      return BadParse("Unknown media: " + media, error);
757    }
758  }
759}
760
761buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
762  buzz::XmlElement* payload_type =
763      new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
764  AddXmlAttr(payload_type, QN_ID, codec.id);
765  payload_type->AddAttr(QN_NAME, codec.name);
766  if (codec.clockrate > 0)
767    AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
768  if (codec.bitrate > 0)
769    AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
770  if (codec.channels > 1)
771    AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
772  return payload_type;
773}
774
775buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
776  buzz::XmlElement* payload_type =
777      new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
778  AddXmlAttr(payload_type, QN_ID, codec.id);
779  payload_type->AddAttr(QN_NAME, codec.name);
780  AddXmlAttr(payload_type, QN_WIDTH, codec.width);
781  AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
782  AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
783  return payload_type;
784}
785
786buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
787  buzz::XmlElement* elem = new buzz::XmlElement(name, true);
788  if (ssrc) {
789    SetXmlBody(elem, ssrc);
790  }
791  return elem;
792}
793
794buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
795  int kbps = bps / 1000;
796  buzz::XmlElement* elem = new buzz::XmlElement(name);
797  elem->AddAttr(buzz::QN_TYPE, "AS");
798  SetXmlBody(elem, kbps);
799  return elem;
800}
801
802// For Jingle, usage_qname is empty.
803buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
804                                             bool required) {
805  buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
806
807  if (required) {
808    encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
809  }
810
811  for (CryptoParamsVec::const_iterator i = cryptos.begin();
812       i != cryptos.end();
813       ++i) {
814    buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
815
816    AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
817    crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
818    crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
819    if (!i->session_params.empty()) {
820      crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
821    }
822    encryption_elem->AddElement(crypto_elem);
823  }
824  return encryption_elem;
825}
826
827buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
828                                             const buzz::QName& usage_qname,
829                                             bool required) {
830  buzz::XmlElement* encryption_elem =
831      CreateJingleEncryptionElem(cryptos, required);
832
833  if (required) {
834    encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
835  }
836
837  buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
838  encryption_elem->AddElement(usage_elem);
839
840  return encryption_elem;
841}
842
843buzz::XmlElement* CreateGingleAudioContentElem(
844    const AudioContentDescription* audio,
845    bool crypto_required) {
846  buzz::XmlElement* elem =
847      new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
848
849  for (AudioCodecs::const_iterator codec = audio->codecs().begin();
850       codec != audio->codecs().end(); ++codec) {
851    elem->AddElement(CreateGingleAudioCodecElem(*codec));
852  }
853  if (audio->ssrc_set()) {
854    elem->AddElement(CreateGingleSsrcElem(
855        QN_GINGLE_AUDIO_SRCID, audio->ssrc()));
856  }
857
858  const CryptoParamsVec& cryptos = audio->cryptos();
859  if (!cryptos.empty()) {
860    elem->AddElement(CreateGingleEncryptionElem(cryptos,
861                                                QN_GINGLE_AUDIO_CRYPTO_USAGE,
862                                                crypto_required));
863  }
864
865
866  return elem;
867}
868
869buzz::XmlElement* CreateGingleVideoContentElem(
870    const VideoContentDescription* video,
871    bool crypto_required) {
872  buzz::XmlElement* elem =
873      new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
874
875  for (VideoCodecs::const_iterator codec = video->codecs().begin();
876       codec != video->codecs().end(); ++codec) {
877    elem->AddElement(CreateGingleVideoCodecElem(*codec));
878  }
879  if (video->ssrc_set()) {
880    elem->AddElement(CreateGingleSsrcElem(
881        QN_GINGLE_VIDEO_SRCID, video->ssrc()));
882  }
883  if (video->bandwidth() != kAutoBandwidth) {
884    elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
885                                         video->bandwidth()));
886  }
887
888  const CryptoParamsVec& cryptos = video->cryptos();
889  if (!cryptos.empty()) {
890    elem->AddElement(CreateGingleEncryptionElem(cryptos,
891                                                QN_GINGLE_VIDEO_CRYPTO_USAGE,
892                                                crypto_required));
893  }
894
895  return elem;
896}
897
898buzz::XmlElement* CreatePayloadTypeParameterElem(
899    const std::string& name, int value) {
900  buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
901
902  elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
903  AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
904
905  return elem;
906}
907
908buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
909  buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
910
911  AddXmlAttr(elem, QN_ID, codec.id);
912  elem->AddAttr(QN_NAME, codec.name);
913  if (codec.clockrate > 0) {
914    AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
915  }
916  if (codec.bitrate > 0) {
917    elem->AddElement(CreatePayloadTypeParameterElem(
918        PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
919  }
920  if (codec.channels > 1) {
921    AddXmlAttr(elem, QN_CHANNELS, codec.channels);
922  }
923
924  return elem;
925}
926
927buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
928  buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
929
930  AddXmlAttr(elem, QN_ID, codec.id);
931  elem->AddAttr(QN_NAME, codec.name);
932  elem->AddElement(CreatePayloadTypeParameterElem(
933      PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
934  elem->AddElement(CreatePayloadTypeParameterElem(
935      PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
936  elem->AddElement(CreatePayloadTypeParameterElem(
937      PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
938
939  return elem;
940}
941
942buzz::XmlElement* CreateJingleAudioContentElem(
943    const AudioContentDescription* audio, bool crypto_required) {
944  buzz::XmlElement* elem =
945      new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
946
947  elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
948
949  for (AudioCodecs::const_iterator codec = audio->codecs().begin();
950       codec != audio->codecs().end(); ++codec) {
951    elem->AddElement(CreateJingleAudioCodecElem(*codec));
952  }
953
954  const CryptoParamsVec& cryptos = audio->cryptos();
955  if (!cryptos.empty()) {
956    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
957  }
958
959  // TODO: Figure out how to integrate SSRC into Jingle.
960  return elem;
961}
962
963buzz::XmlElement* CreateJingleVideoContentElem(
964    const VideoContentDescription* video, bool crypto_required) {
965  buzz::XmlElement* elem =
966      new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
967
968  elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
969
970  for (VideoCodecs::const_iterator codec = video->codecs().begin();
971       codec != video->codecs().end(); ++codec) {
972    elem->AddElement(CreateJingleVideoCodecElem(*codec));
973  }
974
975  const CryptoParamsVec& cryptos = video->cryptos();
976  if (!cryptos.empty()) {
977    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
978  }
979
980  if (video->bandwidth() != kAutoBandwidth) {
981    elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
982                                         video->bandwidth()));
983  }
984
985  // TODO: Figure out how to integrate SSRC into Jingle.
986  return elem;
987}
988
989bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
990                                      const ContentDescription* content,
991                                      buzz::XmlElement** elem,
992                                      WriteError* error) {
993  const MediaContentDescription* media =
994      static_cast<const MediaContentDescription*>(content);
995  bool crypto_required = secure() == SEC_REQUIRED;
996
997  if (media->type() == MEDIA_TYPE_AUDIO) {
998    const AudioContentDescription* audio =
999        static_cast<const AudioContentDescription*>(media);
1000    if (protocol == PROTOCOL_GINGLE) {
1001      *elem = CreateGingleAudioContentElem(audio, crypto_required);
1002    } else {
1003      *elem = CreateJingleAudioContentElem(audio, crypto_required);
1004    }
1005  } else if (media->type() == MEDIA_TYPE_VIDEO) {
1006    const VideoContentDescription* video =
1007        static_cast<const VideoContentDescription*>(media);
1008    if (protocol == PROTOCOL_GINGLE) {
1009      *elem = CreateGingleVideoContentElem(video, crypto_required);
1010    } else {
1011      *elem = CreateJingleVideoContentElem(video, crypto_required);
1012    }
1013  } else {
1014    return BadWrite("Unknown content type: " + media->type(), error);
1015  }
1016
1017  return true;
1018}
1019
1020}  // namespace cricket
1021