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 <string>
29
30#include "talk/session/media/mediasessionclient.h"
31
32#include "talk/media/base/capturemanager.h"
33#include "talk/media/base/cryptoparams.h"
34#include "talk/media/sctp/sctpdataengine.h"
35#include "talk/p2p/base/constants.h"
36#include "talk/p2p/base/parsing.h"
37#include "talk/session/media/mediamessages.h"
38#include "talk/session/media/srtpfilter.h"
39#include "webrtc/libjingle/xmllite/qname.h"
40#include "webrtc/libjingle/xmllite/xmlconstants.h"
41#include "talk/xmpp/constants.h"
42#include "webrtc/base/helpers.h"
43#include "webrtc/base/logging.h"
44#include "webrtc/base/stringencode.h"
45#include "webrtc/base/stringutils.h"
46
47namespace cricket {
48
49#if !defined(DISABLE_MEDIA_ENGINE_FACTORY)
50MediaSessionClient::MediaSessionClient(
51    const buzz::Jid& jid, SessionManager *manager)
52    : jid_(jid),
53      session_manager_(manager),
54      focus_call_(NULL),
55      channel_manager_(new ChannelManager(session_manager_->worker_thread())),
56      desc_factory_(channel_manager_,
57          session_manager_->transport_desc_factory()),
58      multisession_enabled_(false) {
59  Construct();
60}
61#endif
62
63MediaSessionClient::MediaSessionClient(
64    const buzz::Jid& jid, SessionManager *manager,
65    MediaEngineInterface* media_engine,
66    DataEngineInterface* data_media_engine,
67    DeviceManagerInterface* device_manager)
68    : jid_(jid),
69      session_manager_(manager),
70      focus_call_(NULL),
71      channel_manager_(new ChannelManager(
72          media_engine, data_media_engine,
73          device_manager, new CaptureManager(),
74          session_manager_->worker_thread())),
75      desc_factory_(channel_manager_,
76                    session_manager_->transport_desc_factory()),
77      multisession_enabled_(false) {
78  Construct();
79}
80
81void MediaSessionClient::Construct() {
82  // Register ourselves as the handler of audio and video sessions.
83  session_manager_->AddClient(NS_JINGLE_RTP, this);
84  // Forward device notifications.
85  SignalDevicesChange.repeat(channel_manager_->SignalDevicesChange);
86  // Bring up the channel manager.
87  // In previous versions of ChannelManager, this was done automatically
88  // in the constructor.
89  channel_manager_->Init();
90}
91
92MediaSessionClient::~MediaSessionClient() {
93  // Destroy all calls
94  std::map<uint32, Call *>::iterator it;
95  while (calls_.begin() != calls_.end()) {
96    std::map<uint32, Call *>::iterator it = calls_.begin();
97    DestroyCall((*it).second);
98  }
99
100  // Delete channel manager. This will wait for the channels to exit
101  delete channel_manager_;
102
103  // Remove ourselves from the client map.
104  session_manager_->RemoveClient(NS_JINGLE_RTP);
105}
106
107Call *MediaSessionClient::CreateCall() {
108  Call *call = new Call(this);
109  calls_[call->id()] = call;
110  SignalCallCreate(call);
111  return call;
112}
113
114void MediaSessionClient::OnSessionCreate(Session *session,
115                                         bool received_initiate) {
116  if (received_initiate) {
117    session->SignalState.connect(this, &MediaSessionClient::OnSessionState);
118  }
119}
120
121void MediaSessionClient::OnSessionState(BaseSession* base_session,
122                                        BaseSession::State state) {
123  // MediaSessionClient can only be used with a Session*, so it's
124  // safe to cast here.
125  Session* session = static_cast<Session*>(base_session);
126
127  if (state == Session::STATE_RECEIVEDINITIATE) {
128    // The creation of the call must happen after the session has
129    // processed the initiate message because we need the
130    // remote_description to know what content names to use in the
131    // call.
132
133    // If our accept would have no codecs, then we must reject this call.
134    const SessionDescription* offer = session->remote_description();
135    const SessionDescription* accept = CreateAnswer(offer, CallOptions());
136    const ContentInfo* audio_content = GetFirstAudioContent(accept);
137    bool audio_rejected = (!audio_content) ? true : audio_content->rejected;
138    const AudioContentDescription* audio_desc = (!audio_content) ? NULL :
139        static_cast<const AudioContentDescription*>(audio_content->description);
140
141    // For some reason, we need a call even if we reject. So, either find a
142    // matching call or create a new one.
143    // The matching of existing calls is used to support the multi-session mode
144    // required for p2p handoffs: ie. once a MUC call is established, a new
145    // session may be established for the same call but is direct between the
146    // clients. To indicate that this is the case, the initiator of the incoming
147    // session is set to be the same as the remote name of the MUC for the
148    // existing session, thus the client can know that this is a new session for
149    // the existing call, rather than a whole new call.
150    Call* call = NULL;
151    if (multisession_enabled_) {
152      call = FindCallByRemoteName(session->initiator_name());
153    }
154
155    if (call == NULL) {
156      // Could not find a matching call, so create a new one.
157      call = CreateCall();
158    }
159
160    session_map_[session->id()] = call;
161    call->IncomingSession(session, offer);
162
163    if (audio_rejected || !audio_desc || audio_desc->codecs().size() == 0) {
164      session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
165    }
166    delete accept;
167  }
168}
169
170void MediaSessionClient::DestroyCall(Call *call) {
171  // Change focus away, signal destruction
172
173  if (call == focus_call_)
174    SetFocus(NULL);
175  SignalCallDestroy(call);
176
177  // Remove it from calls_ map and delete
178
179  std::map<uint32, Call *>::iterator it = calls_.find(call->id());
180  if (it != calls_.end())
181    calls_.erase(it);
182
183  delete call;
184}
185
186void MediaSessionClient::OnSessionDestroy(Session *session) {
187  // Find the call this session is in, remove it
188  SessionMap::iterator it = session_map_.find(session->id());
189  ASSERT(it != session_map_.end());
190  if (it != session_map_.end()) {
191    Call *call = (*it).second;
192    session_map_.erase(it);
193    call->RemoveSession(session);
194  }
195}
196
197Call *MediaSessionClient::GetFocus() {
198  return focus_call_;
199}
200
201void MediaSessionClient::SetFocus(Call *call) {
202  Call *old_focus_call = focus_call_;
203  if (focus_call_ != call) {
204    if (focus_call_ != NULL)
205      focus_call_->EnableChannels(false);
206    focus_call_ = call;
207    if (focus_call_ != NULL)
208      focus_call_->EnableChannels(true);
209    SignalFocus(focus_call_, old_focus_call);
210  }
211}
212
213void MediaSessionClient::JoinCalls(Call *call_to_join, Call *call) {
214  // Move all sessions from call to call_to_join, delete call.
215  // If call_to_join has focus, added sessions should have enabled channels.
216
217  if (focus_call_ == call)
218    SetFocus(NULL);
219  call_to_join->Join(call, focus_call_ == call_to_join);
220  DestroyCall(call);
221}
222
223Session *MediaSessionClient::CreateSession(Call *call) {
224  std::string id;
225  return CreateSession(id, call);
226}
227
228Session *MediaSessionClient::CreateSession(const std::string& id, Call* call) {
229  const std::string& type = NS_JINGLE_RTP;
230  Session *session = session_manager_->CreateSession(id, jid().Str(), type);
231  session_map_[session->id()] = call;
232  return session;
233}
234
235Call *MediaSessionClient::FindCallByRemoteName(const std::string &remote_name) {
236  SessionMap::const_iterator call;
237  for (call = session_map_.begin(); call != session_map_.end(); ++call) {
238    std::vector<Session *> sessions = call->second->sessions();
239    std::vector<Session *>::const_iterator session;
240    for (session = sessions.begin(); session != sessions.end(); ++session) {
241      if (remote_name == (*session)->remote_name()) {
242        return call->second;
243      }
244    }
245  }
246
247  return NULL;
248}
249
250// TODO(pthatcher): Move all of the parsing and writing functions into
251// mediamessages.cc, with unit tests.
252bool ParseGingleAudioCodec(const buzz::XmlElement* element, AudioCodec* out) {
253  int id = GetXmlAttr(element, QN_ID, -1);
254  if (id < 0)
255    return false;
256
257  std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
258  int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0);
259  int bitrate = GetXmlAttr(element, QN_BITRATE, 0);
260  int channels = GetXmlAttr(element, QN_CHANNELS, 1);
261  *out = AudioCodec(id, name, clockrate, bitrate, channels, 0);
262  return true;
263}
264
265bool ParseGingleVideoCodec(const buzz::XmlElement* element, VideoCodec* out) {
266  int id = GetXmlAttr(element, QN_ID, -1);
267  if (id < 0)
268    return false;
269
270  std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
271  int width = GetXmlAttr(element, QN_WIDTH, 0);
272  int height = GetXmlAttr(element, QN_HEIGHT, 0);
273  int framerate = GetXmlAttr(element, QN_FRAMERATE, 0);
274
275  *out = VideoCodec(id, name, width, height, framerate, 0);
276  return true;
277}
278
279// Parses an ssrc string as a legacy stream.  If it fails, returns
280// false and fills an error message.
281bool ParseSsrcAsLegacyStream(const std::string& ssrc_str,
282                             std::vector<StreamParams>* streams,
283                             ParseError* error) {
284  if (!ssrc_str.empty()) {
285    uint32 ssrc;
286    if (!rtc::FromString(ssrc_str, &ssrc)) {
287      return BadParse("Missing or invalid ssrc.", error);
288    }
289
290    streams->push_back(StreamParams::CreateLegacy(ssrc));
291  }
292  return true;
293}
294
295void ParseGingleSsrc(const buzz::XmlElement* parent_elem,
296                     const buzz::QName& name,
297                     MediaContentDescription* media) {
298  const buzz::XmlElement* ssrc_elem = parent_elem->FirstNamed(name);
299  if (ssrc_elem) {
300    ParseError error;
301    ParseSsrcAsLegacyStream(
302        ssrc_elem->BodyText(), &(media->mutable_streams()), &error);
303  }
304}
305
306bool ParseCryptoParams(const buzz::XmlElement* element,
307                       CryptoParams* out,
308                       ParseError* error) {
309  if (!element->HasAttr(QN_CRYPTO_SUITE)) {
310    return BadParse("crypto: crypto-suite attribute missing ", error);
311  } else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) {
312    return BadParse("crypto: key-params attribute missing ", error);
313  } else if (!element->HasAttr(QN_CRYPTO_TAG)) {
314    return BadParse("crypto: tag attribute missing ", error);
315  }
316
317  const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE);
318  const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS);
319  const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0);
320  const std::string& session_params =
321      element->Attr(QN_CRYPTO_SESSION_PARAMS);  // Optional.
322
323  *out = CryptoParams(tag, crypto_suite, key_params, session_params);
324  return true;
325}
326
327
328// Parse the first encryption element found with a matching 'usage'
329// element.
330// <usage/> is specific to Gingle. In Jingle, <crypto/> is already
331// scoped to a content.
332// Return false if there was an encryption element and it could not be
333// parsed.
334bool ParseGingleEncryption(const buzz::XmlElement* desc,
335                           const buzz::QName& usage,
336                           MediaContentDescription* media,
337                           ParseError* error) {
338  for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION);
339       encryption != NULL;
340       encryption = encryption->NextNamed(QN_ENCRYPTION)) {
341    if (encryption->FirstNamed(usage) != NULL) {
342      if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) {
343        media->set_crypto_required(CT_SDES);
344      }
345      for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
346           crypto != NULL;
347           crypto = crypto->NextNamed(QN_CRYPTO)) {
348        CryptoParams params;
349        if (!ParseCryptoParams(crypto, &params, error)) {
350          return false;
351        }
352        media->AddCrypto(params);
353      }
354      break;
355    }
356  }
357  return true;
358}
359
360void ParseBandwidth(const buzz::XmlElement* parent_elem,
361                    MediaContentDescription* media) {
362  const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
363  int bandwidth_kbps = -1;
364  if (bw_elem && rtc::FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
365    if (bandwidth_kbps >= 0) {
366      media->set_bandwidth(bandwidth_kbps * 1000);
367    }
368  }
369}
370
371bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
372                             ContentDescription** content,
373                             ParseError* error) {
374  AudioContentDescription* audio = new AudioContentDescription();
375
376  int preference = kMaxPayloadId;
377  if (content_elem->FirstElement()) {
378    for (const buzz::XmlElement* codec_elem =
379             content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE);
380         codec_elem != NULL;
381         codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) {
382      AudioCodec codec;
383      if (ParseGingleAudioCodec(codec_elem, &codec)) {
384        codec.preference = preference--;
385        audio->AddCodec(codec);
386      }
387    }
388  } else {
389    // For backward compatibility, we can assume the other client is
390    // an old version of Talk if it has no audio payload types at all.
391    audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1));
392    audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0));
393  }
394
395  ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio);
396
397  if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
398                             audio, error)) {
399    return false;
400  }
401
402  *content = audio;
403  return true;
404}
405
406bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
407                             ContentDescription** content,
408                             ParseError* error) {
409  VideoContentDescription* video = new VideoContentDescription();
410
411  int preference = kMaxPayloadId;
412  for (const buzz::XmlElement* codec_elem =
413           content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE);
414       codec_elem != NULL;
415       codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) {
416    VideoCodec codec;
417    if (ParseGingleVideoCodec(codec_elem, &codec)) {
418      codec.preference = preference--;
419      video->AddCodec(codec);
420    }
421  }
422
423  ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
424  ParseBandwidth(content_elem, video);
425
426  if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
427                             video, error)) {
428    return false;
429  }
430
431  *content = video;
432  return true;
433}
434
435void ParsePayloadTypeParameters(const buzz::XmlElement* element,
436                                std::map<std::string, std::string>* paramap) {
437  for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
438       param != NULL; param = param->NextNamed(QN_PARAMETER)) {
439    std::string name  = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
440                                   buzz::STR_EMPTY);
441    std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
442                                   buzz::STR_EMPTY);
443    if (!name.empty() && !value.empty()) {
444      paramap->insert(make_pair(name, value));
445    }
446  }
447}
448
449void ParseFeedbackParams(const buzz::XmlElement* element,
450                         FeedbackParams* params) {
451  for (const buzz::XmlElement* param = element->FirstNamed(QN_JINGLE_RTCP_FB);
452       param != NULL; param = param->NextNamed(QN_JINGLE_RTCP_FB)) {
453    std::string type = GetXmlAttr(param, QN_TYPE, buzz::STR_EMPTY);
454    std::string subtype = GetXmlAttr(param, QN_SUBTYPE, buzz::STR_EMPTY);
455    if (!type.empty()) {
456      params->Add(FeedbackParam(type, subtype));
457    }
458  }
459}
460
461void AddFeedbackParams(const FeedbackParams& additional_params,
462                       FeedbackParams* params) {
463  for (size_t i = 0; i < additional_params.params().size(); ++i) {
464    params->Add(additional_params.params()[i]);
465  }
466}
467
468int FindWithDefault(const std::map<std::string, std::string>& map,
469                    const std::string& key, const int def) {
470  std::map<std::string, std::string>::const_iterator iter = map.find(key);
471  return (iter == map.end()) ? def : atoi(iter->second.c_str());
472}
473
474
475// Parse the first encryption element found.
476// Return false if there was an encryption element and it could not be
477// parsed.
478bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
479                           MediaContentDescription* media,
480                           ParseError* error) {
481  const buzz::XmlElement* encryption =
482          content_elem->FirstNamed(QN_ENCRYPTION);
483  if (encryption == NULL) {
484      return true;
485  }
486
487  if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) {
488    media->set_crypto_required(CT_SDES);
489  }
490
491  for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
492       crypto != NULL;
493       crypto = crypto->NextNamed(QN_CRYPTO)) {
494    CryptoParams params;
495    if (!ParseCryptoParams(crypto, &params, error)) {
496      return false;
497    }
498    media->AddCrypto(params);
499  }
500  return true;
501}
502
503bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
504  int id = GetXmlAttr(elem, QN_ID, -1);
505  if (id < 0)
506    return false;
507
508  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
509  int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
510  int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
511
512  std::map<std::string, std::string> paramap;
513  ParsePayloadTypeParameters(elem, &paramap);
514  int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
515
516  *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
517  ParseFeedbackParams(elem, &codec->feedback_params);
518  return true;
519}
520
521bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
522  int id = GetXmlAttr(elem, QN_ID, -1);
523  if (id < 0)
524    return false;
525
526  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
527
528  std::map<std::string, std::string> paramap;
529  ParsePayloadTypeParameters(elem, &paramap);
530  int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
531  int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
532  int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
533
534  *codec = VideoCodec(id, name, width, height, framerate, 0);
535  codec->params = paramap;
536  ParseFeedbackParams(elem, &codec->feedback_params);
537  return true;
538}
539
540bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
541  int id = GetXmlAttr(elem, QN_ID, -1);
542  if (id < 0)
543    return false;
544
545  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
546
547  *codec = DataCodec(id, name, 0);
548  ParseFeedbackParams(elem, &codec->feedback_params);
549  return true;
550}
551
552bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem,
553                                    MediaContentDescription* media,
554                                    ParseError* error) {
555  if (HasJingleStreams(desc_elem)) {
556    if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) {
557      return false;
558    }
559  } else {
560    const std::string ssrc_str = desc_elem->Attr(QN_SSRC);
561    if (!ParseSsrcAsLegacyStream(
562            ssrc_str, &(media->mutable_streams()), error)) {
563      return false;
564    }
565  }
566  return true;
567}
568
569bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
570                             ContentDescription** content,
571                             ParseError* error) {
572  rtc::scoped_ptr<AudioContentDescription> audio(
573      new AudioContentDescription());
574
575  FeedbackParams content_feedback_params;
576  ParseFeedbackParams(content_elem, &content_feedback_params);
577
578  int preference = kMaxPayloadId;
579  for (const buzz::XmlElement* payload_elem =
580           content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
581      payload_elem != NULL;
582      payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
583    AudioCodec codec;
584    if (ParseJingleAudioCodec(payload_elem, &codec)) {
585      AddFeedbackParams(content_feedback_params, &codec.feedback_params);
586      codec.preference = preference--;
587      audio->AddCodec(codec);
588    }
589  }
590
591  if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio.get(), error)) {
592    return false;
593  }
594
595  if (!ParseJingleEncryption(content_elem, audio.get(), error)) {
596    return false;
597  }
598
599  audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
600
601  RtpHeaderExtensions hdrexts;
602  if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
603    return false;
604  }
605  audio->set_rtp_header_extensions(hdrexts);
606
607  *content = audio.release();
608  return true;
609}
610
611bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
612                             ContentDescription** content,
613                             ParseError* error) {
614  rtc::scoped_ptr<VideoContentDescription> video(
615      new VideoContentDescription());
616
617  FeedbackParams content_feedback_params;
618  ParseFeedbackParams(content_elem, &content_feedback_params);
619
620  int preference = kMaxPayloadId;
621  for (const buzz::XmlElement* payload_elem =
622           content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
623      payload_elem != NULL;
624      payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
625    VideoCodec codec;
626    if (ParseJingleVideoCodec(payload_elem, &codec)) {
627      AddFeedbackParams(content_feedback_params, &codec.feedback_params);
628      codec.preference = preference--;
629      video->AddCodec(codec);
630    }
631  }
632
633  if (!ParseJingleStreamsOrLegacySsrc(content_elem, video.get(), error)) {
634    return false;
635  }
636  ParseBandwidth(content_elem, video.get());
637
638  if (!ParseJingleEncryption(content_elem, video.get(), error)) {
639    return false;
640  }
641
642  video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
643
644  RtpHeaderExtensions hdrexts;
645  if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
646    return false;
647  }
648  video->set_rtp_header_extensions(hdrexts);
649
650  *content = video.release();
651  return true;
652}
653
654bool ParseJingleSctpDataContent(const buzz::XmlElement* content_elem,
655                                ContentDescription** content,
656                                ParseError* error) {
657  rtc::scoped_ptr<DataContentDescription> data(
658      new DataContentDescription());
659  data->set_protocol(kMediaProtocolSctp);
660
661  for (const buzz::XmlElement* stream_elem =
662           content_elem->FirstNamed(QN_JINGLE_DRAFT_SCTP_STREAM);
663       stream_elem != NULL;
664       stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_SCTP_STREAM)) {
665    StreamParams stream;
666    stream.groupid = stream_elem->Attr(QN_NICK);
667    stream.id = stream_elem->Attr(QN_NAME);
668    uint32 sid;
669    if (!rtc::FromString(stream_elem->Attr(QN_SID), &sid)) {
670      return BadParse("Missing or invalid sid.", error);
671    }
672    if (sid > kMaxSctpSid) {
673      return BadParse("SID is greater than max value.", error);
674    }
675
676    stream.ssrcs.push_back(sid);
677    data->mutable_streams().push_back(stream);
678  }
679
680  *content = data.release();
681  return true;
682}
683
684bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem,
685                               ContentDescription** content,
686                               ParseError* error) {
687  DataContentDescription* data = new DataContentDescription();
688
689  FeedbackParams content_feedback_params;
690  ParseFeedbackParams(content_elem, &content_feedback_params);
691
692  int preference = kMaxPayloadId;
693  for (const buzz::XmlElement* payload_elem =
694           content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
695      payload_elem != NULL;
696      payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
697    DataCodec codec;
698    if (ParseJingleDataCodec(payload_elem, &codec)) {
699      AddFeedbackParams(content_feedback_params, &codec.feedback_params);
700      codec.preference = preference--;
701      data->AddCodec(codec);
702    }
703  }
704
705  if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) {
706    return false;
707  }
708  ParseBandwidth(content_elem, data);
709
710  if (!ParseJingleEncryption(content_elem, data, error)) {
711    return false;
712  }
713
714  data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
715
716  *content = data;
717  return true;
718}
719
720bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
721                                      const buzz::XmlElement* content_elem,
722                                      ContentDescription** content,
723                                      ParseError* error) {
724  if (protocol == PROTOCOL_GINGLE) {
725    const std::string& content_type = content_elem->Name().Namespace();
726    if (NS_GINGLE_AUDIO == content_type) {
727      return ParseGingleAudioContent(content_elem, content, error);
728    } else if (NS_GINGLE_VIDEO == content_type) {
729      return ParseGingleVideoContent(content_elem, content, error);
730    } else {
731      return BadParse("Unknown content type: " + content_type, error);
732    }
733  } else {
734    const std::string& content_type = content_elem->Name().Namespace();
735    // We use the XMLNS of the <description> element to determine if
736    // it's RTP or SCTP.
737    if (content_type == NS_JINGLE_DRAFT_SCTP) {
738      return ParseJingleSctpDataContent(content_elem, content, error);
739    }
740
741    std::string media;
742    if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
743      return false;
744
745    if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
746      return ParseJingleAudioContent(content_elem, content, error);
747    } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
748      return ParseJingleVideoContent(content_elem, content, error);
749    } else if (media == JINGLE_CONTENT_MEDIA_DATA) {
750      return ParseJingleRtpDataContent(content_elem, content, error);
751    } else {
752      return BadParse("Unknown media: " + media, error);
753    }
754  }
755}
756
757buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
758  buzz::XmlElement* payload_type =
759      new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
760  AddXmlAttr(payload_type, QN_ID, codec.id);
761  payload_type->AddAttr(QN_NAME, codec.name);
762  if (codec.clockrate > 0)
763    AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
764  if (codec.bitrate > 0)
765    AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
766  if (codec.channels > 1)
767    AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
768  return payload_type;
769}
770
771buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
772  buzz::XmlElement* payload_type =
773      new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
774  AddXmlAttr(payload_type, QN_ID, codec.id);
775  payload_type->AddAttr(QN_NAME, codec.name);
776  AddXmlAttr(payload_type, QN_WIDTH, codec.width);
777  AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
778  AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
779  return payload_type;
780}
781
782buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
783  buzz::XmlElement* elem = new buzz::XmlElement(name, true);
784  if (ssrc) {
785    SetXmlBody(elem, ssrc);
786  }
787  return elem;
788}
789
790buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
791  int kbps = bps / 1000;
792  buzz::XmlElement* elem = new buzz::XmlElement(name);
793  elem->AddAttr(buzz::QN_TYPE, "AS");
794  SetXmlBody(elem, kbps);
795  return elem;
796}
797
798// For Jingle, usage_qname is empty.
799buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
800                                             bool required) {
801  buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
802
803  if (required) {
804    encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
805  }
806
807  for (CryptoParamsVec::const_iterator i = cryptos.begin();
808       i != cryptos.end();
809       ++i) {
810    buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
811
812    AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
813    crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
814    crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
815    if (!i->session_params.empty()) {
816      crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
817    }
818    encryption_elem->AddElement(crypto_elem);
819  }
820  return encryption_elem;
821}
822
823buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
824                                             const buzz::QName& usage_qname,
825                                             bool required) {
826  buzz::XmlElement* encryption_elem =
827      CreateJingleEncryptionElem(cryptos, required);
828
829  if (required) {
830    encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
831  }
832
833  buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
834  encryption_elem->AddElement(usage_elem);
835
836  return encryption_elem;
837}
838
839buzz::XmlElement* CreateGingleAudioContentElem(
840    const AudioContentDescription* audio,
841    bool crypto_required) {
842  buzz::XmlElement* elem =
843      new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
844
845  for (AudioCodecs::const_iterator codec = audio->codecs().begin();
846       codec != audio->codecs().end(); ++codec) {
847    elem->AddElement(CreateGingleAudioCodecElem(*codec));
848  }
849  if (audio->has_ssrcs()) {
850    elem->AddElement(CreateGingleSsrcElem(
851        QN_GINGLE_AUDIO_SRCID, audio->first_ssrc()));
852  }
853
854  const CryptoParamsVec& cryptos = audio->cryptos();
855  if (!cryptos.empty()) {
856    elem->AddElement(CreateGingleEncryptionElem(cryptos,
857                                                QN_GINGLE_AUDIO_CRYPTO_USAGE,
858                                                crypto_required));
859  }
860  return elem;
861}
862
863buzz::XmlElement* CreateGingleVideoContentElem(
864    const VideoContentDescription* video,
865    bool crypto_required) {
866  buzz::XmlElement* elem =
867      new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
868
869  for (VideoCodecs::const_iterator codec = video->codecs().begin();
870       codec != video->codecs().end(); ++codec) {
871    elem->AddElement(CreateGingleVideoCodecElem(*codec));
872  }
873  if (video->has_ssrcs()) {
874    elem->AddElement(CreateGingleSsrcElem(
875        QN_GINGLE_VIDEO_SRCID, video->first_ssrc()));
876  }
877  if (video->bandwidth() != kAutoBandwidth) {
878    elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
879                                         video->bandwidth()));
880  }
881
882  const CryptoParamsVec& cryptos = video->cryptos();
883  if (!cryptos.empty()) {
884    elem->AddElement(CreateGingleEncryptionElem(cryptos,
885                                                QN_GINGLE_VIDEO_CRYPTO_USAGE,
886                                                crypto_required));
887  }
888
889  return elem;
890}
891
892template <class T>
893buzz::XmlElement* CreatePayloadTypeParameterElem(
894    const std::string& name, T value) {
895  buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
896
897  elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
898  AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
899
900  return elem;
901}
902
903void AddRtcpFeedbackElem(buzz::XmlElement* elem,
904                      const FeedbackParams& feedback_params) {
905  std::vector<FeedbackParam>::const_iterator it;
906  for (it = feedback_params.params().begin();
907       it != feedback_params.params().end(); ++it) {
908    buzz::XmlElement* fb_elem = new buzz::XmlElement(QN_JINGLE_RTCP_FB);
909    fb_elem->AddAttr(QN_TYPE, it->id());
910    fb_elem->AddAttr(QN_SUBTYPE, it->param());
911    elem->AddElement(fb_elem);
912  }
913}
914
915buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
916  buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
917
918  AddXmlAttr(elem, QN_ID, codec.id);
919  elem->AddAttr(QN_NAME, codec.name);
920  if (codec.clockrate > 0) {
921    AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
922  }
923  if (codec.bitrate > 0) {
924    elem->AddElement(CreatePayloadTypeParameterElem(
925        PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
926  }
927  if (codec.channels > 1) {
928    AddXmlAttr(elem, QN_CHANNELS, codec.channels);
929  }
930
931  AddRtcpFeedbackElem(elem, codec.feedback_params);
932
933  return elem;
934}
935
936buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
937  buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
938
939  AddXmlAttr(elem, QN_ID, codec.id);
940  elem->AddAttr(QN_NAME, codec.name);
941  elem->AddElement(CreatePayloadTypeParameterElem(
942      PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
943  elem->AddElement(CreatePayloadTypeParameterElem(
944      PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
945  elem->AddElement(CreatePayloadTypeParameterElem(
946      PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
947
948  AddRtcpFeedbackElem(elem, codec.feedback_params);
949
950  CodecParameterMap::const_iterator param_iter;
951  for (param_iter = codec.params.begin(); param_iter != codec.params.end();
952       ++param_iter) {
953    elem->AddElement(CreatePayloadTypeParameterElem(param_iter->first,
954                                                    param_iter->second));
955  }
956
957  return elem;
958}
959
960buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
961  buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
962
963  AddXmlAttr(elem, QN_ID, codec.id);
964  elem->AddAttr(QN_NAME, codec.name);
965
966  AddRtcpFeedbackElem(elem, codec.feedback_params);
967
968  return elem;
969}
970
971void WriteLegacyJingleSsrc(const MediaContentDescription* media,
972                           buzz::XmlElement* elem) {
973  if (media->has_ssrcs()) {
974    AddXmlAttr(elem, QN_SSRC, media->first_ssrc());
975  }
976}
977
978void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media,
979                                    buzz::XmlElement* desc_elem) {
980  if (!media->multistream()) {
981    WriteLegacyJingleSsrc(media, desc_elem);
982  } else {
983    WriteJingleStreams(media->streams(), desc_elem);
984  }
985}
986
987buzz::XmlElement* CreateJingleAudioContentElem(
988    const AudioContentDescription* audio, bool crypto_required) {
989  buzz::XmlElement* elem =
990      new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
991
992  elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
993  WriteJingleStreamsOrLegacySsrc(audio, elem);
994
995  for (AudioCodecs::const_iterator codec = audio->codecs().begin();
996       codec != audio->codecs().end(); ++codec) {
997    elem->AddElement(CreateJingleAudioCodecElem(*codec));
998  }
999
1000  const CryptoParamsVec& cryptos = audio->cryptos();
1001  if (!cryptos.empty()) {
1002    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1003  }
1004
1005  if (audio->rtcp_mux()) {
1006    elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1007  }
1008
1009  WriteJingleRtpHeaderExtensions(audio->rtp_header_extensions(), elem);
1010
1011  return elem;
1012}
1013
1014buzz::XmlElement* CreateJingleVideoContentElem(
1015    const VideoContentDescription* video, bool crypto_required) {
1016  buzz::XmlElement* elem =
1017      new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
1018
1019  elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
1020  WriteJingleStreamsOrLegacySsrc(video, elem);
1021
1022  for (VideoCodecs::const_iterator codec = video->codecs().begin();
1023       codec != video->codecs().end(); ++codec) {
1024    elem->AddElement(CreateJingleVideoCodecElem(*codec));
1025  }
1026
1027  const CryptoParamsVec& cryptos = video->cryptos();
1028  if (!cryptos.empty()) {
1029    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1030  }
1031
1032  if (video->rtcp_mux()) {
1033    elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1034  }
1035
1036  if (video->bandwidth() != kAutoBandwidth) {
1037    elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
1038                                         video->bandwidth()));
1039  }
1040
1041  WriteJingleRtpHeaderExtensions(video->rtp_header_extensions(), elem);
1042
1043  return elem;
1044}
1045
1046buzz::XmlElement* CreateJingleSctpDataContentElem(
1047    const DataContentDescription* data) {
1048  buzz::XmlElement* content_elem =
1049      new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_CONTENT, true);
1050  for (std::vector<StreamParams>::const_iterator
1051           stream = data->streams().begin();
1052       stream != data->streams().end(); ++stream) {
1053    buzz::XmlElement* stream_elem =
1054      new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_STREAM, false);
1055    AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream->groupid);
1056    AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream->id);
1057    if (!stream->ssrcs.empty()) {
1058      AddXmlAttr(stream_elem, QN_SID, stream->ssrcs[0]);
1059    }
1060    content_elem->AddElement(stream_elem);
1061  }
1062  return content_elem;;
1063}
1064
1065buzz::XmlElement* CreateJingleRtpDataContentElem(
1066    const DataContentDescription* data, bool crypto_required) {
1067
1068  buzz::XmlElement* elem =
1069      new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
1070
1071  elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA);
1072  WriteJingleStreamsOrLegacySsrc(data, elem);
1073
1074  for (DataCodecs::const_iterator codec = data->codecs().begin();
1075       codec != data->codecs().end(); ++codec) {
1076    elem->AddElement(CreateJingleDataCodecElem(*codec));
1077  }
1078
1079  const CryptoParamsVec& cryptos = data->cryptos();
1080  if (!cryptos.empty()) {
1081    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1082  }
1083
1084  if (data->rtcp_mux()) {
1085    elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1086  }
1087
1088  if (data->bandwidth() != kAutoBandwidth) {
1089    elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
1090                                         data->bandwidth()));
1091  }
1092
1093  return elem;
1094}
1095
1096bool IsSctp(const DataContentDescription* data) {
1097  return (data->protocol() == kMediaProtocolSctp ||
1098    data->protocol() == kMediaProtocolDtlsSctp);
1099}
1100
1101buzz::XmlElement* CreateJingleDataContentElem(
1102    const DataContentDescription* data, bool crypto_required) {
1103  if (IsSctp(data)) {
1104    return CreateJingleSctpDataContentElem(data);
1105  } else {
1106    return CreateJingleRtpDataContentElem(data, crypto_required);
1107  }
1108}
1109
1110bool MediaSessionClient::IsWritable(SignalingProtocol protocol,
1111                                    const ContentDescription* content) {
1112  const MediaContentDescription* media =
1113      static_cast<const MediaContentDescription*>(content);
1114  if (protocol == PROTOCOL_GINGLE &&
1115      media->type() == MEDIA_TYPE_DATA) {
1116    return false;
1117  }
1118  return true;
1119}
1120
1121bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
1122                                      const ContentDescription* content,
1123                                      buzz::XmlElement** elem,
1124                                      WriteError* error) {
1125  const MediaContentDescription* media =
1126      static_cast<const MediaContentDescription*>(content);
1127  bool crypto_required = secure() == SEC_REQUIRED;
1128
1129  if (media->type() == MEDIA_TYPE_AUDIO) {
1130    const AudioContentDescription* audio =
1131        static_cast<const AudioContentDescription*>(media);
1132    if (protocol == PROTOCOL_GINGLE) {
1133      *elem = CreateGingleAudioContentElem(audio, crypto_required);
1134    } else {
1135      *elem = CreateJingleAudioContentElem(audio, crypto_required);
1136    }
1137  } else if (media->type() == MEDIA_TYPE_VIDEO) {
1138    const VideoContentDescription* video =
1139        static_cast<const VideoContentDescription*>(media);
1140    if (protocol == PROTOCOL_GINGLE) {
1141      *elem = CreateGingleVideoContentElem(video, crypto_required);
1142    } else {
1143      *elem = CreateJingleVideoContentElem(video, crypto_required);
1144    }
1145  } else if (media->type() == MEDIA_TYPE_DATA) {
1146    const DataContentDescription* data =
1147        static_cast<const DataContentDescription*>(media);
1148    if (protocol == PROTOCOL_GINGLE) {
1149      return BadWrite("Data channel not supported with Gingle.", error);
1150    } else {
1151      *elem = CreateJingleDataContentElem(data, crypto_required);
1152    }
1153  } else {
1154    return BadWrite("Unknown content type: " +
1155                    rtc::ToString<int>(media->type()), error);
1156  }
1157
1158  return true;
1159}
1160
1161}  // namespace cricket
1162