Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(672)

Side by Side Diff: webrtc/libjingle/session/media/mediasessionclient.cc

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

Powered by Google App Engine
This is Rietveld 408576698