3
* Copyright 2004--2005, Google Inc.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions are met:
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.
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.
30
#include "talk/session/phone/mediasessionclient.h"
32
#include "talk/base/helpers.h"
33
#include "talk/base/logging.h"
34
#include "talk/base/stringutils.h"
35
#include "talk/base/stringencode.h"
36
#include "talk/p2p/base/constants.h"
37
#include "talk/p2p/base/parsing.h"
38
#include "talk/session/phone/cryptoparams.h"
39
#include "talk/session/phone/mediamessages.h"
40
#include "talk/session/phone/srtpfilter.h"
41
#include "talk/xmpp/constants.h"
42
#include "talk/xmllite/qname.h"
43
#include "talk/xmllite/xmlconstants.h"
47
MediaSessionClient::MediaSessionClient(
48
const buzz::Jid& jid, SessionManager *manager)
50
session_manager_(manager),
52
channel_manager_(new ChannelManager(session_manager_->worker_thread())),
53
desc_factory_(channel_manager_) {
57
MediaSessionClient::MediaSessionClient(
58
const buzz::Jid& jid, SessionManager *manager,
59
MediaEngineInterface* media_engine,
60
DataEngineInterface* data_media_engine,
61
DeviceManagerInterface* device_manager)
63
session_manager_(manager),
65
channel_manager_(new ChannelManager(
66
media_engine, data_media_engine,
67
device_manager, session_manager_->worker_thread())),
68
desc_factory_(channel_manager_) {
72
void MediaSessionClient::Construct() {
73
// Register ourselves as the handler of audio and video sessions.
74
session_manager_->AddClient(NS_JINGLE_RTP, this);
75
// Forward device notifications.
76
SignalDevicesChange.repeat(channel_manager_->SignalDevicesChange);
77
// Bring up the channel manager.
78
// In previous versions of ChannelManager, this was done automatically
79
// in the constructor.
80
channel_manager_->Init();
83
MediaSessionClient::~MediaSessionClient() {
85
std::map<uint32, Call *>::iterator it;
86
while (calls_.begin() != calls_.end()) {
87
std::map<uint32, Call *>::iterator it = calls_.begin();
88
DestroyCall((*it).second);
91
// Delete channel manager. This will wait for the channels to exit
92
delete channel_manager_;
94
// Remove ourselves from the client map.
95
session_manager_->RemoveClient(NS_JINGLE_RTP);
98
Call *MediaSessionClient::CreateCall() {
99
Call *call = new Call(this);
100
calls_[call->id()] = call;
101
SignalCallCreate(call);
105
void MediaSessionClient::OnSessionCreate(Session *session,
106
bool received_initiate) {
107
if (received_initiate) {
108
session->SignalState.connect(this, &MediaSessionClient::OnSessionState);
112
void MediaSessionClient::OnSessionState(BaseSession* base_session,
113
BaseSession::State state) {
114
// MediaSessionClient can only be used with a Session*, so it's
115
// safe to cast here.
116
Session* session = static_cast<Session*>(base_session);
118
if (state == Session::STATE_RECEIVEDINITIATE) {
119
// The creation of the call must happen after the session has
120
// processed the initiate message because we need the
121
// remote_description to know what content names to use in the
124
// If our accept would have no codecs, then we must reject this call.
125
const SessionDescription* offer = session->remote_description();
126
const SessionDescription* accept = CreateAnswer(offer, CallOptions());
127
const ContentInfo* audio_content = GetFirstAudioContent(accept);
128
const AudioContentDescription* audio_accept = (!audio_content) ? NULL :
129
static_cast<const AudioContentDescription*>(audio_content->description);
131
// For some reason, we need to create the call even when we
133
Call *call = CreateCall();
134
session_map_[session->id()] = call;
135
call->IncomingSession(session, offer);
137
if (!audio_accept || audio_accept->codecs().size() == 0) {
138
session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
144
void MediaSessionClient::DestroyCall(Call *call) {
145
// Change focus away, signal destruction
147
if (call == focus_call_)
149
SignalCallDestroy(call);
151
// Remove it from calls_ map and delete
153
std::map<uint32, Call *>::iterator it = calls_.find(call->id());
154
if (it != calls_.end())
160
void MediaSessionClient::OnSessionDestroy(Session *session) {
161
// Find the call this session is in, remove it
163
std::map<std::string, Call *>::iterator it = session_map_.find(session->id());
164
ASSERT(it != session_map_.end());
165
if (it != session_map_.end()) {
166
Call *call = (*it).second;
167
session_map_.erase(it);
168
call->RemoveSession(session);
172
Call *MediaSessionClient::GetFocus() {
176
void MediaSessionClient::SetFocus(Call *call) {
177
Call *old_focus_call = focus_call_;
178
if (focus_call_ != call) {
179
if (focus_call_ != NULL)
180
focus_call_->EnableChannels(false);
182
if (focus_call_ != NULL)
183
focus_call_->EnableChannels(true);
184
SignalFocus(focus_call_, old_focus_call);
188
void MediaSessionClient::JoinCalls(Call *call_to_join, Call *call) {
189
// Move all sessions from call to call_to_join, delete call.
190
// If call_to_join has focus, added sessions should have enabled channels.
192
if (focus_call_ == call)
194
call_to_join->Join(call, focus_call_ == call_to_join);
198
Session *MediaSessionClient::CreateSession(Call *call) {
199
const std::string& type = NS_JINGLE_RTP;
200
Session *session = session_manager_->CreateSession(jid().Str(), type);
201
session_map_[session->id()] = call;
205
// TODO: Move all of the parsing and writing functions into
206
// mediamessages.cc, with unit tests.
207
bool ParseGingleAudioCodec(const buzz::XmlElement* element, AudioCodec* out) {
208
int id = GetXmlAttr(element, QN_ID, -1);
212
std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
213
int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0);
214
int bitrate = GetXmlAttr(element, QN_BITRATE, 0);
215
int channels = GetXmlAttr(element, QN_CHANNELS, 1);
216
*out = AudioCodec(id, name, clockrate, bitrate, channels, 0);
220
bool ParseGingleVideoCodec(const buzz::XmlElement* element, VideoCodec* out) {
221
int id = GetXmlAttr(element, QN_ID, -1);
225
std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
226
int width = GetXmlAttr(element, QN_WIDTH, 0);
227
int height = GetXmlAttr(element, QN_HEIGHT, 0);
228
int framerate = GetXmlAttr(element, QN_FRAMERATE, 0);
230
*out = VideoCodec(id, name, width, height, framerate, 0);
234
// Parses an ssrc string as a legacy stream. If it fails, returns
235
// false and fills an error message.
236
bool ParseSsrcAsLegacyStream(const std::string& ssrc_str,
237
std::vector<StreamParams>* streams,
239
if (!ssrc_str.empty()) {
241
if (!talk_base::FromString(ssrc_str, &ssrc)) {
242
return BadParse("Missing or invalid ssrc.", error);
245
streams->push_back(StreamParams::CreateLegacy(ssrc));
250
void ParseGingleSsrc(const buzz::XmlElement* parent_elem,
251
const buzz::QName& name,
252
MediaContentDescription* media) {
253
const buzz::XmlElement* ssrc_elem = parent_elem->FirstNamed(name);
256
ParseSsrcAsLegacyStream(
257
ssrc_elem->BodyText(), &(media->mutable_streams()), &error);
261
bool ParseCryptoParams(const buzz::XmlElement* element,
264
if (!element->HasAttr(QN_CRYPTO_SUITE)) {
265
return BadParse("crypto: crypto-suite attribute missing ", error);
266
} else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) {
267
return BadParse("crypto: key-params attribute missing ", error);
268
} else if (!element->HasAttr(QN_CRYPTO_TAG)) {
269
return BadParse("crypto: tag attribute missing ", error);
272
const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE);
273
const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS);
274
const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0);
275
const std::string& session_params =
276
element->Attr(QN_CRYPTO_SESSION_PARAMS); // Optional.
278
*out = CryptoParams(tag, crypto_suite, key_params, session_params);
283
// Parse the first encryption element found with a matching 'usage'
285
// <usage/> is specific to Gingle. In Jingle, <crypto/> is already
286
// scoped to a content.
287
// Return false if there was an encryption element and it could not be
289
bool ParseGingleEncryption(const buzz::XmlElement* desc,
290
const buzz::QName& usage,
291
MediaContentDescription* media,
293
for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION);
295
encryption = encryption->NextNamed(QN_ENCRYPTION)) {
296
if (encryption->FirstNamed(usage) != NULL) {
297
media->set_crypto_required(
298
GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
299
for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
301
crypto = crypto->NextNamed(QN_CRYPTO)) {
303
if (!ParseCryptoParams(crypto, ¶ms, error)) {
306
media->AddCrypto(params);
314
void ParseBandwidth(const buzz::XmlElement* parent_elem,
315
MediaContentDescription* media) {
316
const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
317
int bandwidth_kbps = -1;
318
if (bw_elem && talk_base::FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
319
if (bandwidth_kbps >= 0) {
320
media->set_bandwidth(bandwidth_kbps * 1000);
325
bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
326
const ContentDescription** content,
328
AudioContentDescription* audio = new AudioContentDescription();
330
if (content_elem->FirstElement()) {
331
for (const buzz::XmlElement* codec_elem =
332
content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE);
334
codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) {
336
if (ParseGingleAudioCodec(codec_elem, &codec)) {
337
audio->AddCodec(codec);
341
// For backward compatibility, we can assume the other client is
342
// an old version of Talk if it has no audio payload types at all.
343
audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1));
344
audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0));
347
ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio);
349
if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
358
bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
359
const ContentDescription** content,
361
VideoContentDescription* video = new VideoContentDescription();
363
for (const buzz::XmlElement* codec_elem =
364
content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE);
366
codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) {
368
if (ParseGingleVideoCodec(codec_elem, &codec)) {
369
video->AddCodec(codec);
373
ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
374
ParseBandwidth(content_elem, video);
376
if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
385
void ParsePayloadTypeParameters(const buzz::XmlElement* element,
386
std::map<std::string, std::string>* paramap) {
387
for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
388
param != NULL; param = param->NextNamed(QN_PARAMETER)) {
389
std::string name = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
391
std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
393
if (!name.empty() && !value.empty()) {
394
paramap->insert(make_pair(name, value));
399
int FindWithDefault(const std::map<std::string, std::string>& map,
400
const std::string& key, const int def) {
401
std::map<std::string, std::string>::const_iterator iter = map.find(key);
402
return (iter == map.end()) ? def : atoi(iter->second.c_str());
406
// Parse the first encryption element found.
407
// Return false if there was an encryption element and it could not be
409
bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
410
MediaContentDescription* media,
412
const buzz::XmlElement* encryption =
413
content_elem->FirstNamed(QN_ENCRYPTION);
414
if (encryption == NULL) {
418
media->set_crypto_required(
419
GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
421
for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
423
crypto = crypto->NextNamed(QN_CRYPTO)) {
425
if (!ParseCryptoParams(crypto, ¶ms, error)) {
428
media->AddCrypto(params);
433
bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
434
int id = GetXmlAttr(elem, QN_ID, -1);
438
std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
439
int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
440
int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
442
std::map<std::string, std::string> paramap;
443
ParsePayloadTypeParameters(elem, ¶map);
444
int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
446
*codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
450
bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
451
int id = GetXmlAttr(elem, QN_ID, -1);
455
std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
457
std::map<std::string, std::string> paramap;
458
ParsePayloadTypeParameters(elem, ¶map);
459
int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
460
int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
461
int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
463
*codec = VideoCodec(id, name, width, height, framerate, 0);
467
bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
468
int id = GetXmlAttr(elem, QN_ID, -1);
472
std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
474
*codec = DataCodec(id, name, 0);
478
bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem,
479
MediaContentDescription* media,
481
if (HasJingleStreams(desc_elem)) {
482
if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) {
486
const std::string ssrc_str = desc_elem->Attr(QN_SSRC);
487
if (!ParseSsrcAsLegacyStream(
488
ssrc_str, &(media->mutable_streams()), error)) {
495
bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
496
const ContentDescription** content,
498
AudioContentDescription* audio = new AudioContentDescription();
500
for (const buzz::XmlElement* payload_elem =
501
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
502
payload_elem != NULL;
503
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
505
if (ParseJingleAudioCodec(payload_elem, &codec)) {
506
audio->AddCodec(codec);
510
if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio, error)) {
514
if (!ParseJingleEncryption(content_elem, audio, error)) {
518
audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
524
bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
525
const ContentDescription** content,
527
VideoContentDescription* video = new VideoContentDescription();
529
for (const buzz::XmlElement* payload_elem =
530
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
531
payload_elem != NULL;
532
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
534
if (ParseJingleVideoCodec(payload_elem, &codec)) {
535
video->AddCodec(codec);
539
if (!ParseJingleStreamsOrLegacySsrc(content_elem, video, error)) {
542
ParseBandwidth(content_elem, video);
544
if (!ParseJingleEncryption(content_elem, video, error)) {
548
video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
554
bool ParseJingleDataContent(const buzz::XmlElement* content_elem,
555
const ContentDescription** content,
557
DataContentDescription* data = new DataContentDescription();
559
for (const buzz::XmlElement* payload_elem =
560
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
561
payload_elem != NULL;
562
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
564
if (ParseJingleDataCodec(payload_elem, &codec)) {
565
data->AddCodec(codec);
569
if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) {
572
ParseBandwidth(content_elem, data);
574
if (!ParseJingleEncryption(content_elem, data, error)) {
578
data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
584
bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
585
const buzz::XmlElement* content_elem,
586
const ContentDescription** content,
588
if (protocol == PROTOCOL_GINGLE) {
589
const std::string& content_type = content_elem->Name().Namespace();
590
if (NS_GINGLE_AUDIO == content_type) {
591
return ParseGingleAudioContent(content_elem, content, error);
592
} else if (NS_GINGLE_VIDEO == content_type) {
593
return ParseGingleVideoContent(content_elem, content, error);
595
return BadParse("Unknown content type: " + content_type, error);
599
if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
602
if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
603
return ParseJingleAudioContent(content_elem, content, error);
604
} else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
605
return ParseJingleVideoContent(content_elem, content, error);
606
} else if (media == JINGLE_CONTENT_MEDIA_DATA) {
607
return ParseJingleDataContent(content_elem, content, error);
609
return BadParse("Unknown media: " + media, error);
614
buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
615
buzz::XmlElement* payload_type =
616
new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
617
AddXmlAttr(payload_type, QN_ID, codec.id);
618
payload_type->AddAttr(QN_NAME, codec.name);
619
if (codec.clockrate > 0)
620
AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
621
if (codec.bitrate > 0)
622
AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
623
if (codec.channels > 1)
624
AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
628
buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
629
buzz::XmlElement* payload_type =
630
new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
631
AddXmlAttr(payload_type, QN_ID, codec.id);
632
payload_type->AddAttr(QN_NAME, codec.name);
633
AddXmlAttr(payload_type, QN_WIDTH, codec.width);
634
AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
635
AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
639
buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
640
buzz::XmlElement* elem = new buzz::XmlElement(name, true);
642
SetXmlBody(elem, ssrc);
647
buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
648
int kbps = bps / 1000;
649
buzz::XmlElement* elem = new buzz::XmlElement(name);
650
elem->AddAttr(buzz::QN_TYPE, "AS");
651
SetXmlBody(elem, kbps);
655
// For Jingle, usage_qname is empty.
656
buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
658
buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
661
encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
664
for (CryptoParamsVec::const_iterator i = cryptos.begin();
667
buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
669
AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
670
crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
671
crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
672
if (!i->session_params.empty()) {
673
crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
675
encryption_elem->AddElement(crypto_elem);
677
return encryption_elem;
680
buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
681
const buzz::QName& usage_qname,
683
buzz::XmlElement* encryption_elem =
684
CreateJingleEncryptionElem(cryptos, required);
687
encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
690
buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
691
encryption_elem->AddElement(usage_elem);
693
return encryption_elem;
696
buzz::XmlElement* CreateGingleAudioContentElem(
697
const AudioContentDescription* audio,
698
bool crypto_required) {
699
buzz::XmlElement* elem =
700
new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
702
for (AudioCodecs::const_iterator codec = audio->codecs().begin();
703
codec != audio->codecs().end(); ++codec) {
704
elem->AddElement(CreateGingleAudioCodecElem(*codec));
706
if (audio->has_ssrcs()) {
707
elem->AddElement(CreateGingleSsrcElem(
708
QN_GINGLE_AUDIO_SRCID, audio->first_ssrc()));
711
const CryptoParamsVec& cryptos = audio->cryptos();
712
if (!cryptos.empty()) {
713
elem->AddElement(CreateGingleEncryptionElem(cryptos,
714
QN_GINGLE_AUDIO_CRYPTO_USAGE,
720
buzz::XmlElement* CreateGingleVideoContentElem(
721
const VideoContentDescription* video,
722
bool crypto_required) {
723
buzz::XmlElement* elem =
724
new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
726
for (VideoCodecs::const_iterator codec = video->codecs().begin();
727
codec != video->codecs().end(); ++codec) {
728
elem->AddElement(CreateGingleVideoCodecElem(*codec));
730
if (video->has_ssrcs()) {
731
elem->AddElement(CreateGingleSsrcElem(
732
QN_GINGLE_VIDEO_SRCID, video->first_ssrc()));
734
if (video->bandwidth() != kAutoBandwidth) {
735
elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
736
video->bandwidth()));
739
const CryptoParamsVec& cryptos = video->cryptos();
740
if (!cryptos.empty()) {
741
elem->AddElement(CreateGingleEncryptionElem(cryptos,
742
QN_GINGLE_VIDEO_CRYPTO_USAGE,
749
buzz::XmlElement* CreatePayloadTypeParameterElem(
750
const std::string& name, int value) {
751
buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
753
elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
754
AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
759
buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
760
buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
762
AddXmlAttr(elem, QN_ID, codec.id);
763
elem->AddAttr(QN_NAME, codec.name);
764
if (codec.clockrate > 0) {
765
AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
767
if (codec.bitrate > 0) {
768
elem->AddElement(CreatePayloadTypeParameterElem(
769
PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
771
if (codec.channels > 1) {
772
AddXmlAttr(elem, QN_CHANNELS, codec.channels);
778
buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
779
buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
781
AddXmlAttr(elem, QN_ID, codec.id);
782
elem->AddAttr(QN_NAME, codec.name);
783
elem->AddElement(CreatePayloadTypeParameterElem(
784
PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
785
elem->AddElement(CreatePayloadTypeParameterElem(
786
PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
787
elem->AddElement(CreatePayloadTypeParameterElem(
788
PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
793
buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
794
buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
796
AddXmlAttr(elem, QN_ID, codec.id);
797
elem->AddAttr(QN_NAME, codec.name);
802
void WriteLegacyJingleSsrc(const MediaContentDescription* media,
803
buzz::XmlElement* elem) {
804
if (media->has_ssrcs()) {
805
AddXmlAttr(elem, QN_SSRC, media->first_ssrc());
809
void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media,
810
buzz::XmlElement* desc_elem) {
811
if (!media->multistream()) {
812
WriteLegacyJingleSsrc(media, desc_elem);
814
WriteJingleStreams(media->streams(), desc_elem);
818
buzz::XmlElement* CreateJingleAudioContentElem(
819
const AudioContentDescription* audio, bool crypto_required) {
820
buzz::XmlElement* elem =
821
new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
823
elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
824
WriteJingleStreamsOrLegacySsrc(audio, elem);
826
for (AudioCodecs::const_iterator codec = audio->codecs().begin();
827
codec != audio->codecs().end(); ++codec) {
828
elem->AddElement(CreateJingleAudioCodecElem(*codec));
831
const CryptoParamsVec& cryptos = audio->cryptos();
832
if (!cryptos.empty()) {
833
elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
836
if (audio->rtcp_mux()) {
837
elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
843
buzz::XmlElement* CreateJingleVideoContentElem(
844
const VideoContentDescription* video, bool crypto_required) {
845
buzz::XmlElement* elem =
846
new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
848
elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
849
WriteJingleStreamsOrLegacySsrc(video, elem);
851
for (VideoCodecs::const_iterator codec = video->codecs().begin();
852
codec != video->codecs().end(); ++codec) {
853
elem->AddElement(CreateJingleVideoCodecElem(*codec));
856
const CryptoParamsVec& cryptos = video->cryptos();
857
if (!cryptos.empty()) {
858
elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
861
if (video->rtcp_mux()) {
862
elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
865
if (video->bandwidth() != kAutoBandwidth) {
866
elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
867
video->bandwidth()));
873
buzz::XmlElement* CreateJingleDataContentElem(
874
const DataContentDescription* data, bool crypto_required) {
875
buzz::XmlElement* elem =
876
new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
878
elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA);
879
WriteJingleStreamsOrLegacySsrc(data, elem);
881
for (DataCodecs::const_iterator codec = data->codecs().begin();
882
codec != data->codecs().end(); ++codec) {
883
elem->AddElement(CreateJingleDataCodecElem(*codec));
886
const CryptoParamsVec& cryptos = data->cryptos();
887
if (!cryptos.empty()) {
888
elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
891
if (data->rtcp_mux()) {
892
elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
895
if (data->bandwidth() != kAutoBandwidth) {
896
elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
903
bool MediaSessionClient::IsWritable(SignalingProtocol protocol,
904
const ContentDescription* content) {
905
const MediaContentDescription* media =
906
static_cast<const MediaContentDescription*>(content);
907
if (protocol == PROTOCOL_GINGLE &&
908
media->type() == MEDIA_TYPE_DATA) {
914
bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
915
const ContentDescription* content,
916
buzz::XmlElement** elem,
918
const MediaContentDescription* media =
919
static_cast<const MediaContentDescription*>(content);
920
bool crypto_required = secure() == SEC_REQUIRED;
922
if (media->type() == MEDIA_TYPE_AUDIO) {
923
const AudioContentDescription* audio =
924
static_cast<const AudioContentDescription*>(media);
925
if (protocol == PROTOCOL_GINGLE) {
926
*elem = CreateGingleAudioContentElem(audio, crypto_required);
928
*elem = CreateJingleAudioContentElem(audio, crypto_required);
930
} else if (media->type() == MEDIA_TYPE_VIDEO) {
931
const VideoContentDescription* video =
932
static_cast<const VideoContentDescription*>(media);
933
if (protocol == PROTOCOL_GINGLE) {
934
*elem = CreateGingleVideoContentElem(video, crypto_required);
936
*elem = CreateJingleVideoContentElem(video, crypto_required);
938
} else if (media->type() == MEDIA_TYPE_DATA) {
939
const DataContentDescription* data =
940
static_cast<const DataContentDescription*>(media);
941
if (protocol == PROTOCOL_GINGLE) {
942
return BadWrite("Data channel not supported with Gingle.", error);
944
*elem = CreateJingleDataContentElem(data, crypto_required);
947
return BadWrite("Unknown content type: " +
948
talk_base::ToString<int>(media->type()), error);
954
} // namespace cricket