Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/talk/session/media/mediasessionclient.cc @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

File size: 38.9 KB
Line 
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/base/helpers.h"
33#include "talk/base/logging.h"
34#include "talk/base/stringencode.h"
35#include "talk/base/stringutils.h"
36#include "talk/media/base/cryptoparams.h"
37#include "talk/media/base/capturemanager.h"
38#include "talk/media/sctp/sctpdataengine.h"
39#include "talk/p2p/base/constants.h"
40#include "talk/p2p/base/parsing.h"
41#include "talk/session/media/mediamessages.h"
42#include "talk/session/media/srtpfilter.h"
43#include "talk/xmllite/qname.h"
44#include "talk/xmllite/xmlconstants.h"
45#include "talk/xmpp/constants.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 (!talk_base::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      media->set_crypto_required(
343          GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
344      for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
345           crypto != NULL;
346           crypto = crypto->NextNamed(QN_CRYPTO)) {
347        CryptoParams params;
348        if (!ParseCryptoParams(crypto, &params, error)) {
349          return false;
350        }
351        media->AddCrypto(params);
352      }
353      break;
354    }
355  }
356  return true;
357}
358
359void ParseBandwidth(const buzz::XmlElement* parent_elem,
360                    MediaContentDescription* media) {
361  const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
362  int bandwidth_kbps = -1;
363  if (bw_elem && talk_base::FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
364    if (bandwidth_kbps >= 0) {
365      media->set_bandwidth(bandwidth_kbps * 1000);
366    }
367  }
368}
369
370bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
371                             ContentDescription** content,
372                             ParseError* error) {
373  AudioContentDescription* audio = new AudioContentDescription();
374
375  if (content_elem->FirstElement()) {
376    for (const buzz::XmlElement* codec_elem =
377             content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE);
378         codec_elem != NULL;
379         codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) {
380      AudioCodec codec;
381      if (ParseGingleAudioCodec(codec_elem, &codec)) {
382        audio->AddCodec(codec);
383      }
384    }
385  } else {
386    // For backward compatibility, we can assume the other client is
387    // an old version of Talk if it has no audio payload types at all.
388    audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1));
389    audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0));
390  }
391
392  ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio);
393
394  if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
395                             audio, error)) {
396    return false;
397  }
398
399  *content = audio;
400  return true;
401}
402
403bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
404                             ContentDescription** content,
405                             ParseError* error) {
406  VideoContentDescription* video = new VideoContentDescription();
407
408  for (const buzz::XmlElement* codec_elem =
409           content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE);
410       codec_elem != NULL;
411       codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) {
412    VideoCodec codec;
413    if (ParseGingleVideoCodec(codec_elem, &codec)) {
414      video->AddCodec(codec);
415    }
416  }
417
418  ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
419  ParseBandwidth(content_elem, video);
420
421  if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
422                             video, error)) {
423    return false;
424  }
425
426  *content = video;
427  return true;
428}
429
430void ParsePayloadTypeParameters(const buzz::XmlElement* element,
431                                std::map<std::string, std::string>* paramap) {
432  for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
433       param != NULL; param = param->NextNamed(QN_PARAMETER)) {
434    std::string name  = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
435                                   buzz::STR_EMPTY);
436    std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
437                                   buzz::STR_EMPTY);
438    if (!name.empty() && !value.empty()) {
439      paramap->insert(make_pair(name, value));
440    }
441  }
442}
443
444void ParseFeedbackParams(const buzz::XmlElement* element,
445                         FeedbackParams* params) {
446  for (const buzz::XmlElement* param = element->FirstNamed(QN_JINGLE_RTCP_FB);
447       param != NULL; param = param->NextNamed(QN_JINGLE_RTCP_FB)) {
448    std::string type = GetXmlAttr(param, QN_TYPE, buzz::STR_EMPTY);
449    std::string subtype = GetXmlAttr(param, QN_SUBTYPE, buzz::STR_EMPTY);
450    if (!type.empty()) {
451      params->Add(FeedbackParam(type, subtype));
452    }
453  }
454}
455
456void AddFeedbackParams(const FeedbackParams& additional_params,
457                       FeedbackParams* params) {
458  for (size_t i = 0; i < additional_params.params().size(); ++i) {
459    params->Add(additional_params.params()[i]);
460  }
461}
462
463int FindWithDefault(const std::map<std::string, std::string>& map,
464                    const std::string& key, const int def) {
465  std::map<std::string, std::string>::const_iterator iter = map.find(key);
466  return (iter == map.end()) ? def : atoi(iter->second.c_str());
467}
468
469
470// Parse the first encryption element found.
471// Return false if there was an encryption element and it could not be
472// parsed.
473bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
474                           MediaContentDescription* media,
475                           ParseError* error) {
476  const buzz::XmlElement* encryption =
477          content_elem->FirstNamed(QN_ENCRYPTION);
478  if (encryption == NULL) {
479      return true;
480  }
481
482  media->set_crypto_required(
483      GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
484
485  for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
486       crypto != NULL;
487       crypto = crypto->NextNamed(QN_CRYPTO)) {
488    CryptoParams params;
489    if (!ParseCryptoParams(crypto, &params, error)) {
490      return false;
491    }
492    media->AddCrypto(params);
493  }
494  return true;
495}
496
497bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
498  int id = GetXmlAttr(elem, QN_ID, -1);
499  if (id < 0)
500    return false;
501
502  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
503  int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
504  int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
505
506  std::map<std::string, std::string> paramap;
507  ParsePayloadTypeParameters(elem, &paramap);
508  int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
509
510  *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
511  ParseFeedbackParams(elem, &codec->feedback_params);
512  return true;
513}
514
515bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
516  int id = GetXmlAttr(elem, QN_ID, -1);
517  if (id < 0)
518    return false;
519
520  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
521
522  std::map<std::string, std::string> paramap;
523  ParsePayloadTypeParameters(elem, &paramap);
524  int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
525  int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
526  int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
527
528  *codec = VideoCodec(id, name, width, height, framerate, 0);
529  codec->params = paramap;
530  ParseFeedbackParams(elem, &codec->feedback_params);
531  return true;
532}
533
534bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
535  int id = GetXmlAttr(elem, QN_ID, -1);
536  if (id < 0)
537    return false;
538
539  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
540
541  *codec = DataCodec(id, name, 0);
542  ParseFeedbackParams(elem, &codec->feedback_params);
543  return true;
544}
545
546bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem,
547                                    MediaContentDescription* media,
548                                    ParseError* error) {
549  if (HasJingleStreams(desc_elem)) {
550    if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) {
551      return false;
552    }
553  } else {
554    const std::string ssrc_str = desc_elem->Attr(QN_SSRC);
555    if (!ParseSsrcAsLegacyStream(
556            ssrc_str, &(media->mutable_streams()), error)) {
557      return false;
558    }
559  }
560  return true;
561}
562
563bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
564                             ContentDescription** content,
565                             ParseError* error) {
566  talk_base::scoped_ptr<AudioContentDescription> audio(
567      new AudioContentDescription());
568
569  FeedbackParams content_feedback_params;
570  ParseFeedbackParams(content_elem, &content_feedback_params);
571
572  for (const buzz::XmlElement* payload_elem =
573           content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
574      payload_elem != NULL;
575      payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
576    AudioCodec codec;
577    if (ParseJingleAudioCodec(payload_elem, &codec)) {
578      AddFeedbackParams(content_feedback_params, &codec.feedback_params);
579      audio->AddCodec(codec);
580    }
581  }
582
583  if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio.get(), error)) {
584    return false;
585  }
586
587  if (!ParseJingleEncryption(content_elem, audio.get(), error)) {
588    return false;
589  }
590
591  audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
592
593  RtpHeaderExtensions hdrexts;
594  if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
595    return false;
596  }
597  audio->set_rtp_header_extensions(hdrexts);
598
599  *content = audio.release();
600  return true;
601}
602
603bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
604                             ContentDescription** content,
605                             ParseError* error) {
606  talk_base::scoped_ptr<VideoContentDescription> video(
607      new VideoContentDescription());
608
609  FeedbackParams content_feedback_params;
610  ParseFeedbackParams(content_elem, &content_feedback_params);
611
612  for (const buzz::XmlElement* payload_elem =
613           content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
614      payload_elem != NULL;
615      payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
616    VideoCodec codec;
617    if (ParseJingleVideoCodec(payload_elem, &codec)) {
618      AddFeedbackParams(content_feedback_params, &codec.feedback_params);
619      video->AddCodec(codec);
620    }
621  }
622
623  if (!ParseJingleStreamsOrLegacySsrc(content_elem, video.get(), error)) {
624    return false;
625  }
626  ParseBandwidth(content_elem, video.get());
627
628  if (!ParseJingleEncryption(content_elem, video.get(), error)) {
629    return false;
630  }
631
632  video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
633
634  RtpHeaderExtensions hdrexts;
635  if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
636    return false;
637  }
638  video->set_rtp_header_extensions(hdrexts);
639
640  *content = video.release();
641  return true;
642}
643
644bool ParseJingleSctpDataContent(const buzz::XmlElement* content_elem,
645                                ContentDescription** content,
646                                ParseError* error) {
647  talk_base::scoped_ptr<DataContentDescription> data(
648      new DataContentDescription());
649  data->set_protocol(kMediaProtocolSctp);
650
651  for (const buzz::XmlElement* stream_elem =
652           content_elem->FirstNamed(QN_JINGLE_DRAFT_SCTP_STREAM);
653       stream_elem != NULL;
654       stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_SCTP_STREAM)) {
655    StreamParams stream;
656    stream.groupid = stream_elem->Attr(QN_NICK);
657    stream.id = stream_elem->Attr(QN_NAME);
658    uint32 sid;
659    if (!talk_base::FromString(stream_elem->Attr(QN_SID), &sid)) {
660      return BadParse("Missing or invalid sid.", error);
661    }
662    if (sid > kMaxSctpSid) {
663      return BadParse("SID is greater than max value.", error);
664    }
665
666    stream.ssrcs.push_back(sid);
667    data->mutable_streams().push_back(stream);
668  }
669
670  *content = data.release();
671  return true;
672}
673
674bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem,
675                               ContentDescription** content,
676                               ParseError* error) {
677  DataContentDescription* data = new DataContentDescription();
678
679  FeedbackParams content_feedback_params;
680  ParseFeedbackParams(content_elem, &content_feedback_params);
681
682  for (const buzz::XmlElement* payload_elem =
683           content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
684      payload_elem != NULL;
685      payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
686    DataCodec codec;
687    if (ParseJingleDataCodec(payload_elem, &codec)) {
688      AddFeedbackParams(content_feedback_params, &codec.feedback_params);
689      data->AddCodec(codec);
690    }
691  }
692
693  if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) {
694    return false;
695  }
696  ParseBandwidth(content_elem, data);
697
698  if (!ParseJingleEncryption(content_elem, data, error)) {
699    return false;
700  }
701
702  data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
703
704  *content = data;
705  return true;
706}
707
708bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
709                                      const buzz::XmlElement* content_elem,
710                                      ContentDescription** content,
711                                      ParseError* error) {
712  if (protocol == PROTOCOL_GINGLE) {
713    const std::string& content_type = content_elem->Name().Namespace();
714    if (NS_GINGLE_AUDIO == content_type) {
715      return ParseGingleAudioContent(content_elem, content, error);
716    } else if (NS_GINGLE_VIDEO == content_type) {
717      return ParseGingleVideoContent(content_elem, content, error);
718    } else {
719      return BadParse("Unknown content type: " + content_type, error);
720    }
721  } else {
722    const std::string& content_type = content_elem->Name().Namespace();
723    // We use the XMLNS of the <description> element to determine if
724    // it's RTP or SCTP.
725    if (content_type == NS_JINGLE_DRAFT_SCTP) {
726      return ParseJingleSctpDataContent(content_elem, content, error);
727    }
728
729    std::string media;
730    if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
731      return false;
732
733    if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
734      return ParseJingleAudioContent(content_elem, content, error);
735    } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
736      return ParseJingleVideoContent(content_elem, content, error);
737    } else if (media == JINGLE_CONTENT_MEDIA_DATA) {
738      return ParseJingleRtpDataContent(content_elem, content, error);
739    } else {
740      return BadParse("Unknown media: " + media, error);
741    }
742  }
743}
744
745buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
746  buzz::XmlElement* payload_type =
747      new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
748  AddXmlAttr(payload_type, QN_ID, codec.id);
749  payload_type->AddAttr(QN_NAME, codec.name);
750  if (codec.clockrate > 0)
751    AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
752  if (codec.bitrate > 0)
753    AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
754  if (codec.channels > 1)
755    AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
756  return payload_type;
757}
758
759buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
760  buzz::XmlElement* payload_type =
761      new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
762  AddXmlAttr(payload_type, QN_ID, codec.id);
763  payload_type->AddAttr(QN_NAME, codec.name);
764  AddXmlAttr(payload_type, QN_WIDTH, codec.width);
765  AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
766  AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
767  return payload_type;
768}
769
770buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
771  buzz::XmlElement* elem = new buzz::XmlElement(name, true);
772  if (ssrc) {
773    SetXmlBody(elem, ssrc);
774  }
775  return elem;
776}
777
778buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
779  int kbps = bps / 1000;
780  buzz::XmlElement* elem = new buzz::XmlElement(name);
781  elem->AddAttr(buzz::QN_TYPE, "AS");
782  SetXmlBody(elem, kbps);
783  return elem;
784}
785
786// For Jingle, usage_qname is empty.
787buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
788                                             bool required) {
789  buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
790
791  if (required) {
792    encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
793  }
794
795  for (CryptoParamsVec::const_iterator i = cryptos.begin();
796       i != cryptos.end();
797       ++i) {
798    buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
799
800    AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
801    crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
802    crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
803    if (!i->session_params.empty()) {
804      crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
805    }
806    encryption_elem->AddElement(crypto_elem);
807  }
808  return encryption_elem;
809}
810
811buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
812                                             const buzz::QName& usage_qname,
813                                             bool required) {
814  buzz::XmlElement* encryption_elem =
815      CreateJingleEncryptionElem(cryptos, required);
816
817  if (required) {
818    encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
819  }
820
821  buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
822  encryption_elem->AddElement(usage_elem);
823
824  return encryption_elem;
825}
826
827buzz::XmlElement* CreateGingleAudioContentElem(
828    const AudioContentDescription* audio,
829    bool crypto_required) {
830  buzz::XmlElement* elem =
831      new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
832
833  for (AudioCodecs::const_iterator codec = audio->codecs().begin();
834       codec != audio->codecs().end(); ++codec) {
835    elem->AddElement(CreateGingleAudioCodecElem(*codec));
836  }
837  if (audio->has_ssrcs()) {
838    elem->AddElement(CreateGingleSsrcElem(
839        QN_GINGLE_AUDIO_SRCID, audio->first_ssrc()));
840  }
841
842  const CryptoParamsVec& cryptos = audio->cryptos();
843  if (!cryptos.empty()) {
844    elem->AddElement(CreateGingleEncryptionElem(cryptos,
845                                                QN_GINGLE_AUDIO_CRYPTO_USAGE,
846                                                crypto_required));
847  }
848  return elem;
849}
850
851buzz::XmlElement* CreateGingleVideoContentElem(
852    const VideoContentDescription* video,
853    bool crypto_required) {
854  buzz::XmlElement* elem =
855      new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
856
857  for (VideoCodecs::const_iterator codec = video->codecs().begin();
858       codec != video->codecs().end(); ++codec) {
859    elem->AddElement(CreateGingleVideoCodecElem(*codec));
860  }
861  if (video->has_ssrcs()) {
862    elem->AddElement(CreateGingleSsrcElem(
863        QN_GINGLE_VIDEO_SRCID, video->first_ssrc()));
864  }
865  if (video->bandwidth() != kAutoBandwidth) {
866    elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
867                                         video->bandwidth()));
868  }
869
870  const CryptoParamsVec& cryptos = video->cryptos();
871  if (!cryptos.empty()) {
872    elem->AddElement(CreateGingleEncryptionElem(cryptos,
873                                                QN_GINGLE_VIDEO_CRYPTO_USAGE,
874                                                crypto_required));
875  }
876
877  return elem;
878}
879
880template <class T>
881buzz::XmlElement* CreatePayloadTypeParameterElem(
882    const std::string& name, T value) {
883  buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
884
885  elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
886  AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
887
888  return elem;
889}
890
891void AddRtcpFeedbackElem(buzz::XmlElement* elem,
892                      const FeedbackParams& feedback_params) {
893  std::vector<FeedbackParam>::const_iterator it;
894  for (it = feedback_params.params().begin();
895       it != feedback_params.params().end(); ++it) {
896    buzz::XmlElement* fb_elem = new buzz::XmlElement(QN_JINGLE_RTCP_FB);
897    fb_elem->AddAttr(QN_TYPE, it->id());
898    fb_elem->AddAttr(QN_SUBTYPE, it->param());
899    elem->AddElement(fb_elem);
900  }
901}
902
903buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
904  buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
905
906  AddXmlAttr(elem, QN_ID, codec.id);
907  elem->AddAttr(QN_NAME, codec.name);
908  if (codec.clockrate > 0) {
909    AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
910  }
911  if (codec.bitrate > 0) {
912    elem->AddElement(CreatePayloadTypeParameterElem(
913        PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
914  }
915  if (codec.channels > 1) {
916    AddXmlAttr(elem, QN_CHANNELS, codec.channels);
917  }
918
919  AddRtcpFeedbackElem(elem, codec.feedback_params);
920
921  return elem;
922}
923
924buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
925  buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
926
927  AddXmlAttr(elem, QN_ID, codec.id);
928  elem->AddAttr(QN_NAME, codec.name);
929  elem->AddElement(CreatePayloadTypeParameterElem(
930      PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
931  elem->AddElement(CreatePayloadTypeParameterElem(
932      PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
933  elem->AddElement(CreatePayloadTypeParameterElem(
934      PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
935
936  AddRtcpFeedbackElem(elem, codec.feedback_params);
937
938  CodecParameterMap::const_iterator param_iter;
939  for (param_iter = codec.params.begin(); param_iter != codec.params.end();
940       ++param_iter) {
941    elem->AddElement(CreatePayloadTypeParameterElem(param_iter->first,
942                                                    param_iter->second));
943  }
944
945  return elem;
946}
947
948buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
949  buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
950
951  AddXmlAttr(elem, QN_ID, codec.id);
952  elem->AddAttr(QN_NAME, codec.name);
953
954  AddRtcpFeedbackElem(elem, codec.feedback_params);
955
956  return elem;
957}
958
959void WriteLegacyJingleSsrc(const MediaContentDescription* media,
960                           buzz::XmlElement* elem) {
961  if (media->has_ssrcs()) {
962    AddXmlAttr(elem, QN_SSRC, media->first_ssrc());
963  }
964}
965
966void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media,
967                                    buzz::XmlElement* desc_elem) {
968  if (!media->multistream()) {
969    WriteLegacyJingleSsrc(media, desc_elem);
970  } else {
971    WriteJingleStreams(media->streams(), desc_elem);
972  }
973}
974
975buzz::XmlElement* CreateJingleAudioContentElem(
976    const AudioContentDescription* audio, bool crypto_required) {
977  buzz::XmlElement* elem =
978      new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
979
980  elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
981  WriteJingleStreamsOrLegacySsrc(audio, elem);
982
983  for (AudioCodecs::const_iterator codec = audio->codecs().begin();
984       codec != audio->codecs().end(); ++codec) {
985    elem->AddElement(CreateJingleAudioCodecElem(*codec));
986  }
987
988  const CryptoParamsVec& cryptos = audio->cryptos();
989  if (!cryptos.empty()) {
990    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
991  }
992
993  if (audio->rtcp_mux()) {
994    elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
995  }
996
997  WriteJingleRtpHeaderExtensions(audio->rtp_header_extensions(), elem);
998
999  return elem;
1000}
1001
1002buzz::XmlElement* CreateJingleVideoContentElem(
1003    const VideoContentDescription* video, bool crypto_required) {
1004  buzz::XmlElement* elem =
1005      new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
1006
1007  elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
1008  WriteJingleStreamsOrLegacySsrc(video, elem);
1009
1010  for (VideoCodecs::const_iterator codec = video->codecs().begin();
1011       codec != video->codecs().end(); ++codec) {
1012    elem->AddElement(CreateJingleVideoCodecElem(*codec));
1013  }
1014
1015  const CryptoParamsVec& cryptos = video->cryptos();
1016  if (!cryptos.empty()) {
1017    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1018  }
1019
1020  if (video->rtcp_mux()) {
1021    elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1022  }
1023
1024  if (video->bandwidth() != kAutoBandwidth) {
1025    elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
1026                                         video->bandwidth()));
1027  }
1028
1029  WriteJingleRtpHeaderExtensions(video->rtp_header_extensions(), elem);
1030
1031  return elem;
1032}
1033
1034buzz::XmlElement* CreateJingleSctpDataContentElem(
1035    const DataContentDescription* data) {
1036  buzz::XmlElement* content_elem =
1037      new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_CONTENT, true);
1038  for (std::vector<StreamParams>::const_iterator
1039           stream = data->streams().begin();
1040       stream != data->streams().end(); ++stream) {
1041    buzz::XmlElement* stream_elem =
1042      new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_STREAM, false);
1043    AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream->groupid);
1044    AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream->id);
1045    if (!stream->ssrcs.empty()) {
1046      AddXmlAttr(stream_elem, QN_SID, stream->ssrcs[0]);
1047    }
1048    content_elem->AddElement(stream_elem);
1049  }
1050  return content_elem;;
1051}
1052
1053buzz::XmlElement* CreateJingleRtpDataContentElem(
1054    const DataContentDescription* data, bool crypto_required) {
1055
1056  buzz::XmlElement* elem =
1057      new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
1058
1059  elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA);
1060  WriteJingleStreamsOrLegacySsrc(data, elem);
1061
1062  for (DataCodecs::const_iterator codec = data->codecs().begin();
1063       codec != data->codecs().end(); ++codec) {
1064    elem->AddElement(CreateJingleDataCodecElem(*codec));
1065  }
1066
1067  const CryptoParamsVec& cryptos = data->cryptos();
1068  if (!cryptos.empty()) {
1069    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1070  }
1071
1072  if (data->rtcp_mux()) {
1073    elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1074  }
1075
1076  if (data->bandwidth() != kAutoBandwidth) {
1077    elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
1078                                         data->bandwidth()));
1079  }
1080
1081  return elem;
1082}
1083
1084bool IsSctp(const DataContentDescription* data) {
1085  return (data->protocol() == kMediaProtocolSctp ||
1086    data->protocol() == kMediaProtocolDtlsSctp);
1087}
1088
1089buzz::XmlElement* CreateJingleDataContentElem(
1090    const DataContentDescription* data, bool crypto_required) {
1091  if (IsSctp(data)) {
1092    return CreateJingleSctpDataContentElem(data);
1093  } else {
1094    return CreateJingleRtpDataContentElem(data, crypto_required);
1095  }
1096}
1097
1098bool MediaSessionClient::IsWritable(SignalingProtocol protocol,
1099                                    const ContentDescription* content) {
1100  const MediaContentDescription* media =
1101      static_cast<const MediaContentDescription*>(content);
1102  if (protocol == PROTOCOL_GINGLE &&
1103      media->type() == MEDIA_TYPE_DATA) {
1104    return false;
1105  }
1106  return true;
1107}
1108
1109bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
1110                                      const ContentDescription* content,
1111                                      buzz::XmlElement** elem,
1112                                      WriteError* error) {
1113  const MediaContentDescription* media =
1114      static_cast<const MediaContentDescription*>(content);
1115  bool crypto_required = secure() == SEC_REQUIRED;
1116
1117  if (media->type() == MEDIA_TYPE_AUDIO) {
1118    const AudioContentDescription* audio =
1119        static_cast<const AudioContentDescription*>(media);
1120    if (protocol == PROTOCOL_GINGLE) {
1121      *elem = CreateGingleAudioContentElem(audio, crypto_required);
1122    } else {
1123      *elem = CreateJingleAudioContentElem(audio, crypto_required);
1124    }
1125  } else if (media->type() == MEDIA_TYPE_VIDEO) {
1126    const VideoContentDescription* video =
1127        static_cast<const VideoContentDescription*>(media);
1128    if (protocol == PROTOCOL_GINGLE) {
1129      *elem = CreateGingleVideoContentElem(video, crypto_required);
1130    } else {
1131      *elem = CreateJingleVideoContentElem(video, crypto_required);
1132    }
1133  } else if (media->type() == MEDIA_TYPE_DATA) {
1134    const DataContentDescription* data =
1135        static_cast<const DataContentDescription*>(media);
1136    if (protocol == PROTOCOL_GINGLE) {
1137      return BadWrite("Data channel not supported with Gingle.", error);
1138    } else {
1139      *elem = CreateJingleDataContentElem(data, crypto_required);
1140    }
1141  } else {
1142    return BadWrite("Unknown content type: " +
1143                    talk_base::ToString<int>(media->type()), error);
1144  }
1145
1146  return true;
1147}
1148
1149}  // namespace cricket
Note: See TracBrowser for help on using the repository browser.