blob: 43fdc688368e372712dd3383e099bf5985c1095c [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2011 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Henrik Kjellander15583c12016-02-10 10:53:12 +010011#include "webrtc/api/webrtcsdp.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
kjellandera96e2d72016-02-04 23:52:28 -080013#include <ctype.h>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <limits.h>
15#include <stdio.h>
kwibergd1fe2812016-04-27 06:47:29 -070016
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017#include <algorithm>
kwibergd1fe2812016-04-27 06:47:29 -070018#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000019#include <string>
deadbeef67cf2c12016-04-13 10:07:16 -070020#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021#include <vector>
22
Henrik Kjellander15583c12016-02-10 10:53:12 +010023#include "webrtc/api/jsepicecandidate.h"
24#include "webrtc/api/jsepsessiondescription.h"
tfarina5237aaf2015-11-10 23:44:30 -080025#include "webrtc/base/arraysize.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000026#include "webrtc/base/common.h"
27#include "webrtc/base/logging.h"
28#include "webrtc/base/messagedigest.h"
29#include "webrtc/base/stringutils.h"
isheriff6f8d6862016-05-26 11:24:55 -070030// for RtpExtension
31#include "webrtc/config.h"
kjellandera96e2d72016-02-04 23:52:28 -080032#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-04 23:52:28 -080033#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080034#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080035#include "webrtc/media/base/rtputils.h"
36#include "webrtc/media/sctp/sctpdataengine.h"
37#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080038#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080039#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010040#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000041
42using cricket::AudioContentDescription;
43using cricket::Candidate;
44using cricket::Candidates;
45using cricket::ContentDescription;
46using cricket::ContentInfo;
47using cricket::CryptoParams;
48using cricket::DataContentDescription;
49using cricket::ICE_CANDIDATE_COMPONENT_RTP;
50using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
51using cricket::kCodecParamMaxBitrate;
52using cricket::kCodecParamMaxPTime;
53using cricket::kCodecParamMaxQuantization;
54using cricket::kCodecParamMinBitrate;
55using cricket::kCodecParamMinPTime;
56using cricket::kCodecParamPTime;
57using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000058using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000059using cricket::kCodecParamStereo;
60using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010061using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062using cricket::kCodecParamSctpProtocol;
63using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000064using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000065using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000066using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000067using cricket::MediaContentDescription;
68using cricket::MediaType;
isheriff6f8d6862016-05-26 11:24:55 -070069using cricket::RtpHeaderExtensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000070using cricket::SsrcGroup;
71using cricket::StreamParams;
72using cricket::StreamParamsVec;
73using cricket::TransportDescription;
74using cricket::TransportInfo;
75using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000076using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000077
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078namespace cricket {
79class SessionDescription;
80}
81
82namespace webrtc {
83
84// Line type
85// RFC 4566
86// An SDP session description consists of a number of lines of text of
87// the form:
88// <type>=<value>
89// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -080090static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091static const char kLineTypeVersion = 'v';
92static const char kLineTypeOrigin = 'o';
93static const char kLineTypeSessionName = 's';
94static const char kLineTypeSessionInfo = 'i';
95static const char kLineTypeSessionUri = 'u';
96static const char kLineTypeSessionEmail = 'e';
97static const char kLineTypeSessionPhone = 'p';
98static const char kLineTypeSessionBandwidth = 'b';
99static const char kLineTypeTiming = 't';
100static const char kLineTypeRepeatTimes = 'r';
101static const char kLineTypeTimeZone = 'z';
102static const char kLineTypeEncryptionKey = 'k';
103static const char kLineTypeMedia = 'm';
104static const char kLineTypeConnection = 'c';
105static const char kLineTypeAttributes = 'a';
106
107// Attributes
108static const char kAttributeGroup[] = "group";
109static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800110static const char kAttributeMsid[] = "msid";
deadbeef25ed4352016-12-12 18:37:36 -0800111static const char kAttributeBundleOnly[] = "bundle-only";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800113static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114static const char kAttributeSsrc[] = "ssrc";
115static const char kSsrcAttributeCname[] = "cname";
116static const char kAttributeExtmap[] = "extmap";
117// draft-alvestrand-mmusic-msid-01
118// a=msid-semantic: WMS
119static const char kAttributeMsidSemantics[] = "msid-semantic";
120static const char kMediaStreamSemantic[] = "WMS";
121static const char kSsrcAttributeMsid[] = "msid";
122static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000123static const char kSsrcAttributeMslabel[] = "mslabel";
124static const char kSSrcAttributeLabel[] = "label";
125static const char kAttributeSsrcGroup[] = "ssrc-group";
126static const char kAttributeCrypto[] = "crypto";
127static const char kAttributeCandidate[] = "candidate";
128static const char kAttributeCandidateTyp[] = "typ";
129static const char kAttributeCandidateRaddr[] = "raddr";
130static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800131static const char kAttributeCandidateUfrag[] = "ufrag";
132static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700134static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800135static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000136static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000137static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138static const char kAttributeFmtp[] = "fmtp";
139static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000140static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000141static const char kAttributeRtcp[] = "rtcp";
142static const char kAttributeIceUfrag[] = "ice-ufrag";
143static const char kAttributeIcePwd[] = "ice-pwd";
144static const char kAttributeIceLite[] = "ice-lite";
145static const char kAttributeIceOption[] = "ice-options";
146static const char kAttributeSendOnly[] = "sendonly";
147static const char kAttributeRecvOnly[] = "recvonly";
148static const char kAttributeRtcpFb[] = "rtcp-fb";
149static const char kAttributeSendRecv[] = "sendrecv";
150static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000151// draft-ietf-mmusic-sctp-sdp-07
152// a=sctp-port
153static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154
155// Experimental flags
156static const char kAttributeXGoogleFlag[] = "x-google-flag";
157static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000158
159// Candidate
160static const char kCandidateHost[] = "host";
161static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700162static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000163static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000164static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165
166static const char kSdpDelimiterEqual = '=';
167static const char kSdpDelimiterSpace = ' ';
168static const char kSdpDelimiterColon = ':';
169static const char kSdpDelimiterSemicolon = ';';
170static const char kSdpDelimiterSlash = '/';
171static const char kNewLine = '\n';
172static const char kReturn = '\r';
173static const char kLineBreak[] = "\r\n";
174
175// TODO: Generate the Session and Time description
176// instead of hardcoding.
177static const char kSessionVersion[] = "v=0";
178// RFC 4566
179static const char kSessionOriginUsername[] = "-";
180static const char kSessionOriginSessionId[] = "0";
181static const char kSessionOriginSessionVersion[] = "0";
182static const char kSessionOriginNettype[] = "IN";
183static const char kSessionOriginAddrtype[] = "IP4";
184static const char kSessionOriginAddress[] = "127.0.0.1";
185static const char kSessionName[] = "s=-";
186static const char kTimeDescription[] = "t=0 0";
187static const char kAttrGroup[] = "a=group:BUNDLE";
188static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000189static const char kConnectionIpv4Addrtype[] = "IP4";
190static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000191static const char kMediaTypeVideo[] = "video";
192static const char kMediaTypeAudio[] = "audio";
193static const char kMediaTypeData[] = "application";
194static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000195// draft-ietf-mmusic-trickle-ice-01
196// When no candidates have been gathered, set the connection
197// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000198// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
199// Use IPV4 per default.
200static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000201static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000202// RFC 3556
203static const char kApplicationSpecificMaximum[] = "AS";
204
205static const int kDefaultVideoClockrate = 90000;
206
207// ISAC special-case.
208static const char kIsacCodecName[] = "ISAC"; // From webrtcvoiceengine.cc
209static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h
210static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h
211
wu@webrtc.org78187522013-10-07 23:32:02 +0000212static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000213
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000214// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
215// types.
216const int kWildcardPayloadType = -1;
217
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000218struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200219 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800221 std::string stream_id;
222 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000223
224 // For backward compatibility.
225 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
226 std::string label;
227 std::string mslabel;
228};
229typedef std::vector<SsrcInfo> SsrcInfoVec;
230typedef std::vector<SsrcGroup> SsrcGroupVec;
231
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000232template <class T>
233static void AddFmtpLine(const T& codec, std::string* message);
234static void BuildMediaDescription(const ContentInfo* content_info,
235 const TransportInfo* transport_info,
236 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000237 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800238 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000239 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000240static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800241static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
242 const MediaType media_type,
243 bool unified_plan_sdp,
244 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000245static void BuildRtpMap(const MediaContentDescription* media_desc,
246 const MediaType media_type,
247 std::string* message);
248static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800249 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000250 std::string* message);
251static void BuildIceOptions(const std::vector<std::string>& transport_options,
252 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000253static bool IsRtp(const std::string& protocol);
254static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255static bool ParseSessionDescription(const std::string& message, size_t* pos,
256 std::string* session_id,
257 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000258 TransportDescription* session_td,
259 RtpHeaderExtensions* session_extmaps,
260 cricket::SessionDescription* desc,
261 SdpParseError* error);
262static bool ParseGroupAttribute(const std::string& line,
263 cricket::SessionDescription* desc,
264 SdpParseError* error);
265static bool ParseMediaDescription(
266 const std::string& message,
267 const TransportDescription& session_td,
268 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000269 size_t* pos, cricket::SessionDescription* desc,
270 std::vector<JsepIceCandidate*>* candidates,
271 SdpParseError* error);
272static bool ParseContent(const std::string& message,
273 const MediaType media_type,
274 int mline_index,
275 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700276 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000277 size_t* pos,
278 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -0800279 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280 MediaContentDescription* media_desc,
281 TransportDescription* transport,
282 std::vector<JsepIceCandidate*>* candidates,
283 SdpParseError* error);
284static bool ParseSsrcAttribute(const std::string& line,
285 SsrcInfoVec* ssrc_infos,
286 SdpParseError* error);
287static bool ParseSsrcGroupAttribute(const std::string& line,
288 SsrcGroupVec* ssrc_groups,
289 SdpParseError* error);
290static bool ParseCryptoAttribute(const std::string& line,
291 MediaContentDescription* media_desc,
292 SdpParseError* error);
293static bool ParseRtpmapAttribute(const std::string& line,
294 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700295 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296 MediaContentDescription* media_desc,
297 SdpParseError* error);
298static bool ParseFmtpAttributes(const std::string& line,
299 const MediaType media_type,
300 MediaContentDescription* media_desc,
301 SdpParseError* error);
302static bool ParseFmtpParam(const std::string& line, std::string* parameter,
303 std::string* value, SdpParseError* error);
304static bool ParseCandidate(const std::string& message, Candidate* candidate,
305 SdpParseError* error, bool is_raw);
306static bool ParseRtcpFbAttribute(const std::string& line,
307 const MediaType media_type,
308 MediaContentDescription* media_desc,
309 SdpParseError* error);
310static bool ParseIceOptions(const std::string& line,
311 std::vector<std::string>* transport_options,
312 SdpParseError* error);
313static bool ParseExtmap(const std::string& line,
isheriff6f8d6862016-05-26 11:24:55 -0700314 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000315 SdpParseError* error);
316static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000317 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000318 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000319static bool ParseDtlsSetup(const std::string& line,
320 cricket::ConnectionRole* role,
321 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800322static bool ParseMsidAttribute(const std::string& line,
323 std::string* stream_id,
324 std::string* track_id,
325 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000326
327// Helper functions
328
329// Below ParseFailed*** functions output the line that caused the parsing
330// failure and the detailed reason (|description|) of the failure to |error|.
331// The functions always return false so that they can be used directly in the
332// following way when error happens:
333// "return ParseFailed***(...);"
334
335// The line starting at |line_start| of |message| is the failing line.
336// The reason for the failure should be provided in the |description|.
337// An example of a description could be "unknown character".
338static bool ParseFailed(const std::string& message,
339 size_t line_start,
340 const std::string& description,
341 SdpParseError* error) {
342 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000343 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000344 size_t line_end = message.find(kNewLine, line_start);
345 if (line_end != std::string::npos) {
346 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
347 --line_end;
348 }
349 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000350 } else {
351 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000352 }
353
354 if (error) {
355 error->line = first_line;
356 error->description = description;
357 }
358 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
359 << "\". Reason: " << description;
360 return false;
361}
362
363// |line| is the failing line. The reason for the failure should be
364// provided in the |description|.
365static bool ParseFailed(const std::string& line,
366 const std::string& description,
367 SdpParseError* error) {
368 return ParseFailed(line, 0, description, error);
369}
370
371// Parses failure where the failing SDP line isn't know or there are multiple
372// failing lines.
373static bool ParseFailed(const std::string& description,
374 SdpParseError* error) {
375 return ParseFailed("", description, error);
376}
377
378// |line| is the failing line. The failure is due to the fact that |line|
379// doesn't have |expected_fields| fields.
380static bool ParseFailedExpectFieldNum(const std::string& line,
381 int expected_fields,
382 SdpParseError* error) {
383 std::ostringstream description;
384 description << "Expects " << expected_fields << " fields.";
385 return ParseFailed(line, description.str(), error);
386}
387
388// |line| is the failing line. The failure is due to the fact that |line| has
389// less than |expected_min_fields| fields.
390static bool ParseFailedExpectMinFieldNum(const std::string& line,
391 int expected_min_fields,
392 SdpParseError* error) {
393 std::ostringstream description;
394 description << "Expects at least " << expected_min_fields << " fields.";
395 return ParseFailed(line, description.str(), error);
396}
397
398// |line| is the failing line. The failure is due to the fact that it failed to
399// get the value of |attribute|.
400static bool ParseFailedGetValue(const std::string& line,
401 const std::string& attribute,
402 SdpParseError* error) {
403 std::ostringstream description;
404 description << "Failed to get the value of attribute: " << attribute;
405 return ParseFailed(line, description.str(), error);
406}
407
408// The line starting at |line_start| of |message| is the failing line. The
409// failure is due to the line type (e.g. the "m" part of the "m-line")
410// not matching what is expected. The expected line type should be
411// provided as |line_type|.
412static bool ParseFailedExpectLine(const std::string& message,
413 size_t line_start,
414 const char line_type,
415 const std::string& line_value,
416 SdpParseError* error) {
417 std::ostringstream description;
418 description << "Expect line: " << line_type << "=" << line_value;
419 return ParseFailed(message, line_start, description.str(), error);
420}
421
422static bool AddLine(const std::string& line, std::string* message) {
423 if (!message)
424 return false;
425
426 message->append(line);
427 message->append(kLineBreak);
428 return true;
429}
430
431static bool GetLine(const std::string& message,
432 size_t* pos,
433 std::string* line) {
434 size_t line_begin = *pos;
435 size_t line_end = message.find(kNewLine, line_begin);
436 if (line_end == std::string::npos) {
437 return false;
438 }
439 // Update the new start position
440 *pos = line_end + 1;
441 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
442 --line_end;
443 }
444 *line = message.substr(line_begin, (line_end - line_begin));
445 const char* cline = line->c_str();
446 // RFC 4566
447 // An SDP session description consists of a number of lines of text of
448 // the form:
449 // <type>=<value>
450 // where <type> MUST be exactly one case-significant character and
451 // <value> is structured text whose format depends on <type>.
452 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000453 if (line->length() < 3 ||
454 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000455 cline[1] != kSdpDelimiterEqual ||
456 cline[2] == kSdpDelimiterSpace) {
457 *pos = line_begin;
458 return false;
459 }
460 return true;
461}
462
463// Init |os| to "|type|=|value|".
464static void InitLine(const char type,
465 const std::string& value,
466 std::ostringstream* os) {
467 os->str("");
468 *os << type << kSdpDelimiterEqual << value;
469}
470
471// Init |os| to "a=|attribute|".
472static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
473 InitLine(kLineTypeAttributes, attribute, os);
474}
475
476// Writes a SDP attribute line based on |attribute| and |value| to |message|.
477static void AddAttributeLine(const std::string& attribute, int value,
478 std::string* message) {
479 std::ostringstream os;
480 InitAttrLine(attribute, &os);
481 os << kSdpDelimiterColon << value;
482 AddLine(os.str(), message);
483}
484
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000485static bool IsLineType(const std::string& message,
486 const char type,
487 size_t line_start) {
488 if (message.size() < line_start + kLinePrefixLength) {
489 return false;
490 }
491 const char* cmessage = message.c_str();
492 return (cmessage[line_start] == type &&
493 cmessage[line_start + 1] == kSdpDelimiterEqual);
494}
495
496static bool IsLineType(const std::string& line,
497 const char type) {
498 return IsLineType(line, type, 0);
499}
500
501static bool GetLineWithType(const std::string& message, size_t* pos,
502 std::string* line, const char type) {
503 if (!IsLineType(message, type, *pos)) {
504 return false;
505 }
506
507 if (!GetLine(message, pos, line))
508 return false;
509
510 return true;
511}
512
513static bool HasAttribute(const std::string& line,
514 const std::string& attribute) {
515 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
516}
517
Peter Boström0c4e06b2015-10-07 12:23:21 +0200518static bool AddSsrcLine(uint32_t ssrc_id,
519 const std::string& attribute,
520 const std::string& value,
521 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000522 // RFC 5576
523 // a=ssrc:<ssrc-id> <attribute>:<value>
524 std::ostringstream os;
525 InitAttrLine(kAttributeSsrc, &os);
526 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
527 << attribute << kSdpDelimiterColon << value;
528 return AddLine(os.str(), message);
529}
530
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531// Get value only from <attribute>:<value>.
532static bool GetValue(const std::string& message, const std::string& attribute,
533 std::string* value, SdpParseError* error) {
534 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700535 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000536 return ParseFailedGetValue(message, attribute, error);
537 }
538 // The left part should end with the expected attribute.
539 if (leftpart.length() < attribute.length() ||
540 leftpart.compare(leftpart.length() - attribute.length(),
541 attribute.length(), attribute) != 0) {
542 return ParseFailedGetValue(message, attribute, error);
543 }
544 return true;
545}
546
547static bool CaseInsensitiveFind(std::string str1, std::string str2) {
548 std::transform(str1.begin(), str1.end(), str1.begin(),
549 ::tolower);
550 std::transform(str2.begin(), str2.end(), str2.begin(),
551 ::tolower);
552 return str1.find(str2) != std::string::npos;
553}
554
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000555template <class T>
556static bool GetValueFromString(const std::string& line,
557 const std::string& s,
558 T* t,
559 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000560 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000561 std::ostringstream description;
562 description << "Invalid value: " << s << ".";
563 return ParseFailed(line, description.str(), error);
564 }
565 return true;
566}
567
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000568static bool GetPayloadTypeFromString(const std::string& line,
569 const std::string& s,
570 int* payload_type,
571 SdpParseError* error) {
572 return GetValueFromString(line, s, payload_type, error) &&
573 cricket::IsValidRtpPayloadType(*payload_type);
574}
575
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800576// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
577// "a=msid" attribute, if it exists. They are empty if the attribute does not
578// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000579void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800580 const std::string& msid_stream_id,
581 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000582 StreamParamsVec* tracks) {
583 ASSERT(tracks != NULL);
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800584 ASSERT(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000585 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
586 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
587 if (ssrc_info->cname.empty()) {
588 continue;
589 }
590
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800591 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800593 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000594 // If there's no msid and there's mslabel, we consider this is a sdp from
595 // a older version of client that doesn't support msid.
596 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800597 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000598 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800599 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
600 // If there's no msid in the SSRC attributes, but there's a global one
601 // (from a=msid), use that. This is the case with unified plan SDP.
602 stream_id = msid_stream_id;
603 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000604 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800605 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800606 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000607 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800608 // If a stream/track ID wasn't populated from the SSRC attributes OR the
609 // msid attribute, use default/random values.
610 if (stream_id.empty()) {
611 stream_id = kDefaultMsid;
612 }
613 if (track_id.empty()) {
614 // TODO(ronghuawu): What should we do if the track id doesn't appear?
615 // Create random string (which will be used as track label later)?
616 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000617 }
618
619 StreamParamsVec::iterator track = tracks->begin();
620 for (; track != tracks->end(); ++track) {
621 if (track->id == track_id) {
622 break;
623 }
624 }
625 if (track == tracks->end()) {
626 // If we don't find an existing track, create a new one.
627 tracks->push_back(StreamParams());
628 track = tracks->end() - 1;
629 }
630 track->add_ssrc(ssrc_info->ssrc_id);
631 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800632 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000633 track->id = track_id;
634 }
635}
636
637void GetMediaStreamLabels(const ContentInfo* content,
638 std::set<std::string>* labels) {
639 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000640 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000641 content->description);
642 const cricket::StreamParamsVec& streams = media_desc->streams();
643 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
644 it != streams.end(); ++it) {
645 labels->insert(it->sync_label);
646 }
647}
648
649// RFC 5245
650// It is RECOMMENDED that default candidates be chosen based on the
651// likelihood of those candidates to work with the peer that is being
652// contacted. It is RECOMMENDED that relayed > reflexive > host.
653static const int kPreferenceUnknown = 0;
654static const int kPreferenceHost = 1;
655static const int kPreferenceReflexive = 2;
656static const int kPreferenceRelayed = 3;
657
658static int GetCandidatePreferenceFromType(const std::string& type) {
659 int preference = kPreferenceUnknown;
660 if (type == cricket::LOCAL_PORT_TYPE) {
661 preference = kPreferenceHost;
662 } else if (type == cricket::STUN_PORT_TYPE) {
663 preference = kPreferenceReflexive;
664 } else if (type == cricket::RELAY_PORT_TYPE) {
665 preference = kPreferenceRelayed;
666 } else {
667 ASSERT(false);
668 }
669 return preference;
670}
671
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000672// Get ip and port of the default destination from the |candidates| with the
673// given value of |component_id|. The default candidate should be the one most
674// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000675// RFC 5245
676// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
677// TODO: Decide the default destination in webrtcsession and
678// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000679static void GetDefaultDestination(
680 const std::vector<Candidate>& candidates,
681 int component_id, std::string* port,
682 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000683 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000684 *port = kDummyPort;
685 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000686 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000687 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000688 for (std::vector<Candidate>::const_iterator it = candidates.begin();
689 it != candidates.end(); ++it) {
690 if (it->component() != component_id) {
691 continue;
692 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000693 // Default destination should be UDP only.
694 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000695 continue;
696 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000697 const int preference = GetCandidatePreferenceFromType(it->type());
698 const int family = it->address().ipaddr().family();
699 // See if this candidate is more preferable then the current one if it's the
700 // same family. Or if the current family is IPv4 already so we could safely
701 // ignore all IPv6 ones. WebRTC bug 4269.
702 // http://code.google.com/p/webrtc/issues/detail?id=4269
703 if ((preference <= current_preference && current_family == family) ||
704 (current_family == AF_INET && family == AF_INET6)) {
705 continue;
706 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000707 if (family == AF_INET) {
708 addr_type->assign(kConnectionIpv4Addrtype);
709 } else if (family == AF_INET6) {
710 addr_type->assign(kConnectionIpv6Addrtype);
711 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000712 current_preference = preference;
713 current_family = family;
714 *port = it->address().PortAsString();
715 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000716 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000717}
718
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000719// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000720static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000721 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700722 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000723 std::string* message) {
724 std::string new_lines;
725 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000726 // RFC 4566
727 // m=<media> <port> <proto> <fmt> ...
728 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000729 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000730 if (fields.size() < 3) {
731 return;
732 }
733
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000734 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000735 std::string rtp_port, rtp_ip, addr_type;
736 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
737 &rtp_port, &rtp_ip, &addr_type);
738 // Found default RTP candidate.
739 // RFC 5245
740 // The default candidates are added to the SDP as the default
741 // destination for media. For streams based on RTP, this is done by
742 // placing the IP address and port of the RTP candidate into the c and m
743 // lines, respectively.
744 // Update the port in the m line.
745 // If this is a m-line with port equal to 0, we don't change it.
746 if (fields[1] != kMediaPortRejected) {
747 new_lines.replace(fields[0].size() + 1,
748 fields[1].size(),
749 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000750 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000751 // Add the c line.
752 // RFC 4566
753 // c=<nettype> <addrtype> <connection-address>
754 InitLine(kLineTypeConnection, kConnectionNettype, &os);
755 os << " " << addr_type << " " << rtp_ip;
756 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000757 message->append(new_lines);
758}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000759
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000760// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
761static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000762 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
763 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
764 &rtcp_port, &rtcp_ip, &addr_type);
765 // Found default RTCP candidate.
766 // RFC 5245
767 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
768 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000769
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000770 // RFC 3605
771 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
772 // connection-address] CRLF
773 std::ostringstream os;
774 InitAttrLine(kAttributeRtcp, &os);
775 os << kSdpDelimiterColon
776 << rtcp_port << " "
777 << kConnectionNettype << " "
778 << addr_type << " "
779 << rtcp_ip;
780 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000781 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000782}
783
784// Get candidates according to the mline index from SessionDescriptionInterface.
785static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
786 int mline_index,
787 std::vector<Candidate>* candidates) {
788 if (!candidates) {
789 return;
790 }
791 const IceCandidateCollection* cc = desci.candidates(mline_index);
792 for (size_t i = 0; i < cc->count(); ++i) {
793 const IceCandidateInterface* candidate = cc->at(i);
794 candidates->push_back(candidate->candidate());
795 }
796}
797
deadbeef9d3584c2016-02-16 17:54:10 -0800798std::string SdpSerialize(const JsepSessionDescription& jdesc,
799 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000800 const cricket::SessionDescription* desc = jdesc.description();
801 if (!desc) {
802 return "";
803 }
804
805 std::string message;
806
807 // Session Description.
808 AddLine(kSessionVersion, &message);
809 // Session Origin
810 // RFC 4566
811 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
812 // <unicast-address>
813 std::ostringstream os;
814 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700815 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000816 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700817 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000818 kSessionOriginSessionVersion : jdesc.session_version();
819 os << " " << session_id << " " << session_version << " "
820 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
821 << kSessionOriginAddress;
822 AddLine(os.str(), &message);
823 AddLine(kSessionName, &message);
824
825 // Time Description.
826 AddLine(kTimeDescription, &message);
827
828 // Group
829 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
830 std::string group_line = kAttrGroup;
831 const cricket::ContentGroup* group =
832 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
833 ASSERT(group != NULL);
834 const cricket::ContentNames& content_names = group->content_names();
835 for (cricket::ContentNames::const_iterator it = content_names.begin();
836 it != content_names.end(); ++it) {
837 group_line.append(" ");
838 group_line.append(*it);
839 }
840 AddLine(group_line, &message);
841 }
842
843 // MediaStream semantics
844 InitAttrLine(kAttributeMsidSemantics, &os);
845 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000846
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000847 std::set<std::string> media_stream_labels;
848 const ContentInfo* audio_content = GetFirstAudioContent(desc);
849 if (audio_content)
850 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000851
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000852 const ContentInfo* video_content = GetFirstVideoContent(desc);
853 if (video_content)
854 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000855
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000856 for (std::set<std::string>::const_iterator it =
857 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
858 os << " " << *it;
859 }
860 AddLine(os.str(), &message);
861
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000862 // Preserve the order of the media contents.
863 int mline_index = -1;
864 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
865 it != desc->contents().end(); ++it) {
866 const MediaContentDescription* mdesc =
867 static_cast<const MediaContentDescription*>(it->description);
868 std::vector<Candidate> candidates;
869 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800870 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
871 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000872 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000873 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000874 return message;
875}
876
877// Serializes the passed in IceCandidateInterface to a SDP string.
878// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700879std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
880 return SdpSerializeCandidate(candidate.candidate());
881}
882
883// Serializes a cricket Candidate.
884std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000885 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700886 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800887 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000888 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
889 // just candidate:<candidate> not a=candidate:<blah>CRLF
890 ASSERT(message.find("a=") == 0);
891 message.erase(0, 2);
892 ASSERT(message.find(kLineBreak) == message.size() - 2);
893 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000894 return message;
895}
896
897bool SdpDeserialize(const std::string& message,
898 JsepSessionDescription* jdesc,
899 SdpParseError* error) {
900 std::string session_id;
901 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700902 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000903 RtpHeaderExtensions session_extmaps;
904 cricket::SessionDescription* desc = new cricket::SessionDescription();
905 std::vector<JsepIceCandidate*> candidates;
906 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000907
908 // Session Description
909 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700910 &session_version, &session_td, &session_extmaps,
911 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000912 delete desc;
913 return false;
914 }
915
916 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700917 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
918 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000919 delete desc;
920 for (std::vector<JsepIceCandidate*>::const_iterator
921 it = candidates.begin(); it != candidates.end(); ++it) {
922 delete *it;
923 }
924 return false;
925 }
926
927 jdesc->Initialize(desc, session_id, session_version);
928
929 for (std::vector<JsepIceCandidate*>::const_iterator
930 it = candidates.begin(); it != candidates.end(); ++it) {
931 jdesc->AddCandidate(*it);
932 delete *it;
933 }
934 return true;
935}
936
937bool SdpDeserializeCandidate(const std::string& message,
938 JsepIceCandidate* jcandidate,
939 SdpParseError* error) {
940 ASSERT(jcandidate != NULL);
941 Candidate candidate;
942 if (!ParseCandidate(message, &candidate, error, true)) {
943 return false;
944 }
945 jcandidate->SetCandidate(candidate);
946 return true;
947}
948
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700949bool SdpDeserializeCandidate(const std::string& transport_name,
950 const std::string& message,
951 cricket::Candidate* candidate,
952 SdpParseError* error) {
953 ASSERT(candidate != nullptr);
954 if (!ParseCandidate(message, candidate, error, true)) {
955 return false;
956 }
957 candidate->set_transport_name(transport_name);
958 return true;
959}
960
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000961bool ParseCandidate(const std::string& message, Candidate* candidate,
962 SdpParseError* error, bool is_raw) {
963 ASSERT(candidate != NULL);
964
965 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000966 std::string first_line = message;
967 size_t pos = 0;
968 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000969
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000970 // Makes sure |message| contains only one line.
971 if (message.size() > first_line.size()) {
972 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700973 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
974 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000975 return ParseFailed(message, 0, "Expect one line only", error);
976 }
977 }
978
979 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
980 // candidate:<candidate> when trickled, but we still support
981 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
982 // from the SDP.
983 if (IsLineType(first_line, kLineTypeAttributes)) {
984 first_line = first_line.substr(kLinePrefixLength);
985 }
986
987 std::string attribute_candidate;
988 std::string candidate_value;
989
990 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700991 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
992 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000993 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000994 if (is_raw) {
995 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000996 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000997 << ":" << "<candidate-str>";
998 return ParseFailed(first_line, 0, description.str(), error);
999 } else {
1000 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
1001 kAttributeCandidate, error);
1002 }
1003 }
1004
1005 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001006 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1007
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001008 // RFC 5245
1009 // a=candidate:<foundation> <component-id> <transport> <priority>
1010 // <connection-address> <port> typ <candidate-types>
1011 // [raddr <connection-address>] [rport <port>]
1012 // *(SP extension-att-name SP extension-att-value)
1013 const size_t expected_min_fields = 8;
1014 if (fields.size() < expected_min_fields ||
1015 (fields[6] != kAttributeCandidateTyp)) {
1016 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1017 }
jbauch083b73f2015-07-16 02:46:32 -07001018 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001019
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001020 int component_id = 0;
1021 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1022 return false;
1023 }
jbauch083b73f2015-07-16 02:46:32 -07001024 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001025 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001026 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1027 return false;
1028 }
jbauch083b73f2015-07-16 02:46:32 -07001029 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001030 int port = 0;
1031 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1032 return false;
1033 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001034 SocketAddress address(connection_address, port);
1035
1036 cricket::ProtocolType protocol;
1037 if (!StringToProto(transport.c_str(), &protocol)) {
1038 return ParseFailed(first_line, "Unsupported transport type.", error);
1039 }
1040
1041 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001042 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001043 if (type == kCandidateHost) {
1044 candidate_type = cricket::LOCAL_PORT_TYPE;
1045 } else if (type == kCandidateSrflx) {
1046 candidate_type = cricket::STUN_PORT_TYPE;
1047 } else if (type == kCandidateRelay) {
1048 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001049 } else if (type == kCandidatePrflx) {
1050 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001051 } else {
1052 return ParseFailed(first_line, "Unsupported candidate type.", error);
1053 }
1054
1055 size_t current_position = expected_min_fields;
1056 SocketAddress related_address;
1057 // The 2 optional fields for related address
1058 // [raddr <connection-address>] [rport <port>]
1059 if (fields.size() >= (current_position + 2) &&
1060 fields[current_position] == kAttributeCandidateRaddr) {
1061 related_address.SetIP(fields[++current_position]);
1062 ++current_position;
1063 }
1064 if (fields.size() >= (current_position + 2) &&
1065 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001066 int port = 0;
1067 if (!GetValueFromString(
1068 first_line, fields[++current_position], &port, error)) {
1069 return false;
1070 }
1071 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001072 ++current_position;
1073 }
1074
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001075 // If this is a TCP candidate, it has additional extension as defined in
1076 // RFC 6544.
1077 std::string tcptype;
1078 if (fields.size() >= (current_position + 2) &&
1079 fields[current_position] == kTcpCandidateType) {
1080 tcptype = fields[++current_position];
1081 ++current_position;
1082
1083 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1084 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1085 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1086 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1087 }
1088
1089 if (protocol != cricket::PROTO_TCP) {
1090 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1091 }
1092 }
1093
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001094 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001095 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1096 // the candidate to avoid issues with confusing which generation a candidate
1097 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001098 std::string username;
1099 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001100 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001101 uint16_t network_id = 0;
1102 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001103 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1104 // RFC 5245
1105 // *(SP extension-att-name SP extension-att-value)
1106 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001107 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1108 return false;
1109 }
honghaiza54a0802015-12-16 18:37:23 -08001110 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001111 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001112 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001113 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001114 } else if (fields[i] == kAttributeCandidateNetworkId) {
1115 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1116 return false;
1117 }
honghaize1a0c942016-02-16 14:54:56 -08001118 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1119 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1120 return false;
1121 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001122 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001123 } else {
1124 // Skip the unknown extension.
1125 ++i;
1126 }
1127 }
1128
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001129 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001130 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001131 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001132 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001133 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001134 return true;
1135}
1136
1137bool ParseIceOptions(const std::string& line,
1138 std::vector<std::string>* transport_options,
1139 SdpParseError* error) {
1140 std::string ice_options;
1141 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1142 return false;
1143 }
1144 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001145 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001146 for (size_t i = 0; i < fields.size(); ++i) {
1147 transport_options->push_back(fields[i]);
1148 }
1149 return true;
1150}
1151
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001152bool ParseSctpPort(const std::string& line,
1153 int* sctp_port,
1154 SdpParseError* error) {
1155 // draft-ietf-mmusic-sctp-sdp-07
1156 // a=sctp-port
1157 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001158 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001159 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1160 if (fields.size() < expected_min_fields) {
1161 fields.resize(0);
1162 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1163 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001164 if (fields.size() < expected_min_fields) {
1165 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1166 }
1167 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001168 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001169 }
1170 return true;
1171}
1172
isheriff6f8d6862016-05-26 11:24:55 -07001173bool ParseExtmap(const std::string& line,
1174 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001175 SdpParseError* error) {
1176 // RFC 5285
1177 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1178 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001179 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001180 kSdpDelimiterSpace, &fields);
1181 const size_t expected_min_fields = 2;
1182 if (fields.size() < expected_min_fields) {
1183 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1184 }
1185 std::string uri = fields[1];
1186
1187 std::string value_direction;
1188 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1189 return false;
1190 }
1191 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001192 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001193 int value = 0;
1194 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1195 return false;
1196 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001197
isheriff6f8d6862016-05-26 11:24:55 -07001198 *extmap = RtpExtension(uri, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001199 return true;
1200}
1201
1202void BuildMediaDescription(const ContentInfo* content_info,
1203 const TransportInfo* transport_info,
1204 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001205 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001206 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001207 std::string* message) {
1208 ASSERT(message != NULL);
1209 if (content_info == NULL || message == NULL) {
1210 return;
1211 }
1212 // TODO: Rethink if we should use sprintfn instead of stringstream.
1213 // According to the style guide, streams should only be used for logging.
1214 // http://google-styleguide.googlecode.com/svn/
1215 // trunk/cppguide.xml?showone=Streams#Streams
1216 std::ostringstream os;
1217 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001218 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001219 content_info->description);
1220 ASSERT(media_desc != NULL);
1221
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001222 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001223
1224 // RFC 4566
1225 // m=<media> <port> <proto> <fmt>
1226 // fmt is a list of payload type numbers that MAY be used in the session.
1227 const char* type = NULL;
1228 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1229 type = kMediaTypeAudio;
1230 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1231 type = kMediaTypeVideo;
1232 else if (media_type == cricket::MEDIA_TYPE_DATA)
1233 type = kMediaTypeData;
1234 else
1235 ASSERT(false);
1236
1237 std::string fmt;
1238 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1239 const VideoContentDescription* video_desc =
1240 static_cast<const VideoContentDescription*>(media_desc);
1241 for (std::vector<cricket::VideoCodec>::const_iterator it =
1242 video_desc->codecs().begin();
1243 it != video_desc->codecs().end(); ++it) {
1244 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001245 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001246 }
1247 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1248 const AudioContentDescription* audio_desc =
1249 static_cast<const AudioContentDescription*>(media_desc);
1250 for (std::vector<cricket::AudioCodec>::const_iterator it =
1251 audio_desc->codecs().begin();
1252 it != audio_desc->codecs().end(); ++it) {
1253 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001254 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001255 }
1256 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001257 const DataContentDescription* data_desc =
1258 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001259 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001260 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001261
1262 for (std::vector<cricket::DataCodec>::const_iterator it =
1263 data_desc->codecs().begin();
1264 it != data_desc->codecs().end(); ++it) {
solenberg9fa49752016-10-08 13:02:44 -07001265 if (cricket::CodecNamesEq(it->name, cricket::kGoogleSctpDataCodecName)
1266 && it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001267 break;
1268 }
1269 }
1270
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001271 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001272 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001273 for (std::vector<cricket::DataCodec>::const_iterator it =
1274 data_desc->codecs().begin();
1275 it != data_desc->codecs().end(); ++it) {
1276 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001277 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001278 }
1279 }
1280 }
1281 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1282 // to 0.
1283 if (fmt.empty()) {
1284 fmt = " 0";
1285 }
1286
deadbeef25ed4352016-12-12 18:37:36 -08001287 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001288 // the candidates.
deadbeef25ed4352016-12-12 18:37:36 -08001289 //
1290 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001291 // RFC 3264
1292 // To reject an offered stream, the port number in the corresponding stream in
1293 // the answer MUST be set to zero.
deadbeef25ed4352016-12-12 18:37:36 -08001294 //
1295 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1296 // with a=bundle-only.
1297 const std::string& port =
1298 (content_info->rejected || content_info->bundle_only) ? kMediaPortRejected
1299 : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001300
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001301 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001302 transport_info->description.identity_fingerprint.get() : NULL;
1303
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001304 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001305 InitLine(kLineTypeMedia, type, &os);
1306 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001307 std::string mline = os.str();
1308 UpdateMediaDefaultDestination(candidates, mline, message);
1309
1310 // RFC 4566
1311 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001312 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001313 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1314 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1315 AddLine(os.str(), message);
1316 }
1317
deadbeef25ed4352016-12-12 18:37:36 -08001318 // Add the a=bundle-only line.
1319 if (content_info->bundle_only) {
1320 InitAttrLine(kAttributeBundleOnly, &os);
1321 AddLine(os.str(), message);
1322 }
1323
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001324 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001325 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001326 std::string rtcp_line = GetRtcpLine(candidates);
1327 if (!rtcp_line.empty()) {
1328 AddLine(rtcp_line, message);
1329 }
1330 }
1331
honghaiza54a0802015-12-16 18:37:23 -08001332 // Build the a=candidate lines. We don't include ufrag and pwd in the
1333 // candidates in the SDP to avoid redundancy.
1334 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001335
1336 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1337 if (transport_info) {
1338 // RFC 5245
1339 // ice-pwd-att = "ice-pwd" ":" password
1340 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1341 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001342 if (!transport_info->description.ice_ufrag.empty()) {
1343 InitAttrLine(kAttributeIceUfrag, &os);
1344 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1345 AddLine(os.str(), message);
1346 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001347 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001348 if (!transport_info->description.ice_pwd.empty()) {
1349 InitAttrLine(kAttributeIcePwd, &os);
1350 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1351 AddLine(os.str(), message);
1352 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001353
1354 // draft-petithuguenin-mmusic-ice-attributes-level-03
1355 BuildIceOptions(transport_info->description.transport_options, message);
1356
1357 // RFC 4572
1358 // fingerprint-attribute =
1359 // "fingerprint" ":" hash-func SP fingerprint
1360 if (fp) {
1361 // Insert the fingerprint attribute.
1362 InitAttrLine(kAttributeFingerprint, &os);
1363 os << kSdpDelimiterColon
1364 << fp->algorithm << kSdpDelimiterSpace
1365 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001366 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001367
1368 // Inserting setup attribute.
1369 if (transport_info->description.connection_role !=
1370 cricket::CONNECTIONROLE_NONE) {
1371 // Making sure we are not using "passive" mode.
1372 cricket::ConnectionRole role =
1373 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001374 std::string dtls_role_str;
1375 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001376 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001377 os << kSdpDelimiterColon << dtls_role_str;
1378 AddLine(os.str(), message);
1379 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001380 }
1381 }
1382
1383 // RFC 3388
1384 // mid-attribute = "a=mid:" identification-tag
1385 // identification-tag = token
1386 // Use the content name as the mid identification-tag.
1387 InitAttrLine(kAttributeMid, &os);
1388 os << kSdpDelimiterColon << content_info->name;
1389 AddLine(os.str(), message);
1390
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001391 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001392 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001393 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001394 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1395 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001396 }
1397}
1398
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001399void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001400 // draft-ietf-mmusic-sctp-sdp-04
1401 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001402 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1403 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001404 std::ostringstream os;
1405 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001406 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001407 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -07001408 << cricket::kMaxSctpStreams;
wu@webrtc.org78187522013-10-07 23:32:02 +00001409 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001410}
1411
deadbeef9d3584c2016-02-16 17:54:10 -08001412// If unified_plan_sdp is true, will use "a=msid".
1413void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1414 const MediaType media_type,
1415 bool unified_plan_sdp,
1416 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001417 std::ostringstream os;
1418 // RFC 5285
1419 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1420 // The definitions MUST be either all session level or all media level. This
1421 // implementation uses all media level.
1422 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1423 InitAttrLine(kAttributeExtmap, &os);
1424 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1425 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1426 AddLine(os.str(), message);
1427 }
1428
1429 // RFC 3264
1430 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001431 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001432 case cricket::MD_INACTIVE:
1433 InitAttrLine(kAttributeInactive, &os);
1434 break;
1435 case cricket::MD_SENDONLY:
1436 InitAttrLine(kAttributeSendOnly, &os);
1437 break;
1438 case cricket::MD_RECVONLY:
1439 InitAttrLine(kAttributeRecvOnly, &os);
1440 break;
1441 case cricket::MD_SENDRECV:
1442 default:
1443 InitAttrLine(kAttributeSendRecv, &os);
1444 break;
1445 }
1446 AddLine(os.str(), message);
1447
deadbeef9d3584c2016-02-16 17:54:10 -08001448 // draft-ietf-mmusic-msid-11
1449 // a=msid:<stream id> <track id>
1450 if (unified_plan_sdp && !media_desc->streams().empty()) {
1451 if (media_desc->streams().size() > 1u) {
1452 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1453 << "one track in a media section. Omitting 'a=msid'.";
1454 } else {
1455 auto track = media_desc->streams().begin();
1456 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001457 InitAttrLine(kAttributeMsid, &os);
1458 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1459 AddLine(os.str(), message);
1460 }
1461 }
1462
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001463 // RFC 5761
1464 // a=rtcp-mux
1465 if (media_desc->rtcp_mux()) {
1466 InitAttrLine(kAttributeRtcpMux, &os);
1467 AddLine(os.str(), message);
1468 }
1469
deadbeef13871492015-12-09 12:37:51 -08001470 // RFC 5506
1471 // a=rtcp-rsize
1472 if (media_desc->rtcp_reduced_size()) {
1473 InitAttrLine(kAttributeRtcpReducedSize, &os);
1474 AddLine(os.str(), message);
1475 }
1476
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001477 // RFC 4568
1478 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1479 for (std::vector<CryptoParams>::const_iterator it =
1480 media_desc->cryptos().begin();
1481 it != media_desc->cryptos().end(); ++it) {
1482 InitAttrLine(kAttributeCrypto, &os);
1483 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1484 << it->key_params;
1485 if (!it->session_params.empty()) {
1486 os << " " << it->session_params;
1487 }
1488 AddLine(os.str(), message);
1489 }
1490
1491 // RFC 4566
1492 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1493 // [/<encodingparameters>]
1494 BuildRtpMap(media_desc, media_type, message);
1495
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001496 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1497 track != media_desc->streams().end(); ++track) {
1498 // Require that the track belongs to a media stream,
1499 // ie the sync_label is set. This extra check is necessary since the
1500 // MediaContentDescription always contains a streamparam with an ssrc even
1501 // if no track or media stream have been created.
1502 if (track->sync_label.empty()) continue;
1503
1504 // Build the ssrc-group lines.
1505 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1506 // RFC 5576
1507 // a=ssrc-group:<semantics> <ssrc-id> ...
1508 if (track->ssrc_groups[i].ssrcs.empty()) {
1509 continue;
1510 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001511 InitAttrLine(kAttributeSsrcGroup, &os);
1512 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001513 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001514 track->ssrc_groups[i].ssrcs.begin();
1515 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001516 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001517 }
1518 AddLine(os.str(), message);
1519 }
1520 // Build the ssrc lines for each ssrc.
1521 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001522 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001523 // RFC 5576
1524 // a=ssrc:<ssrc-id> cname:<value>
1525 AddSsrcLine(ssrc, kSsrcAttributeCname,
1526 track->cname, message);
1527
1528 // draft-alvestrand-mmusic-msid-00
1529 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001530 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1531 // which corresponds to the "id" attribute of StreamParams.
1532 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001533 InitAttrLine(kAttributeSsrc, &os);
1534 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001535 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1536 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001537 AddLine(os.str(), message);
1538
deadbeef9d3584c2016-02-16 17:54:10 -08001539 // TODO(ronghuawu): Remove below code which is for backward
1540 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001541 // draft-alvestrand-rtcweb-mid-01
1542 // a=ssrc:<ssrc-id> mslabel:<value>
1543 // The label isn't yet defined.
1544 // a=ssrc:<ssrc-id> label:<value>
1545 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1546 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1547 }
1548 }
1549}
1550
1551void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1552 // fmtp header: a=fmtp:|payload_type| <parameters>
1553 // Add a=fmtp
1554 InitAttrLine(kAttributeFmtp, os);
1555 // Add :|payload_type|
1556 *os << kSdpDelimiterColon << payload_type;
1557}
1558
1559void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1560 // rtcp-fb header: a=rtcp-fb:|payload_type|
1561 // <parameters>/<ccm <ccm_parameters>>
1562 // Add a=rtcp-fb
1563 InitAttrLine(kAttributeRtcpFb, os);
1564 // Add :
1565 *os << kSdpDelimiterColon;
1566 if (payload_type == kWildcardPayloadType) {
1567 *os << "*";
1568 } else {
1569 *os << payload_type;
1570 }
1571}
1572
1573void WriteFmtpParameter(const std::string& parameter_name,
1574 const std::string& parameter_value,
1575 std::ostringstream* os) {
1576 // fmtp parameters: |parameter_name|=|parameter_value|
1577 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1578}
1579
1580void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1581 std::ostringstream* os) {
1582 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1583 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 11:02:14 -07001584 // Parameters are a semicolon-separated list, no spaces.
1585 // The list is separated from the header by a space.
1586 if (fmtp == parameters.begin()) {
1587 *os << kSdpDelimiterSpace;
1588 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001589 *os << kSdpDelimiterSemicolon;
1590 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001591 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1592 }
1593}
1594
1595bool IsFmtpParam(const std::string& name) {
1596 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001597 // TODO(hta): Split FMTP parameters apart from parameters in general.
1598 // FMTP parameters are codec specific, not generic.
1599 kCodecParamMinPTime,
1600 kCodecParamSPropStereo,
1601 kCodecParamStereo,
1602 kCodecParamUseInbandFec,
1603 kCodecParamUseDtx,
1604 kCodecParamStartBitrate,
1605 kCodecParamMaxBitrate,
1606 kCodecParamMinBitrate,
1607 kCodecParamMaxQuantization,
1608 kCodecParamSctpProtocol,
1609 kCodecParamSctpStreams,
1610 kCodecParamMaxAverageBitrate,
1611 kCodecParamMaxPlaybackRate,
1612 kCodecParamAssociatedPayloadType,
1613 cricket::kH264FmtpPacketizationMode,
1614 cricket::kH264FmtpLevelAsymmetryAllowed,
brandtr87d7d772016-11-07 03:03:41 -08001615 cricket::kH264FmtpProfileLevelId,
1616 cricket::kFlexfecFmtpRepairWindow};
tfarina5237aaf2015-11-10 23:44:30 -08001617 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001618 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001619 return true;
1620 }
1621 }
1622 return false;
1623}
1624
1625// Retreives fmtp parameters from |params|, which may contain other parameters
1626// as well, and puts them in |fmtp_parameters|.
1627void GetFmtpParams(const cricket::CodecParameterMap& params,
1628 cricket::CodecParameterMap* fmtp_parameters) {
1629 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1630 iter != params.end(); ++iter) {
1631 if (IsFmtpParam(iter->first)) {
1632 (*fmtp_parameters)[iter->first] = iter->second;
1633 }
1634 }
1635}
1636
1637template <class T>
1638void AddFmtpLine(const T& codec, std::string* message) {
1639 cricket::CodecParameterMap fmtp_parameters;
1640 GetFmtpParams(codec.params, &fmtp_parameters);
1641 if (fmtp_parameters.empty()) {
1642 // No need to add an fmtp if it will have no (optional) parameters.
1643 return;
1644 }
1645 std::ostringstream os;
1646 WriteFmtpHeader(codec.id, &os);
1647 WriteFmtpParameters(fmtp_parameters, &os);
1648 AddLine(os.str(), message);
1649 return;
1650}
1651
1652template <class T>
1653void AddRtcpFbLines(const T& codec, std::string* message) {
1654 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1655 codec.feedback_params.params().begin();
1656 iter != codec.feedback_params.params().end(); ++iter) {
1657 std::ostringstream os;
1658 WriteRtcpFbHeader(codec.id, &os);
1659 os << " " << iter->id();
1660 if (!iter->param().empty()) {
1661 os << " " << iter->param();
1662 }
1663 AddLine(os.str(), message);
1664 }
1665}
1666
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001667bool AddSctpDataCodec(DataContentDescription* media_desc,
1668 int sctp_port) {
solenberg9fa49752016-10-08 13:02:44 -07001669 for (const auto& codec : media_desc->codecs()) {
1670 if (cricket::CodecNamesEq(codec.name, cricket::kGoogleSctpDataCodecName)) {
1671 return ParseFailed("",
1672 "Can't have multiple sctp port attributes.",
1673 NULL);
1674 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001675 }
1676 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 13:02:44 -07001677 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 10:07:16 -07001678 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001679 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1680 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1681 << sctp_port;
1682 media_desc->AddCodec(codec_port);
1683 return true;
1684}
1685
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001686bool GetMinValue(const std::vector<int>& values, int* value) {
1687 if (values.empty()) {
1688 return false;
1689 }
1690 std::vector<int>::const_iterator found =
1691 std::min_element(values.begin(), values.end());
1692 *value = *found;
1693 return true;
1694}
1695
1696bool GetParameter(const std::string& name,
1697 const cricket::CodecParameterMap& params, int* value) {
1698 std::map<std::string, std::string>::const_iterator found =
1699 params.find(name);
1700 if (found == params.end()) {
1701 return false;
1702 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001703 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001704 return false;
1705 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001706 return true;
1707}
1708
1709void BuildRtpMap(const MediaContentDescription* media_desc,
1710 const MediaType media_type,
1711 std::string* message) {
1712 ASSERT(message != NULL);
1713 ASSERT(media_desc != NULL);
1714 std::ostringstream os;
1715 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1716 const VideoContentDescription* video_desc =
1717 static_cast<const VideoContentDescription*>(media_desc);
1718 for (std::vector<cricket::VideoCodec>::const_iterator it =
1719 video_desc->codecs().begin();
1720 it != video_desc->codecs().end(); ++it) {
1721 // RFC 4566
1722 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1723 // [/<encodingparameters>]
1724 if (it->id != kWildcardPayloadType) {
1725 InitAttrLine(kAttributeRtpmap, &os);
1726 os << kSdpDelimiterColon << it->id << " " << it->name
1727 << "/" << kDefaultVideoClockrate;
1728 AddLine(os.str(), message);
1729 }
1730 AddRtcpFbLines(*it, message);
1731 AddFmtpLine(*it, message);
1732 }
1733 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1734 const AudioContentDescription* audio_desc =
1735 static_cast<const AudioContentDescription*>(media_desc);
1736 std::vector<int> ptimes;
1737 std::vector<int> maxptimes;
1738 int max_minptime = 0;
1739 for (std::vector<cricket::AudioCodec>::const_iterator it =
1740 audio_desc->codecs().begin();
1741 it != audio_desc->codecs().end(); ++it) {
1742 ASSERT(!it->name.empty());
1743 // RFC 4566
1744 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1745 // [/<encodingparameters>]
1746 InitAttrLine(kAttributeRtpmap, &os);
1747 os << kSdpDelimiterColon << it->id << " ";
1748 os << it->name << "/" << it->clockrate;
1749 if (it->channels != 1) {
1750 os << "/" << it->channels;
1751 }
1752 AddLine(os.str(), message);
1753 AddRtcpFbLines(*it, message);
1754 AddFmtpLine(*it, message);
1755 int minptime = 0;
1756 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1757 max_minptime = std::max(minptime, max_minptime);
1758 }
1759 int ptime;
1760 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1761 ptimes.push_back(ptime);
1762 }
1763 int maxptime;
1764 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1765 maxptimes.push_back(maxptime);
1766 }
1767 }
1768 // Populate the maxptime attribute with the smallest maxptime of all codecs
1769 // under the same m-line.
1770 int min_maxptime = INT_MAX;
1771 if (GetMinValue(maxptimes, &min_maxptime)) {
1772 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1773 }
1774 ASSERT(min_maxptime > max_minptime);
1775 // Populate the ptime attribute with the smallest ptime or the largest
1776 // minptime, whichever is the largest, for all codecs under the same m-line.
1777 int ptime = INT_MAX;
1778 if (GetMinValue(ptimes, &ptime)) {
1779 ptime = std::min(ptime, min_maxptime);
1780 ptime = std::max(ptime, max_minptime);
1781 AddAttributeLine(kCodecParamPTime, ptime, message);
1782 }
1783 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1784 const DataContentDescription* data_desc =
1785 static_cast<const DataContentDescription*>(media_desc);
1786 for (std::vector<cricket::DataCodec>::const_iterator it =
1787 data_desc->codecs().begin();
1788 it != data_desc->codecs().end(); ++it) {
1789 // RFC 4566
1790 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1791 // [/<encodingparameters>]
1792 InitAttrLine(kAttributeRtpmap, &os);
1793 os << kSdpDelimiterColon << it->id << " "
1794 << it->name << "/" << it->clockrate;
1795 AddLine(os.str(), message);
1796 }
1797 }
1798}
1799
1800void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001801 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001802 std::string* message) {
1803 std::ostringstream os;
1804
1805 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1806 it != candidates.end(); ++it) {
1807 // RFC 5245
1808 // a=candidate:<foundation> <component-id> <transport> <priority>
1809 // <connection-address> <port> typ <candidate-types>
1810 // [raddr <connection-address>] [rport <port>]
1811 // *(SP extension-att-name SP extension-att-value)
1812 std::string type;
1813 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1814 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1815 type = kCandidateHost;
1816 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1817 type = kCandidateSrflx;
1818 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1819 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001820 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1821 type = kCandidatePrflx;
1822 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001823 } else {
1824 ASSERT(false);
Peter Thatcher019087f2015-04-28 09:06:26 -07001825 // Never write out candidates if we don't know the type.
1826 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001827 }
1828
1829 InitAttrLine(kAttributeCandidate, &os);
1830 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001831 << it->foundation() << " "
1832 << it->component() << " "
1833 << it->protocol() << " "
1834 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001835 << it->address().ipaddr().ToString() << " "
1836 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001837 << kAttributeCandidateTyp << " "
1838 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001839
1840 // Related address
1841 if (!it->related_address().IsNil()) {
1842 os << kAttributeCandidateRaddr << " "
1843 << it->related_address().ipaddr().ToString() << " "
1844 << kAttributeCandidateRport << " "
1845 << it->related_address().PortAsString() << " ";
1846 }
1847
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001848 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001849 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001850 }
1851
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001852 // Extensions
1853 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001854 if (include_ufrag && !it->username().empty()) {
1855 os << " " << kAttributeCandidateUfrag << " " << it->username();
1856 }
honghaiza0c44ea2016-03-23 16:07:48 -07001857 if (it->network_id() > 0) {
1858 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1859 }
honghaize1a0c942016-02-16 14:54:56 -08001860 if (it->network_cost() > 0) {
1861 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1862 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001863
1864 AddLine(os.str(), message);
1865 }
1866}
1867
1868void BuildIceOptions(const std::vector<std::string>& transport_options,
1869 std::string* message) {
1870 if (!transport_options.empty()) {
1871 std::ostringstream os;
1872 InitAttrLine(kAttributeIceOption, &os);
1873 os << kSdpDelimiterColon << transport_options[0];
1874 for (size_t i = 1; i < transport_options.size(); ++i) {
1875 os << kSdpDelimiterSpace << transport_options[i];
1876 }
1877 AddLine(os.str(), message);
1878 }
1879}
1880
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001881bool IsRtp(const std::string& protocol) {
1882 return protocol.empty() ||
1883 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1884}
1885
1886bool IsDtlsSctp(const std::string& protocol) {
1887 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001888 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001889}
1890
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001891bool ParseSessionDescription(const std::string& message, size_t* pos,
1892 std::string* session_id,
1893 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001894 TransportDescription* session_td,
1895 RtpHeaderExtensions* session_extmaps,
1896 cricket::SessionDescription* desc,
1897 SdpParseError* error) {
1898 std::string line;
1899
deadbeefc80741f2015-10-22 13:14:45 -07001900 desc->set_msid_supported(false);
1901
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001902 // RFC 4566
1903 // v= (protocol version)
1904 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1905 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1906 std::string(), error);
1907 }
1908 // RFC 4566
1909 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1910 // <unicast-address>
1911 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1912 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1913 std::string(), error);
1914 }
1915 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001916 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001917 kSdpDelimiterSpace, &fields);
1918 const size_t expected_fields = 6;
1919 if (fields.size() != expected_fields) {
1920 return ParseFailedExpectFieldNum(line, expected_fields, error);
1921 }
1922 *session_id = fields[1];
1923 *session_version = fields[2];
1924
1925 // RFC 4566
1926 // s= (session name)
1927 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1928 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1929 std::string(), error);
1930 }
1931
1932 // Optional lines
1933 // Those are the optional lines, so shouldn't return false if not present.
1934 // RFC 4566
1935 // i=* (session information)
1936 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1937
1938 // RFC 4566
1939 // u=* (URI of description)
1940 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1941
1942 // RFC 4566
1943 // e=* (email address)
1944 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1945
1946 // RFC 4566
1947 // p=* (phone number)
1948 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1949
1950 // RFC 4566
1951 // c=* (connection information -- not required if included in
1952 // all media)
1953 GetLineWithType(message, pos, &line, kLineTypeConnection);
1954
1955 // RFC 4566
1956 // b=* (zero or more bandwidth information lines)
1957 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1958 // By pass zero or more b lines.
1959 }
1960
1961 // RFC 4566
1962 // One or more time descriptions ("t=" and "r=" lines; see below)
1963 // t= (time the session is active)
1964 // r=* (zero or more repeat times)
1965 // Ensure there's at least one time description
1966 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1967 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1968 error);
1969 }
1970
1971 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1972 // By pass zero or more r lines.
1973 }
1974
1975 // Go through the rest of the time descriptions
1976 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1977 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1978 // By pass zero or more r lines.
1979 }
1980 }
1981
1982 // RFC 4566
1983 // z=* (time zone adjustments)
1984 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1985
1986 // RFC 4566
1987 // k=* (encryption key)
1988 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1989
1990 // RFC 4566
1991 // a=* (zero or more session attribute lines)
1992 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1993 if (HasAttribute(line, kAttributeGroup)) {
1994 if (!ParseGroupAttribute(line, desc, error)) {
1995 return false;
1996 }
1997 } else if (HasAttribute(line, kAttributeIceUfrag)) {
1998 if (!GetValue(line, kAttributeIceUfrag,
1999 &(session_td->ice_ufrag), error)) {
2000 return false;
2001 }
2002 } else if (HasAttribute(line, kAttributeIcePwd)) {
2003 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2004 return false;
2005 }
2006 } else if (HasAttribute(line, kAttributeIceLite)) {
2007 session_td->ice_mode = cricket::ICEMODE_LITE;
2008 } else if (HasAttribute(line, kAttributeIceOption)) {
2009 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2010 return false;
2011 }
2012 } else if (HasAttribute(line, kAttributeFingerprint)) {
2013 if (session_td->identity_fingerprint.get()) {
2014 return ParseFailed(
2015 line,
2016 "Can't have multiple fingerprint attributes at the same level.",
2017 error);
2018 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002019 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002020 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2021 return false;
2022 }
2023 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002024 } else if (HasAttribute(line, kAttributeSetup)) {
2025 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2026 return false;
2027 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002028 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2029 std::string semantics;
2030 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2031 return false;
2032 }
deadbeefc80741f2015-10-22 13:14:45 -07002033 desc->set_msid_supported(
2034 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002035 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002036 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002037 if (!ParseExtmap(line, &extmap, error)) {
2038 return false;
2039 }
2040 session_extmaps->push_back(extmap);
2041 }
2042 }
2043
2044 return true;
2045}
2046
2047bool ParseGroupAttribute(const std::string& line,
2048 cricket::SessionDescription* desc,
2049 SdpParseError* error) {
2050 ASSERT(desc != NULL);
2051
2052 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2053 // a=group:BUNDLE video voice
2054 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002055 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002056 kSdpDelimiterSpace, &fields);
2057 std::string semantics;
2058 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2059 return false;
2060 }
2061 cricket::ContentGroup group(semantics);
2062 for (size_t i = 1; i < fields.size(); ++i) {
2063 group.AddContentName(fields[i]);
2064 }
2065 desc->AddGroup(group);
2066 return true;
2067}
2068
2069static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002070 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002071 SdpParseError* error) {
2072 if (!IsLineType(line, kLineTypeAttributes) ||
2073 !HasAttribute(line, kAttributeFingerprint)) {
2074 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2075 kAttributeFingerprint, error);
2076 }
2077
2078 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002079 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002080 kSdpDelimiterSpace, &fields);
2081 const size_t expected_fields = 2;
2082 if (fields.size() != expected_fields) {
2083 return ParseFailedExpectFieldNum(line, expected_fields, error);
2084 }
2085
2086 // The first field here is "fingerprint:<hash>.
2087 std::string algorithm;
2088 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2089 return false;
2090 }
2091
2092 // Downcase the algorithm. Note that we don't need to downcase the
2093 // fingerprint because hex_decode can handle upper-case.
2094 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2095 ::tolower);
2096
2097 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002098 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002099 algorithm, fields[1]);
2100 if (!*fingerprint) {
2101 return ParseFailed(line,
2102 "Failed to create fingerprint from the digest.",
2103 error);
2104 }
2105
2106 return true;
2107}
2108
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002109static bool ParseDtlsSetup(const std::string& line,
2110 cricket::ConnectionRole* role,
2111 SdpParseError* error) {
2112 // setup-attr = "a=setup:" role
2113 // role = "active" / "passive" / "actpass" / "holdconn"
2114 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002115 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002116 const size_t expected_fields = 2;
2117 if (fields.size() != expected_fields) {
2118 return ParseFailedExpectFieldNum(line, expected_fields, error);
2119 }
2120 std::string role_str = fields[1];
2121 if (!cricket::StringToConnectionRole(role_str, role)) {
2122 return ParseFailed(line, "Invalid attribute value.", error);
2123 }
2124 return true;
2125}
2126
deadbeef9d3584c2016-02-16 17:54:10 -08002127static bool ParseMsidAttribute(const std::string& line,
2128 std::string* stream_id,
2129 std::string* track_id,
2130 SdpParseError* error) {
2131 // draft-ietf-mmusic-msid-11
2132 // a=msid:<stream id> <track id>
2133 // msid-value = msid-id [ SP msid-appdata ]
2134 // msid-id = 1*64token-char ; see RFC 4566
2135 // msid-appdata = 1*64token-char ; see RFC 4566
2136 std::string field1;
2137 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2138 &field1, track_id)) {
2139 const size_t expected_fields = 2;
2140 return ParseFailedExpectFieldNum(line, expected_fields, error);
2141 }
2142
2143 // msid:<msid-id>
2144 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2145 return false;
2146 }
2147 return true;
2148}
2149
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002150// RFC 3551
2151// PT encoding media type clock rate channels
2152// name (Hz)
2153// 0 PCMU A 8,000 1
2154// 1 reserved A
2155// 2 reserved A
2156// 3 GSM A 8,000 1
2157// 4 G723 A 8,000 1
2158// 5 DVI4 A 8,000 1
2159// 6 DVI4 A 16,000 1
2160// 7 LPC A 8,000 1
2161// 8 PCMA A 8,000 1
2162// 9 G722 A 8,000 1
2163// 10 L16 A 44,100 2
2164// 11 L16 A 44,100 1
2165// 12 QCELP A 8,000 1
2166// 13 CN A 8,000 1
2167// 14 MPA A 90,000 (see text)
2168// 15 G728 A 8,000 1
2169// 16 DVI4 A 11,025 1
2170// 17 DVI4 A 22,050 1
2171// 18 G729 A 8,000 1
2172struct StaticPayloadAudioCodec {
2173 const char* name;
2174 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002175 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002176};
2177static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2178 { "PCMU", 8000, 1 },
2179 { "reserved", 0, 0 },
2180 { "reserved", 0, 0 },
2181 { "GSM", 8000, 1 },
2182 { "G723", 8000, 1 },
2183 { "DVI4", 8000, 1 },
2184 { "DVI4", 16000, 1 },
2185 { "LPC", 8000, 1 },
2186 { "PCMA", 8000, 1 },
2187 { "G722", 8000, 1 },
2188 { "L16", 44100, 2 },
2189 { "L16", 44100, 1 },
2190 { "QCELP", 8000, 1 },
2191 { "CN", 8000, 1 },
2192 { "MPA", 90000, 1 },
2193 { "G728", 8000, 1 },
2194 { "DVI4", 11025, 1 },
2195 { "DVI4", 22050, 1 },
2196 { "G729", 8000, 1 },
2197};
2198
2199void MaybeCreateStaticPayloadAudioCodecs(
2200 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2201 if (!media_desc) {
2202 return;
2203 }
deadbeef67cf2c12016-04-13 10:07:16 -07002204 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002205 for (int payload_type : fmts) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002206 if (!media_desc->HasCodec(payload_type) &&
2207 payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002208 static_cast<uint32_t>(payload_type) <
2209 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002210 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2211 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002212 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002213 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002214 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002215 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002216 }
2217}
2218
2219template <class C>
2220static C* ParseContentDescription(const std::string& message,
2221 const MediaType media_type,
2222 int mline_index,
2223 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002224 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002225 size_t* pos,
2226 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002227 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002228 TransportDescription* transport,
2229 std::vector<JsepIceCandidate*>* candidates,
2230 webrtc::SdpParseError* error) {
2231 C* media_desc = new C();
2232 switch (media_type) {
2233 case cricket::MEDIA_TYPE_AUDIO:
2234 *content_name = cricket::CN_AUDIO;
2235 break;
2236 case cricket::MEDIA_TYPE_VIDEO:
2237 *content_name = cricket::CN_VIDEO;
2238 break;
2239 case cricket::MEDIA_TYPE_DATA:
2240 *content_name = cricket::CN_DATA;
2241 break;
2242 default:
2243 ASSERT(false);
2244 break;
2245 }
deadbeef67cf2c12016-04-13 10:07:16 -07002246 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
deadbeef25ed4352016-12-12 18:37:36 -08002247 pos, content_name, bundle_only, media_desc, transport,
2248 candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002249 delete media_desc;
2250 return NULL;
2251 }
2252 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002253 std::unordered_map<int, int> payload_type_preferences;
2254 // "size + 1" so that the lowest preference payload type has a preference of
2255 // 1, which is greater than the default (0) for payload types not in the fmt
2256 // list.
2257 int preference = static_cast<int>(payload_types.size() + 1);
2258 for (int pt : payload_types) {
2259 payload_type_preferences[pt] = preference--;
2260 }
2261 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2262 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2263 const typename C::CodecType& a,
2264 const typename C::CodecType& b) {
2265 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2266 });
2267 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002268 return media_desc;
2269}
2270
2271bool ParseMediaDescription(const std::string& message,
2272 const TransportDescription& session_td,
2273 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002274 size_t* pos,
2275 cricket::SessionDescription* desc,
2276 std::vector<JsepIceCandidate*>* candidates,
2277 SdpParseError* error) {
2278 ASSERT(desc != NULL);
2279 std::string line;
2280 int mline_index = -1;
2281
2282 // Zero or more media descriptions
2283 // RFC 4566
2284 // m=<media> <port> <proto> <fmt>
2285 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2286 ++mline_index;
2287
2288 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002289 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002290 kSdpDelimiterSpace, &fields);
2291 const size_t expected_min_fields = 4;
2292 if (fields.size() < expected_min_fields) {
2293 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2294 }
deadbeef25ed4352016-12-12 18:37:36 -08002295 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002296 // RFC 3264
2297 // To reject an offered stream, the port number in the corresponding stream
2298 // in the answer MUST be set to zero.
2299 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-12 18:37:36 -08002300 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002301 }
2302
2303 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002304
2305 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002306 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002307 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002308 for (size_t j = 3 ; j < fields.size(); ++j) {
2309 // TODO(wu): Remove when below bug is fixed.
2310 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002311 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002312 continue;
2313 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002314
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002315 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002316 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002317 return false;
2318 }
deadbeef67cf2c12016-04-13 10:07:16 -07002319 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002320 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002321 }
2322
2323 // Make a temporary TransportDescription based on |session_td|.
2324 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002325 TransportDescription transport(
2326 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2327 session_td.ice_mode, session_td.connection_role,
2328 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002329
kwibergd1fe2812016-04-27 06:47:29 -07002330 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002331 std::string content_name;
deadbeef25ed4352016-12-12 18:37:36 -08002332 bool bundle_only = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002333 if (HasAttribute(line, kMediaTypeVideo)) {
2334 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002335 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002336 payload_types, pos, &content_name, &bundle_only, &transport,
2337 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002338 } else if (HasAttribute(line, kMediaTypeAudio)) {
2339 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002340 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002341 payload_types, pos, &content_name, &bundle_only, &transport,
2342 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002343 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002344 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002345 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002346 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002347 payload_types, pos, &content_name, &bundle_only, &transport,
2348 candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002349 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002350
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002351 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002352 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002353 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002354 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002355 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002356 } else {
2357 LOG(LS_WARNING) << "Unsupported media type: " << line;
2358 continue;
2359 }
2360 if (!content.get()) {
2361 // ParseContentDescription returns NULL if failed.
2362 return false;
2363 }
2364
deadbeef25ed4352016-12-12 18:37:36 -08002365 bool content_rejected = false;
2366 if (bundle_only) {
2367 // A port of 0 is not interpreted as a rejected m= section when it's
2368 // used along with a=bundle-only.
2369 if (!port_rejected) {
2370 return ParseFailed(
2371 "a=bundle-only",
2372 "a=bundle-only MUST only be used in combination with a 0 port.",
2373 error);
2374 }
2375 } else {
2376 // If not using bundle-only, interpret port 0 in the normal way; the m=
2377 // section is being rejected.
2378 content_rejected = port_rejected;
2379 }
2380
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002381 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002382 // Set the extmap.
2383 if (!session_extmaps.empty() &&
2384 !content->rtp_header_extensions().empty()) {
2385 return ParseFailed("",
2386 "The a=extmap MUST be either all session level or "
2387 "all media level.",
2388 error);
2389 }
2390 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2391 content->AddRtpHeaderExtension(session_extmaps[i]);
2392 }
2393 }
2394 content->set_protocol(protocol);
2395 desc->AddContent(content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002396 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP
2397 : cricket::NS_JINGLE_RTP,
2398 content_rejected, bundle_only, content.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002399 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2400 TransportInfo transport_info(content_name, transport);
2401
2402 if (!desc->AddTransportInfo(transport_info)) {
2403 std::ostringstream description;
2404 description << "Failed to AddTransportInfo with content name: "
2405 << content_name;
2406 return ParseFailed("", description.str(), error);
2407 }
2408 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002409
2410 size_t end_of_message = message.size();
2411 if (mline_index == -1 && *pos != end_of_message) {
2412 ParseFailed(message, *pos, "Expects m line.", error);
2413 return false;
2414 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002415 return true;
2416}
2417
2418bool VerifyCodec(const cricket::Codec& codec) {
2419 // Codec has not been populated correctly unless the name has been set. This
2420 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2421 // have a corresponding "rtpmap" line.
htab39db842016-12-08 01:50:48 -08002422 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002423}
2424
2425bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2426 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2427 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2428 iter != codecs.end(); ++iter) {
2429 if (!VerifyCodec(*iter)) {
2430 return false;
2431 }
2432 }
2433 return true;
2434}
2435
2436bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2437 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2438 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2439 iter != codecs.end(); ++iter) {
2440 if (!VerifyCodec(*iter)) {
2441 return false;
2442 }
2443 }
2444 return true;
2445}
2446
2447void AddParameters(const cricket::CodecParameterMap& parameters,
2448 cricket::Codec* codec) {
2449 for (cricket::CodecParameterMap::const_iterator iter =
2450 parameters.begin(); iter != parameters.end(); ++iter) {
2451 codec->SetParam(iter->first, iter->second);
2452 }
2453}
2454
2455void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2456 cricket::Codec* codec) {
2457 codec->AddFeedbackParam(feedback_param);
2458}
2459
2460void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2461 cricket::Codec* codec) {
2462 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2463 feedback_params.params().begin();
2464 iter != feedback_params.params().end(); ++iter) {
2465 codec->AddFeedbackParam(*iter);
2466 }
2467}
2468
2469// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002470// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002471// with that payload type.
2472template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002473T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 04:00:16 -08002474 const T* codec = FindCodecById(codecs, payload_type);
2475 if (codec)
2476 return *codec;
2477 // Return empty codec with |payload_type|.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002478 T ret_val;
magjedb05fa242016-11-11 04:00:16 -08002479 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002480 return ret_val;
2481}
2482
2483// Updates or creates a new codec entry in the audio description.
2484template <class T, class U>
2485void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2486 T* desc = static_cast<T*>(content_desc);
2487 std::vector<U> codecs = desc->codecs();
2488 bool found = false;
2489
2490 typename std::vector<U>::iterator iter;
2491 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2492 if (iter->id == codec.id) {
2493 *iter = codec;
2494 found = true;
2495 break;
2496 }
2497 }
2498 if (!found) {
2499 desc->AddCodec(codec);
2500 return;
2501 }
2502 desc->set_codecs(codecs);
2503}
2504
2505// Adds or updates existing codec corresponding to |payload_type| according
2506// to |parameters|.
2507template <class T, class U>
2508void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2509 const cricket::CodecParameterMap& parameters) {
2510 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002511 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2512 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002513 AddParameters(parameters, &new_codec);
2514 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2515}
2516
2517// Adds or updates existing codec corresponding to |payload_type| according
2518// to |feedback_param|.
2519template <class T, class U>
2520void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2521 const cricket::FeedbackParam& feedback_param) {
2522 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002523 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2524 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002525 AddFeedbackParameter(feedback_param, &new_codec);
2526 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2527}
2528
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002529template <class T>
2530bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2531 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002532 if (iter->id == kWildcardPayloadType) {
2533 *wildcard_codec = *iter;
2534 codecs->erase(iter);
2535 return true;
2536 }
2537 }
2538 return false;
2539}
2540
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002541template<class T>
2542void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2543 auto codecs = desc->codecs();
2544 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002545 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2546 return;
2547 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002548 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002549 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2550 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002551 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002552}
2553
2554void AddAudioAttribute(const std::string& name, const std::string& value,
2555 AudioContentDescription* audio_desc) {
2556 if (value.empty()) {
2557 return;
2558 }
2559 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2560 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2561 iter != codecs.end(); ++iter) {
2562 iter->params[name] = value;
2563 }
2564 audio_desc->set_codecs(codecs);
2565}
2566
2567bool ParseContent(const std::string& message,
2568 const MediaType media_type,
2569 int mline_index,
2570 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002571 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002572 size_t* pos,
2573 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002574 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002575 MediaContentDescription* media_desc,
2576 TransportDescription* transport,
2577 std::vector<JsepIceCandidate*>* candidates,
2578 SdpParseError* error) {
2579 ASSERT(media_desc != NULL);
2580 ASSERT(content_name != NULL);
2581 ASSERT(transport != NULL);
2582
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002583 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2584 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002585 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002586 }
2587
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002588 // The media level "ice-ufrag" and "ice-pwd".
2589 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2590 Candidates candidates_orig;
2591 std::string line;
2592 std::string mline_id;
2593 // Tracks created out of the ssrc attributes.
2594 StreamParamsVec tracks;
2595 SsrcInfoVec ssrc_infos;
2596 SsrcGroupVec ssrc_groups;
2597 std::string maxptime_as_string;
2598 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002599 std::string stream_id;
2600 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002601
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002602 // Loop until the next m line
2603 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2604 if (!GetLine(message, pos, &line)) {
2605 if (*pos >= message.size()) {
2606 break; // Done parsing
2607 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002608 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002609 }
2610 }
2611
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002612 // RFC 4566
2613 // b=* (zero or more bandwidth information lines)
2614 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2615 std::string bandwidth;
2616 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2617 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2618 return false;
2619 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002620 int b = 0;
2621 if (!GetValueFromString(line, bandwidth, &b, error)) {
2622 return false;
2623 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002624 // We should never use more than the default bandwidth for RTP-based
2625 // data channels. Don't allow SDP to set the bandwidth, because
2626 // that would give JS the opportunity to "break the Internet".
2627 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2628 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2629 b > cricket::kDataMaxBandwidth / 1000) {
2630 std::ostringstream description;
2631 description << "RTP-based data channels may not send more than "
2632 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2633 return ParseFailed(line, description.str(), error);
2634 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002635 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002636 }
2637 }
2638 continue;
2639 }
2640
2641 if (!IsLineType(line, kLineTypeAttributes)) {
2642 // TODO: Handle other lines if needed.
2643 LOG(LS_INFO) << "Ignored line: " << line;
2644 continue;
2645 }
2646
2647 // Handle attributes common to SCTP and RTP.
2648 if (HasAttribute(line, kAttributeMid)) {
2649 // RFC 3388
2650 // mid-attribute = "a=mid:" identification-tag
2651 // identification-tag = token
2652 // Use the mid identification-tag as the content name.
2653 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2654 return false;
2655 }
2656 *content_name = mline_id;
deadbeef25ed4352016-12-12 18:37:36 -08002657 } else if (HasAttribute(line, kAttributeBundleOnly)) {
2658 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002659 } else if (HasAttribute(line, kAttributeCandidate)) {
2660 Candidate candidate;
2661 if (!ParseCandidate(line, &candidate, error, false)) {
2662 return false;
2663 }
2664 candidates_orig.push_back(candidate);
2665 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2666 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2667 return false;
2668 }
2669 } else if (HasAttribute(line, kAttributeIcePwd)) {
2670 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2671 return false;
2672 }
2673 } else if (HasAttribute(line, kAttributeIceOption)) {
2674 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2675 return false;
2676 }
2677 } else if (HasAttribute(line, kAttributeFmtp)) {
2678 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2679 return false;
2680 }
2681 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002682 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002683
2684 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2685 return false;
2686 }
2687 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002688 } else if (HasAttribute(line, kAttributeSetup)) {
2689 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2690 return false;
2691 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002692 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 10:04:34 -07002693 if (media_type != cricket::MEDIA_TYPE_DATA) {
2694 return ParseFailed(
2695 line, "sctp-port attribute found in non-data media description.",
2696 error);
2697 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002698 int sctp_port;
2699 if (!ParseSctpPort(line, &sctp_port, error)) {
2700 return false;
2701 }
2702 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2703 sctp_port)) {
2704 return false;
2705 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002706 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002707 //
2708 // RTP specific attrubtes
2709 //
2710 if (HasAttribute(line, kAttributeRtcpMux)) {
2711 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002712 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2713 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002714 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2715 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2716 return false;
2717 }
2718 } else if (HasAttribute(line, kAttributeSsrc)) {
2719 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2720 return false;
2721 }
2722 } else if (HasAttribute(line, kAttributeCrypto)) {
2723 if (!ParseCryptoAttribute(line, media_desc, error)) {
2724 return false;
2725 }
2726 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002727 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2728 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002729 return false;
2730 }
2731 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2732 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2733 return false;
2734 }
2735 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2736 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2737 return false;
2738 }
2739 } else if (HasAttribute(line, kCodecParamPTime)) {
2740 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2741 return false;
2742 }
2743 } else if (HasAttribute(line, kAttributeSendOnly)) {
2744 media_desc->set_direction(cricket::MD_SENDONLY);
2745 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2746 media_desc->set_direction(cricket::MD_RECVONLY);
2747 } else if (HasAttribute(line, kAttributeInactive)) {
2748 media_desc->set_direction(cricket::MD_INACTIVE);
2749 } else if (HasAttribute(line, kAttributeSendRecv)) {
2750 media_desc->set_direction(cricket::MD_SENDRECV);
2751 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002752 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002753 if (!ParseExtmap(line, &extmap, error)) {
2754 return false;
2755 }
2756 media_desc->AddRtpHeaderExtension(extmap);
2757 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2758 // Experimental attribute. Conference mode activates more aggressive
2759 // AEC and NS settings.
2760 // TODO: expose API to set these directly.
2761 std::string flag_value;
2762 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2763 return false;
2764 }
2765 if (flag_value.compare(kValueConference) == 0)
2766 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002767 } else if (HasAttribute(line, kAttributeMsid)) {
2768 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2769 return false;
2770 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002771 }
2772 } else {
2773 // Only parse lines that we are interested of.
2774 LOG(LS_INFO) << "Ignored line: " << line;
2775 continue;
2776 }
2777 }
2778
2779 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002780 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2781 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2782 // the m= section.
2783 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002784
2785 // Add the ssrc group to the track.
2786 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2787 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2788 if (ssrc_group->ssrcs.empty()) {
2789 continue;
2790 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002791 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002792 for (StreamParamsVec::iterator track = tracks.begin();
2793 track != tracks.end(); ++track) {
2794 if (track->has_ssrc(ssrc)) {
2795 track->ssrc_groups.push_back(*ssrc_group);
2796 }
2797 }
2798 }
2799
2800 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002801 for (StreamParams& track : tracks) {
2802 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002803 }
2804
2805 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2806 AudioContentDescription* audio_desc =
2807 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002808 UpdateFromWildcardCodecs(audio_desc);
2809
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002810 // Verify audio codec ensures that no audio codec has been populated with
2811 // only fmtp.
2812 if (!VerifyAudioCodecs(audio_desc)) {
2813 return ParseFailed("Failed to parse audio codecs correctly.", error);
2814 }
2815 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2816 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2817 }
2818
2819 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002820 VideoContentDescription* video_desc =
2821 static_cast<VideoContentDescription*>(media_desc);
2822 UpdateFromWildcardCodecs(video_desc);
2823 // Verify video codec ensures that no video codec has been populated with
2824 // only rtcp-fb.
2825 if (!VerifyVideoCodecs(video_desc)) {
2826 return ParseFailed("Failed to parse video codecs correctly.", error);
2827 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002828 }
2829
2830 // RFC 5245
2831 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2832 for (Candidates::iterator it = candidates_orig.begin();
2833 it != candidates_orig.end(); ++it) {
honghaiza54a0802015-12-16 18:37:23 -08002834 ASSERT((*it).username().empty() ||
2835 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002836 (*it).set_username(transport->ice_ufrag);
2837 ASSERT((*it).password().empty());
2838 (*it).set_password(transport->ice_pwd);
2839 candidates->push_back(
2840 new JsepIceCandidate(mline_id, mline_index, *it));
2841 }
2842 return true;
2843}
2844
2845bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2846 SdpParseError* error) {
2847 ASSERT(ssrc_infos != NULL);
2848 // RFC 5576
2849 // a=ssrc:<ssrc-id> <attribute>
2850 // a=ssrc:<ssrc-id> <attribute>:<value>
2851 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002852 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2853 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002854 const size_t expected_fields = 2;
2855 return ParseFailedExpectFieldNum(line, expected_fields, error);
2856 }
2857
2858 // ssrc:<ssrc-id>
2859 std::string ssrc_id_s;
2860 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2861 return false;
2862 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002863 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002864 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2865 return false;
2866 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002867
2868 std::string attribute;
2869 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002870 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002871 std::ostringstream description;
2872 description << "Failed to get the ssrc attribute value from " << field2
2873 << ". Expected format <attribute>:<value>.";
2874 return ParseFailed(line, description.str(), error);
2875 }
2876
2877 // Check if there's already an item for this |ssrc_id|. Create a new one if
2878 // there isn't.
2879 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2880 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2881 if (ssrc_info->ssrc_id == ssrc_id) {
2882 break;
2883 }
2884 }
2885 if (ssrc_info == ssrc_infos->end()) {
2886 SsrcInfo info;
2887 info.ssrc_id = ssrc_id;
2888 ssrc_infos->push_back(info);
2889 ssrc_info = ssrc_infos->end() - 1;
2890 }
2891
2892 // Store the info to the |ssrc_info|.
2893 if (attribute == kSsrcAttributeCname) {
2894 // RFC 5576
2895 // cname:<value>
2896 ssrc_info->cname = value;
2897 } else if (attribute == kSsrcAttributeMsid) {
2898 // draft-alvestrand-mmusic-msid-00
2899 // "msid:" identifier [ " " appdata ]
2900 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002901 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002902 if (fields.size() < 1 || fields.size() > 2) {
2903 return ParseFailed(line,
2904 "Expected format \"msid:<identifier>[ <appdata>]\".",
2905 error);
2906 }
deadbeef9d3584c2016-02-16 17:54:10 -08002907 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002908 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002909 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002910 }
2911 } else if (attribute == kSsrcAttributeMslabel) {
2912 // draft-alvestrand-rtcweb-mid-01
2913 // mslabel:<value>
2914 ssrc_info->mslabel = value;
2915 } else if (attribute == kSSrcAttributeLabel) {
2916 // The label isn't defined.
2917 // label:<value>
2918 ssrc_info->label = value;
2919 }
2920 return true;
2921}
2922
2923bool ParseSsrcGroupAttribute(const std::string& line,
2924 SsrcGroupVec* ssrc_groups,
2925 SdpParseError* error) {
2926 ASSERT(ssrc_groups != NULL);
2927 // RFC 5576
2928 // a=ssrc-group:<semantics> <ssrc-id> ...
2929 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002930 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002931 kSdpDelimiterSpace, &fields);
2932 const size_t expected_min_fields = 2;
2933 if (fields.size() < expected_min_fields) {
2934 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2935 }
2936 std::string semantics;
2937 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2938 return false;
2939 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002940 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002941 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002942 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002943 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2944 return false;
2945 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002946 ssrcs.push_back(ssrc);
2947 }
2948 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2949 return true;
2950}
2951
2952bool ParseCryptoAttribute(const std::string& line,
2953 MediaContentDescription* media_desc,
2954 SdpParseError* error) {
2955 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002956 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002957 kSdpDelimiterSpace, &fields);
2958 // RFC 4568
2959 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2960 const size_t expected_min_fields = 3;
2961 if (fields.size() < expected_min_fields) {
2962 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2963 }
2964 std::string tag_value;
2965 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2966 return false;
2967 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002968 int tag = 0;
2969 if (!GetValueFromString(line, tag_value, &tag, error)) {
2970 return false;
2971 }
jbauch083b73f2015-07-16 02:46:32 -07002972 const std::string& crypto_suite = fields[1];
2973 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002974 std::string session_params;
2975 if (fields.size() > 3) {
2976 session_params = fields[3];
2977 }
2978 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2979 session_params));
2980 return true;
2981}
2982
2983// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07002984// to |name|, |clockrate|, |bitrate|, and |channels|.
2985void UpdateCodec(int payload_type,
2986 const std::string& name,
2987 int clockrate,
2988 int bitrate,
2989 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002990 AudioContentDescription* audio_desc) {
2991 // Codec may already be populated with (only) optional parameters
2992 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002993 cricket::AudioCodec codec =
2994 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002995 codec.name = name;
2996 codec.clockrate = clockrate;
2997 codec.bitrate = bitrate;
2998 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002999 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3000 codec);
3001}
3002
3003// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07003004// |name|, |width|, |height|, and |framerate|.
3005void UpdateCodec(int payload_type,
3006 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003007 VideoContentDescription* video_desc) {
3008 // Codec may already be populated with (only) optional parameters
3009 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003010 cricket::VideoCodec codec =
3011 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003012 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003013 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3014 codec);
3015}
3016
3017bool ParseRtpmapAttribute(const std::string& line,
3018 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07003019 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003020 MediaContentDescription* media_desc,
3021 SdpParseError* error) {
3022 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003023 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003024 kSdpDelimiterSpace, &fields);
3025 // RFC 4566
3026 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3027 const size_t expected_min_fields = 2;
3028 if (fields.size() < expected_min_fields) {
3029 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3030 }
3031 std::string payload_type_value;
3032 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3033 return false;
3034 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003035 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003036 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3037 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003038 return false;
3039 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003040
deadbeef67cf2c12016-04-13 10:07:16 -07003041 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3042 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003043 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3044 << "<fmt> of the m-line: " << line;
3045 return true;
3046 }
jbauch083b73f2015-07-16 02:46:32 -07003047 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003048 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003049 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003050 // <encoding name>/<clock rate>[/<encodingparameters>]
3051 // 2 mandatory fields
3052 if (codec_params.size() < 2 || codec_params.size() > 3) {
3053 return ParseFailed(line,
3054 "Expected format \"<encoding name>/<clock rate>"
3055 "[/<encodingparameters>]\".",
3056 error);
3057 }
jbauch083b73f2015-07-16 02:46:32 -07003058 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003059 int clock_rate = 0;
3060 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3061 return false;
3062 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003063 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3064 VideoContentDescription* video_desc =
3065 static_cast<VideoContentDescription*>(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003066 UpdateCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07003067 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003068 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3069 // RFC 4566
3070 // For audio streams, <encoding parameters> indicates the number
3071 // of audio channels. This parameter is OPTIONAL and may be
3072 // omitted if the number of channels is one, provided that no
3073 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003074 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003075 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003076 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3077 return false;
3078 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003079 }
3080 int bitrate = 0;
3081 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3082 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3083 // The bandwidth adaptation doesn't always work well, so this code
3084 // sets a fixed target bitrate instead.
3085 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3086 if (clock_rate <= 16000) {
3087 bitrate = kIsacWbDefaultRate;
3088 } else {
3089 bitrate = kIsacSwbDefaultRate;
3090 }
3091 }
3092 AudioContentDescription* audio_desc =
3093 static_cast<AudioContentDescription*>(media_desc);
3094 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003095 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003096 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3097 DataContentDescription* data_desc =
3098 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003099 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003100 }
3101 return true;
3102}
3103
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003104bool ParseFmtpParam(const std::string& line, std::string* parameter,
3105 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003106 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003107 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3108 return false;
3109 }
3110 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003111 return true;
3112}
3113
3114bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3115 MediaContentDescription* media_desc,
3116 SdpParseError* error) {
3117 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3118 media_type != cricket::MEDIA_TYPE_VIDEO) {
3119 return true;
3120 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003121
3122 std::string line_payload;
3123 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003124
3125 // RFC 5576
3126 // a=fmtp:<format> <format specific parameters>
3127 // At least two fields, whereas the second one is any of the optional
3128 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003129 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3130 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003131 ParseFailedExpectMinFieldNum(line, 2, error);
3132 return false;
3133 }
3134
Donald Curtis0e07f922015-05-15 09:21:23 -07003135 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003136 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003137 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003138 return false;
3139 }
3140
Donald Curtis0e07f922015-05-15 09:21:23 -07003141 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003142 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3143 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003144 return false;
3145 }
3146
3147 // Parse out format specific parameters.
3148 std::vector<std::string> fields;
3149 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3150
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003151 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003152 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003153 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003154 // Only fmtps with equals are currently supported. Other fmtp types
3155 // should be ignored. Unknown fmtps do not constitute an error.
3156 continue;
3157 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003158
3159 std::string name;
3160 std::string value;
3161 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003162 return false;
3163 }
3164 codec_params[name] = value;
3165 }
3166
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003167 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3168 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003169 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003170 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3171 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003172 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003173 }
3174 return true;
3175}
3176
3177bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3178 MediaContentDescription* media_desc,
3179 SdpParseError* error) {
3180 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3181 media_type != cricket::MEDIA_TYPE_VIDEO) {
3182 return true;
3183 }
3184 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003185 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003186 if (rtcp_fb_fields.size() < 2) {
3187 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3188 }
3189 std::string payload_type_string;
3190 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3191 error)) {
3192 return false;
3193 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003194 int payload_type = kWildcardPayloadType;
3195 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003196 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3197 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003198 return false;
3199 }
3200 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003201 std::string id = rtcp_fb_fields[1];
3202 std::string param = "";
3203 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3204 iter != rtcp_fb_fields.end(); ++iter) {
3205 param.append(*iter);
3206 }
3207 const cricket::FeedbackParam feedback_param(id, param);
3208
3209 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003210 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3211 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003212 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003213 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3214 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003215 }
3216 return true;
3217}
3218
3219} // namespace webrtc