blob: 2c1ba640f8e7d1bcca654a09cb188bf2d6bfb1f8 [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"
deadbeef953c2ce2017-01-09 14:53:41 -080036#include "webrtc/media/sctp/sctptransportinternal.h"
kjellandera96e2d72016-02-04 23:52:28 -080037#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;
hnslb68cc752016-12-13 10:33:41 -08001037 if (!StringToProto(transport.c_str(), &protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001038 return ParseFailed(first_line, "Unsupported transport type.", error);
1039 }
hnslb68cc752016-12-13 10:33:41 -08001040 switch (protocol) {
1041 case cricket::PROTO_UDP:
1042 case cricket::PROTO_TCP:
1043 case cricket::PROTO_SSLTCP:
1044 // Supported protocol.
1045 break;
1046 default:
1047 return ParseFailed(first_line, "Unsupported transport type.", error);
1048 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001049
1050 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001051 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001052 if (type == kCandidateHost) {
1053 candidate_type = cricket::LOCAL_PORT_TYPE;
1054 } else if (type == kCandidateSrflx) {
1055 candidate_type = cricket::STUN_PORT_TYPE;
1056 } else if (type == kCandidateRelay) {
1057 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001058 } else if (type == kCandidatePrflx) {
1059 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001060 } else {
1061 return ParseFailed(first_line, "Unsupported candidate type.", error);
1062 }
1063
1064 size_t current_position = expected_min_fields;
1065 SocketAddress related_address;
1066 // The 2 optional fields for related address
1067 // [raddr <connection-address>] [rport <port>]
1068 if (fields.size() >= (current_position + 2) &&
1069 fields[current_position] == kAttributeCandidateRaddr) {
1070 related_address.SetIP(fields[++current_position]);
1071 ++current_position;
1072 }
1073 if (fields.size() >= (current_position + 2) &&
1074 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001075 int port = 0;
1076 if (!GetValueFromString(
1077 first_line, fields[++current_position], &port, error)) {
1078 return false;
1079 }
1080 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001081 ++current_position;
1082 }
1083
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001084 // If this is a TCP candidate, it has additional extension as defined in
1085 // RFC 6544.
1086 std::string tcptype;
1087 if (fields.size() >= (current_position + 2) &&
1088 fields[current_position] == kTcpCandidateType) {
1089 tcptype = fields[++current_position];
1090 ++current_position;
1091
1092 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1093 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1094 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1095 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1096 }
1097
1098 if (protocol != cricket::PROTO_TCP) {
1099 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1100 }
1101 }
1102
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001103 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001104 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1105 // the candidate to avoid issues with confusing which generation a candidate
1106 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001107 std::string username;
1108 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001109 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001110 uint16_t network_id = 0;
1111 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001112 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1113 // RFC 5245
1114 // *(SP extension-att-name SP extension-att-value)
1115 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001116 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1117 return false;
1118 }
honghaiza54a0802015-12-16 18:37:23 -08001119 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001120 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001121 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001122 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001123 } else if (fields[i] == kAttributeCandidateNetworkId) {
1124 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1125 return false;
1126 }
honghaize1a0c942016-02-16 14:54:56 -08001127 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1128 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1129 return false;
1130 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001131 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001132 } else {
1133 // Skip the unknown extension.
1134 ++i;
1135 }
1136 }
1137
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001138 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001139 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001140 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001141 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001142 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001143 return true;
1144}
1145
1146bool ParseIceOptions(const std::string& line,
1147 std::vector<std::string>* transport_options,
1148 SdpParseError* error) {
1149 std::string ice_options;
1150 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1151 return false;
1152 }
1153 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001154 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001155 for (size_t i = 0; i < fields.size(); ++i) {
1156 transport_options->push_back(fields[i]);
1157 }
1158 return true;
1159}
1160
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001161bool ParseSctpPort(const std::string& line,
1162 int* sctp_port,
1163 SdpParseError* error) {
1164 // draft-ietf-mmusic-sctp-sdp-07
1165 // a=sctp-port
1166 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001167 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001168 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1169 if (fields.size() < expected_min_fields) {
1170 fields.resize(0);
1171 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1172 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001173 if (fields.size() < expected_min_fields) {
1174 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1175 }
1176 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001177 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001178 }
1179 return true;
1180}
1181
isheriff6f8d6862016-05-26 11:24:55 -07001182bool ParseExtmap(const std::string& line,
1183 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001184 SdpParseError* error) {
1185 // RFC 5285
1186 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1187 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001188 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001189 kSdpDelimiterSpace, &fields);
1190 const size_t expected_min_fields = 2;
1191 if (fields.size() < expected_min_fields) {
1192 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1193 }
1194 std::string uri = fields[1];
1195
1196 std::string value_direction;
1197 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1198 return false;
1199 }
1200 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001201 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001202 int value = 0;
1203 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1204 return false;
1205 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001206
isheriff6f8d6862016-05-26 11:24:55 -07001207 *extmap = RtpExtension(uri, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001208 return true;
1209}
1210
1211void BuildMediaDescription(const ContentInfo* content_info,
1212 const TransportInfo* transport_info,
1213 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001214 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001215 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001216 std::string* message) {
1217 ASSERT(message != NULL);
1218 if (content_info == NULL || message == NULL) {
1219 return;
1220 }
1221 // TODO: Rethink if we should use sprintfn instead of stringstream.
1222 // According to the style guide, streams should only be used for logging.
1223 // http://google-styleguide.googlecode.com/svn/
1224 // trunk/cppguide.xml?showone=Streams#Streams
1225 std::ostringstream os;
1226 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001227 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001228 content_info->description);
1229 ASSERT(media_desc != NULL);
1230
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001231 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001232
1233 // RFC 4566
1234 // m=<media> <port> <proto> <fmt>
1235 // fmt is a list of payload type numbers that MAY be used in the session.
1236 const char* type = NULL;
1237 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1238 type = kMediaTypeAudio;
1239 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1240 type = kMediaTypeVideo;
1241 else if (media_type == cricket::MEDIA_TYPE_DATA)
1242 type = kMediaTypeData;
1243 else
1244 ASSERT(false);
1245
1246 std::string fmt;
1247 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1248 const VideoContentDescription* video_desc =
1249 static_cast<const VideoContentDescription*>(media_desc);
1250 for (std::vector<cricket::VideoCodec>::const_iterator it =
1251 video_desc->codecs().begin();
1252 it != video_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_AUDIO) {
1257 const AudioContentDescription* audio_desc =
1258 static_cast<const AudioContentDescription*>(media_desc);
1259 for (std::vector<cricket::AudioCodec>::const_iterator it =
1260 audio_desc->codecs().begin();
1261 it != audio_desc->codecs().end(); ++it) {
1262 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001263 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001264 }
1265 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001266 const DataContentDescription* data_desc =
1267 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001268 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001269 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001270
1271 for (std::vector<cricket::DataCodec>::const_iterator it =
1272 data_desc->codecs().begin();
1273 it != data_desc->codecs().end(); ++it) {
solenberg9fa49752016-10-08 13:02:44 -07001274 if (cricket::CodecNamesEq(it->name, cricket::kGoogleSctpDataCodecName)
1275 && it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001276 break;
1277 }
1278 }
1279
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001280 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001281 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001282 for (std::vector<cricket::DataCodec>::const_iterator it =
1283 data_desc->codecs().begin();
1284 it != data_desc->codecs().end(); ++it) {
1285 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001286 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001287 }
1288 }
1289 }
1290 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1291 // to 0.
1292 if (fmt.empty()) {
1293 fmt = " 0";
1294 }
1295
deadbeef25ed4352016-12-12 18:37:36 -08001296 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001297 // the candidates.
deadbeef25ed4352016-12-12 18:37:36 -08001298 //
1299 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001300 // RFC 3264
1301 // To reject an offered stream, the port number in the corresponding stream in
1302 // the answer MUST be set to zero.
deadbeef25ed4352016-12-12 18:37:36 -08001303 //
1304 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1305 // with a=bundle-only.
1306 const std::string& port =
1307 (content_info->rejected || content_info->bundle_only) ? kMediaPortRejected
1308 : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001309
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001310 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001311 transport_info->description.identity_fingerprint.get() : NULL;
1312
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001313 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001314 InitLine(kLineTypeMedia, type, &os);
1315 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001316 std::string mline = os.str();
1317 UpdateMediaDefaultDestination(candidates, mline, message);
1318
1319 // RFC 4566
1320 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001321 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001322 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1323 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1324 AddLine(os.str(), message);
1325 }
1326
deadbeef25ed4352016-12-12 18:37:36 -08001327 // Add the a=bundle-only line.
1328 if (content_info->bundle_only) {
1329 InitAttrLine(kAttributeBundleOnly, &os);
1330 AddLine(os.str(), message);
1331 }
1332
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001333 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001334 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001335 std::string rtcp_line = GetRtcpLine(candidates);
1336 if (!rtcp_line.empty()) {
1337 AddLine(rtcp_line, message);
1338 }
1339 }
1340
honghaiza54a0802015-12-16 18:37:23 -08001341 // Build the a=candidate lines. We don't include ufrag and pwd in the
1342 // candidates in the SDP to avoid redundancy.
1343 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001344
1345 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1346 if (transport_info) {
1347 // RFC 5245
1348 // ice-pwd-att = "ice-pwd" ":" password
1349 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1350 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001351 if (!transport_info->description.ice_ufrag.empty()) {
1352 InitAttrLine(kAttributeIceUfrag, &os);
1353 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1354 AddLine(os.str(), message);
1355 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001356 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001357 if (!transport_info->description.ice_pwd.empty()) {
1358 InitAttrLine(kAttributeIcePwd, &os);
1359 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1360 AddLine(os.str(), message);
1361 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001362
1363 // draft-petithuguenin-mmusic-ice-attributes-level-03
1364 BuildIceOptions(transport_info->description.transport_options, message);
1365
1366 // RFC 4572
1367 // fingerprint-attribute =
1368 // "fingerprint" ":" hash-func SP fingerprint
1369 if (fp) {
1370 // Insert the fingerprint attribute.
1371 InitAttrLine(kAttributeFingerprint, &os);
1372 os << kSdpDelimiterColon
1373 << fp->algorithm << kSdpDelimiterSpace
1374 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001375 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001376
1377 // Inserting setup attribute.
1378 if (transport_info->description.connection_role !=
1379 cricket::CONNECTIONROLE_NONE) {
1380 // Making sure we are not using "passive" mode.
1381 cricket::ConnectionRole role =
1382 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001383 std::string dtls_role_str;
1384 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001385 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001386 os << kSdpDelimiterColon << dtls_role_str;
1387 AddLine(os.str(), message);
1388 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001389 }
1390 }
1391
1392 // RFC 3388
1393 // mid-attribute = "a=mid:" identification-tag
1394 // identification-tag = token
1395 // Use the content name as the mid identification-tag.
1396 InitAttrLine(kAttributeMid, &os);
1397 os << kSdpDelimiterColon << content_info->name;
1398 AddLine(os.str(), message);
1399
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001400 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001401 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001402 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001403 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1404 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001405 }
1406}
1407
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001408void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001409 // draft-ietf-mmusic-sctp-sdp-04
1410 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001411 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1412 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001413 std::ostringstream os;
1414 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001415 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001416 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -07001417 << cricket::kMaxSctpStreams;
wu@webrtc.org78187522013-10-07 23:32:02 +00001418 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001419}
1420
deadbeef9d3584c2016-02-16 17:54:10 -08001421// If unified_plan_sdp is true, will use "a=msid".
1422void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1423 const MediaType media_type,
1424 bool unified_plan_sdp,
1425 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001426 std::ostringstream os;
1427 // RFC 5285
1428 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1429 // The definitions MUST be either all session level or all media level. This
1430 // implementation uses all media level.
1431 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1432 InitAttrLine(kAttributeExtmap, &os);
1433 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1434 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1435 AddLine(os.str(), message);
1436 }
1437
1438 // RFC 3264
1439 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001440 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001441 case cricket::MD_INACTIVE:
1442 InitAttrLine(kAttributeInactive, &os);
1443 break;
1444 case cricket::MD_SENDONLY:
1445 InitAttrLine(kAttributeSendOnly, &os);
1446 break;
1447 case cricket::MD_RECVONLY:
1448 InitAttrLine(kAttributeRecvOnly, &os);
1449 break;
1450 case cricket::MD_SENDRECV:
1451 default:
1452 InitAttrLine(kAttributeSendRecv, &os);
1453 break;
1454 }
1455 AddLine(os.str(), message);
1456
deadbeef9d3584c2016-02-16 17:54:10 -08001457 // draft-ietf-mmusic-msid-11
1458 // a=msid:<stream id> <track id>
1459 if (unified_plan_sdp && !media_desc->streams().empty()) {
1460 if (media_desc->streams().size() > 1u) {
1461 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1462 << "one track in a media section. Omitting 'a=msid'.";
1463 } else {
1464 auto track = media_desc->streams().begin();
1465 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001466 InitAttrLine(kAttributeMsid, &os);
1467 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1468 AddLine(os.str(), message);
1469 }
1470 }
1471
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001472 // RFC 5761
1473 // a=rtcp-mux
1474 if (media_desc->rtcp_mux()) {
1475 InitAttrLine(kAttributeRtcpMux, &os);
1476 AddLine(os.str(), message);
1477 }
1478
deadbeef13871492015-12-09 12:37:51 -08001479 // RFC 5506
1480 // a=rtcp-rsize
1481 if (media_desc->rtcp_reduced_size()) {
1482 InitAttrLine(kAttributeRtcpReducedSize, &os);
1483 AddLine(os.str(), message);
1484 }
1485
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001486 // RFC 4568
1487 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1488 for (std::vector<CryptoParams>::const_iterator it =
1489 media_desc->cryptos().begin();
1490 it != media_desc->cryptos().end(); ++it) {
1491 InitAttrLine(kAttributeCrypto, &os);
1492 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1493 << it->key_params;
1494 if (!it->session_params.empty()) {
1495 os << " " << it->session_params;
1496 }
1497 AddLine(os.str(), message);
1498 }
1499
1500 // RFC 4566
1501 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1502 // [/<encodingparameters>]
1503 BuildRtpMap(media_desc, media_type, message);
1504
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001505 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1506 track != media_desc->streams().end(); ++track) {
1507 // Require that the track belongs to a media stream,
1508 // ie the sync_label is set. This extra check is necessary since the
1509 // MediaContentDescription always contains a streamparam with an ssrc even
1510 // if no track or media stream have been created.
1511 if (track->sync_label.empty()) continue;
1512
1513 // Build the ssrc-group lines.
1514 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1515 // RFC 5576
1516 // a=ssrc-group:<semantics> <ssrc-id> ...
1517 if (track->ssrc_groups[i].ssrcs.empty()) {
1518 continue;
1519 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001520 InitAttrLine(kAttributeSsrcGroup, &os);
1521 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001522 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001523 track->ssrc_groups[i].ssrcs.begin();
1524 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001525 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001526 }
1527 AddLine(os.str(), message);
1528 }
1529 // Build the ssrc lines for each ssrc.
1530 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001531 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001532 // RFC 5576
1533 // a=ssrc:<ssrc-id> cname:<value>
1534 AddSsrcLine(ssrc, kSsrcAttributeCname,
1535 track->cname, message);
1536
1537 // draft-alvestrand-mmusic-msid-00
1538 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001539 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1540 // which corresponds to the "id" attribute of StreamParams.
1541 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001542 InitAttrLine(kAttributeSsrc, &os);
1543 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001544 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1545 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001546 AddLine(os.str(), message);
1547
deadbeef9d3584c2016-02-16 17:54:10 -08001548 // TODO(ronghuawu): Remove below code which is for backward
1549 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001550 // draft-alvestrand-rtcweb-mid-01
1551 // a=ssrc:<ssrc-id> mslabel:<value>
1552 // The label isn't yet defined.
1553 // a=ssrc:<ssrc-id> label:<value>
1554 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1555 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1556 }
1557 }
1558}
1559
1560void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1561 // fmtp header: a=fmtp:|payload_type| <parameters>
1562 // Add a=fmtp
1563 InitAttrLine(kAttributeFmtp, os);
1564 // Add :|payload_type|
1565 *os << kSdpDelimiterColon << payload_type;
1566}
1567
1568void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1569 // rtcp-fb header: a=rtcp-fb:|payload_type|
1570 // <parameters>/<ccm <ccm_parameters>>
1571 // Add a=rtcp-fb
1572 InitAttrLine(kAttributeRtcpFb, os);
1573 // Add :
1574 *os << kSdpDelimiterColon;
1575 if (payload_type == kWildcardPayloadType) {
1576 *os << "*";
1577 } else {
1578 *os << payload_type;
1579 }
1580}
1581
1582void WriteFmtpParameter(const std::string& parameter_name,
1583 const std::string& parameter_value,
1584 std::ostringstream* os) {
1585 // fmtp parameters: |parameter_name|=|parameter_value|
1586 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1587}
1588
1589void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1590 std::ostringstream* os) {
1591 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1592 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 11:02:14 -07001593 // Parameters are a semicolon-separated list, no spaces.
1594 // The list is separated from the header by a space.
1595 if (fmtp == parameters.begin()) {
1596 *os << kSdpDelimiterSpace;
1597 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001598 *os << kSdpDelimiterSemicolon;
1599 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001600 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1601 }
1602}
1603
1604bool IsFmtpParam(const std::string& name) {
1605 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001606 // TODO(hta): Split FMTP parameters apart from parameters in general.
1607 // FMTP parameters are codec specific, not generic.
1608 kCodecParamMinPTime,
1609 kCodecParamSPropStereo,
1610 kCodecParamStereo,
1611 kCodecParamUseInbandFec,
1612 kCodecParamUseDtx,
1613 kCodecParamStartBitrate,
1614 kCodecParamMaxBitrate,
1615 kCodecParamMinBitrate,
1616 kCodecParamMaxQuantization,
1617 kCodecParamSctpProtocol,
1618 kCodecParamSctpStreams,
1619 kCodecParamMaxAverageBitrate,
1620 kCodecParamMaxPlaybackRate,
1621 kCodecParamAssociatedPayloadType,
1622 cricket::kH264FmtpPacketizationMode,
1623 cricket::kH264FmtpLevelAsymmetryAllowed,
brandtr87d7d772016-11-07 03:03:41 -08001624 cricket::kH264FmtpProfileLevelId,
1625 cricket::kFlexfecFmtpRepairWindow};
tfarina5237aaf2015-11-10 23:44:30 -08001626 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001627 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001628 return true;
1629 }
1630 }
1631 return false;
1632}
1633
1634// Retreives fmtp parameters from |params|, which may contain other parameters
1635// as well, and puts them in |fmtp_parameters|.
1636void GetFmtpParams(const cricket::CodecParameterMap& params,
1637 cricket::CodecParameterMap* fmtp_parameters) {
1638 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1639 iter != params.end(); ++iter) {
1640 if (IsFmtpParam(iter->first)) {
1641 (*fmtp_parameters)[iter->first] = iter->second;
1642 }
1643 }
1644}
1645
1646template <class T>
1647void AddFmtpLine(const T& codec, std::string* message) {
1648 cricket::CodecParameterMap fmtp_parameters;
1649 GetFmtpParams(codec.params, &fmtp_parameters);
1650 if (fmtp_parameters.empty()) {
1651 // No need to add an fmtp if it will have no (optional) parameters.
1652 return;
1653 }
1654 std::ostringstream os;
1655 WriteFmtpHeader(codec.id, &os);
1656 WriteFmtpParameters(fmtp_parameters, &os);
1657 AddLine(os.str(), message);
1658 return;
1659}
1660
1661template <class T>
1662void AddRtcpFbLines(const T& codec, std::string* message) {
1663 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1664 codec.feedback_params.params().begin();
1665 iter != codec.feedback_params.params().end(); ++iter) {
1666 std::ostringstream os;
1667 WriteRtcpFbHeader(codec.id, &os);
1668 os << " " << iter->id();
1669 if (!iter->param().empty()) {
1670 os << " " << iter->param();
1671 }
1672 AddLine(os.str(), message);
1673 }
1674}
1675
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001676bool AddSctpDataCodec(DataContentDescription* media_desc,
1677 int sctp_port) {
solenberg9fa49752016-10-08 13:02:44 -07001678 for (const auto& codec : media_desc->codecs()) {
1679 if (cricket::CodecNamesEq(codec.name, cricket::kGoogleSctpDataCodecName)) {
1680 return ParseFailed("",
1681 "Can't have multiple sctp port attributes.",
1682 NULL);
1683 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001684 }
1685 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 13:02:44 -07001686 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 10:07:16 -07001687 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001688 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1689 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1690 << sctp_port;
1691 media_desc->AddCodec(codec_port);
1692 return true;
1693}
1694
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001695bool GetMinValue(const std::vector<int>& values, int* value) {
1696 if (values.empty()) {
1697 return false;
1698 }
1699 std::vector<int>::const_iterator found =
1700 std::min_element(values.begin(), values.end());
1701 *value = *found;
1702 return true;
1703}
1704
1705bool GetParameter(const std::string& name,
1706 const cricket::CodecParameterMap& params, int* value) {
1707 std::map<std::string, std::string>::const_iterator found =
1708 params.find(name);
1709 if (found == params.end()) {
1710 return false;
1711 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001712 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001713 return false;
1714 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001715 return true;
1716}
1717
1718void BuildRtpMap(const MediaContentDescription* media_desc,
1719 const MediaType media_type,
1720 std::string* message) {
1721 ASSERT(message != NULL);
1722 ASSERT(media_desc != NULL);
1723 std::ostringstream os;
1724 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1725 const VideoContentDescription* video_desc =
1726 static_cast<const VideoContentDescription*>(media_desc);
1727 for (std::vector<cricket::VideoCodec>::const_iterator it =
1728 video_desc->codecs().begin();
1729 it != video_desc->codecs().end(); ++it) {
1730 // RFC 4566
1731 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1732 // [/<encodingparameters>]
1733 if (it->id != kWildcardPayloadType) {
1734 InitAttrLine(kAttributeRtpmap, &os);
1735 os << kSdpDelimiterColon << it->id << " " << it->name
1736 << "/" << kDefaultVideoClockrate;
1737 AddLine(os.str(), message);
1738 }
1739 AddRtcpFbLines(*it, message);
1740 AddFmtpLine(*it, message);
1741 }
1742 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1743 const AudioContentDescription* audio_desc =
1744 static_cast<const AudioContentDescription*>(media_desc);
1745 std::vector<int> ptimes;
1746 std::vector<int> maxptimes;
1747 int max_minptime = 0;
1748 for (std::vector<cricket::AudioCodec>::const_iterator it =
1749 audio_desc->codecs().begin();
1750 it != audio_desc->codecs().end(); ++it) {
1751 ASSERT(!it->name.empty());
1752 // RFC 4566
1753 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1754 // [/<encodingparameters>]
1755 InitAttrLine(kAttributeRtpmap, &os);
1756 os << kSdpDelimiterColon << it->id << " ";
1757 os << it->name << "/" << it->clockrate;
1758 if (it->channels != 1) {
1759 os << "/" << it->channels;
1760 }
1761 AddLine(os.str(), message);
1762 AddRtcpFbLines(*it, message);
1763 AddFmtpLine(*it, message);
1764 int minptime = 0;
1765 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1766 max_minptime = std::max(minptime, max_minptime);
1767 }
1768 int ptime;
1769 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1770 ptimes.push_back(ptime);
1771 }
1772 int maxptime;
1773 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1774 maxptimes.push_back(maxptime);
1775 }
1776 }
1777 // Populate the maxptime attribute with the smallest maxptime of all codecs
1778 // under the same m-line.
1779 int min_maxptime = INT_MAX;
1780 if (GetMinValue(maxptimes, &min_maxptime)) {
1781 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1782 }
1783 ASSERT(min_maxptime > max_minptime);
1784 // Populate the ptime attribute with the smallest ptime or the largest
1785 // minptime, whichever is the largest, for all codecs under the same m-line.
1786 int ptime = INT_MAX;
1787 if (GetMinValue(ptimes, &ptime)) {
1788 ptime = std::min(ptime, min_maxptime);
1789 ptime = std::max(ptime, max_minptime);
1790 AddAttributeLine(kCodecParamPTime, ptime, message);
1791 }
1792 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1793 const DataContentDescription* data_desc =
1794 static_cast<const DataContentDescription*>(media_desc);
1795 for (std::vector<cricket::DataCodec>::const_iterator it =
1796 data_desc->codecs().begin();
1797 it != data_desc->codecs().end(); ++it) {
1798 // RFC 4566
1799 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1800 // [/<encodingparameters>]
1801 InitAttrLine(kAttributeRtpmap, &os);
1802 os << kSdpDelimiterColon << it->id << " "
1803 << it->name << "/" << it->clockrate;
1804 AddLine(os.str(), message);
1805 }
1806 }
1807}
1808
1809void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001810 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001811 std::string* message) {
1812 std::ostringstream os;
1813
1814 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1815 it != candidates.end(); ++it) {
1816 // RFC 5245
1817 // a=candidate:<foundation> <component-id> <transport> <priority>
1818 // <connection-address> <port> typ <candidate-types>
1819 // [raddr <connection-address>] [rport <port>]
1820 // *(SP extension-att-name SP extension-att-value)
1821 std::string type;
1822 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1823 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1824 type = kCandidateHost;
1825 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1826 type = kCandidateSrflx;
1827 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1828 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001829 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1830 type = kCandidatePrflx;
1831 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001832 } else {
1833 ASSERT(false);
Peter Thatcher019087f2015-04-28 09:06:26 -07001834 // Never write out candidates if we don't know the type.
1835 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001836 }
1837
1838 InitAttrLine(kAttributeCandidate, &os);
1839 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001840 << it->foundation() << " "
1841 << it->component() << " "
1842 << it->protocol() << " "
1843 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001844 << it->address().ipaddr().ToString() << " "
1845 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001846 << kAttributeCandidateTyp << " "
1847 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001848
1849 // Related address
1850 if (!it->related_address().IsNil()) {
1851 os << kAttributeCandidateRaddr << " "
1852 << it->related_address().ipaddr().ToString() << " "
1853 << kAttributeCandidateRport << " "
1854 << it->related_address().PortAsString() << " ";
1855 }
1856
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001857 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001858 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001859 }
1860
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001861 // Extensions
1862 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001863 if (include_ufrag && !it->username().empty()) {
1864 os << " " << kAttributeCandidateUfrag << " " << it->username();
1865 }
honghaiza0c44ea2016-03-23 16:07:48 -07001866 if (it->network_id() > 0) {
1867 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1868 }
honghaize1a0c942016-02-16 14:54:56 -08001869 if (it->network_cost() > 0) {
1870 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1871 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001872
1873 AddLine(os.str(), message);
1874 }
1875}
1876
1877void BuildIceOptions(const std::vector<std::string>& transport_options,
1878 std::string* message) {
1879 if (!transport_options.empty()) {
1880 std::ostringstream os;
1881 InitAttrLine(kAttributeIceOption, &os);
1882 os << kSdpDelimiterColon << transport_options[0];
1883 for (size_t i = 1; i < transport_options.size(); ++i) {
1884 os << kSdpDelimiterSpace << transport_options[i];
1885 }
1886 AddLine(os.str(), message);
1887 }
1888}
1889
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001890bool IsRtp(const std::string& protocol) {
1891 return protocol.empty() ||
1892 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1893}
1894
1895bool IsDtlsSctp(const std::string& protocol) {
1896 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001897 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001898}
1899
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001900bool ParseSessionDescription(const std::string& message, size_t* pos,
1901 std::string* session_id,
1902 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001903 TransportDescription* session_td,
1904 RtpHeaderExtensions* session_extmaps,
1905 cricket::SessionDescription* desc,
1906 SdpParseError* error) {
1907 std::string line;
1908
deadbeefc80741f2015-10-22 13:14:45 -07001909 desc->set_msid_supported(false);
1910
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001911 // RFC 4566
1912 // v= (protocol version)
1913 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1914 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1915 std::string(), error);
1916 }
1917 // RFC 4566
1918 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1919 // <unicast-address>
1920 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1921 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1922 std::string(), error);
1923 }
1924 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001925 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001926 kSdpDelimiterSpace, &fields);
1927 const size_t expected_fields = 6;
1928 if (fields.size() != expected_fields) {
1929 return ParseFailedExpectFieldNum(line, expected_fields, error);
1930 }
1931 *session_id = fields[1];
1932 *session_version = fields[2];
1933
1934 // RFC 4566
1935 // s= (session name)
1936 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1937 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1938 std::string(), error);
1939 }
1940
1941 // Optional lines
1942 // Those are the optional lines, so shouldn't return false if not present.
1943 // RFC 4566
1944 // i=* (session information)
1945 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1946
1947 // RFC 4566
1948 // u=* (URI of description)
1949 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1950
1951 // RFC 4566
1952 // e=* (email address)
1953 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1954
1955 // RFC 4566
1956 // p=* (phone number)
1957 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1958
1959 // RFC 4566
1960 // c=* (connection information -- not required if included in
1961 // all media)
1962 GetLineWithType(message, pos, &line, kLineTypeConnection);
1963
1964 // RFC 4566
1965 // b=* (zero or more bandwidth information lines)
1966 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1967 // By pass zero or more b lines.
1968 }
1969
1970 // RFC 4566
1971 // One or more time descriptions ("t=" and "r=" lines; see below)
1972 // t= (time the session is active)
1973 // r=* (zero or more repeat times)
1974 // Ensure there's at least one time description
1975 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1976 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1977 error);
1978 }
1979
1980 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1981 // By pass zero or more r lines.
1982 }
1983
1984 // Go through the rest of the time descriptions
1985 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1986 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1987 // By pass zero or more r lines.
1988 }
1989 }
1990
1991 // RFC 4566
1992 // z=* (time zone adjustments)
1993 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1994
1995 // RFC 4566
1996 // k=* (encryption key)
1997 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1998
1999 // RFC 4566
2000 // a=* (zero or more session attribute lines)
2001 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
2002 if (HasAttribute(line, kAttributeGroup)) {
2003 if (!ParseGroupAttribute(line, desc, error)) {
2004 return false;
2005 }
2006 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2007 if (!GetValue(line, kAttributeIceUfrag,
2008 &(session_td->ice_ufrag), error)) {
2009 return false;
2010 }
2011 } else if (HasAttribute(line, kAttributeIcePwd)) {
2012 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2013 return false;
2014 }
2015 } else if (HasAttribute(line, kAttributeIceLite)) {
2016 session_td->ice_mode = cricket::ICEMODE_LITE;
2017 } else if (HasAttribute(line, kAttributeIceOption)) {
2018 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2019 return false;
2020 }
2021 } else if (HasAttribute(line, kAttributeFingerprint)) {
2022 if (session_td->identity_fingerprint.get()) {
2023 return ParseFailed(
2024 line,
2025 "Can't have multiple fingerprint attributes at the same level.",
2026 error);
2027 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002028 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002029 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2030 return false;
2031 }
2032 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002033 } else if (HasAttribute(line, kAttributeSetup)) {
2034 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2035 return false;
2036 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002037 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2038 std::string semantics;
2039 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2040 return false;
2041 }
deadbeefc80741f2015-10-22 13:14:45 -07002042 desc->set_msid_supported(
2043 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002044 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002045 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002046 if (!ParseExtmap(line, &extmap, error)) {
2047 return false;
2048 }
2049 session_extmaps->push_back(extmap);
2050 }
2051 }
2052
2053 return true;
2054}
2055
2056bool ParseGroupAttribute(const std::string& line,
2057 cricket::SessionDescription* desc,
2058 SdpParseError* error) {
2059 ASSERT(desc != NULL);
2060
2061 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2062 // a=group:BUNDLE video voice
2063 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002064 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002065 kSdpDelimiterSpace, &fields);
2066 std::string semantics;
2067 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2068 return false;
2069 }
2070 cricket::ContentGroup group(semantics);
2071 for (size_t i = 1; i < fields.size(); ++i) {
2072 group.AddContentName(fields[i]);
2073 }
2074 desc->AddGroup(group);
2075 return true;
2076}
2077
2078static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002079 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002080 SdpParseError* error) {
2081 if (!IsLineType(line, kLineTypeAttributes) ||
2082 !HasAttribute(line, kAttributeFingerprint)) {
2083 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2084 kAttributeFingerprint, error);
2085 }
2086
2087 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002088 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002089 kSdpDelimiterSpace, &fields);
2090 const size_t expected_fields = 2;
2091 if (fields.size() != expected_fields) {
2092 return ParseFailedExpectFieldNum(line, expected_fields, error);
2093 }
2094
2095 // The first field here is "fingerprint:<hash>.
2096 std::string algorithm;
2097 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2098 return false;
2099 }
2100
2101 // Downcase the algorithm. Note that we don't need to downcase the
2102 // fingerprint because hex_decode can handle upper-case.
2103 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2104 ::tolower);
2105
2106 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002107 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002108 algorithm, fields[1]);
2109 if (!*fingerprint) {
2110 return ParseFailed(line,
2111 "Failed to create fingerprint from the digest.",
2112 error);
2113 }
2114
2115 return true;
2116}
2117
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002118static bool ParseDtlsSetup(const std::string& line,
2119 cricket::ConnectionRole* role,
2120 SdpParseError* error) {
2121 // setup-attr = "a=setup:" role
2122 // role = "active" / "passive" / "actpass" / "holdconn"
2123 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002124 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002125 const size_t expected_fields = 2;
2126 if (fields.size() != expected_fields) {
2127 return ParseFailedExpectFieldNum(line, expected_fields, error);
2128 }
2129 std::string role_str = fields[1];
2130 if (!cricket::StringToConnectionRole(role_str, role)) {
2131 return ParseFailed(line, "Invalid attribute value.", error);
2132 }
2133 return true;
2134}
2135
deadbeef9d3584c2016-02-16 17:54:10 -08002136static bool ParseMsidAttribute(const std::string& line,
2137 std::string* stream_id,
2138 std::string* track_id,
2139 SdpParseError* error) {
2140 // draft-ietf-mmusic-msid-11
2141 // a=msid:<stream id> <track id>
2142 // msid-value = msid-id [ SP msid-appdata ]
2143 // msid-id = 1*64token-char ; see RFC 4566
2144 // msid-appdata = 1*64token-char ; see RFC 4566
2145 std::string field1;
2146 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2147 &field1, track_id)) {
2148 const size_t expected_fields = 2;
2149 return ParseFailedExpectFieldNum(line, expected_fields, error);
2150 }
2151
2152 // msid:<msid-id>
2153 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2154 return false;
2155 }
2156 return true;
2157}
2158
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002159// RFC 3551
2160// PT encoding media type clock rate channels
2161// name (Hz)
2162// 0 PCMU A 8,000 1
2163// 1 reserved A
2164// 2 reserved A
2165// 3 GSM A 8,000 1
2166// 4 G723 A 8,000 1
2167// 5 DVI4 A 8,000 1
2168// 6 DVI4 A 16,000 1
2169// 7 LPC A 8,000 1
2170// 8 PCMA A 8,000 1
2171// 9 G722 A 8,000 1
2172// 10 L16 A 44,100 2
2173// 11 L16 A 44,100 1
2174// 12 QCELP A 8,000 1
2175// 13 CN A 8,000 1
2176// 14 MPA A 90,000 (see text)
2177// 15 G728 A 8,000 1
2178// 16 DVI4 A 11,025 1
2179// 17 DVI4 A 22,050 1
2180// 18 G729 A 8,000 1
2181struct StaticPayloadAudioCodec {
2182 const char* name;
2183 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002184 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002185};
2186static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2187 { "PCMU", 8000, 1 },
2188 { "reserved", 0, 0 },
2189 { "reserved", 0, 0 },
2190 { "GSM", 8000, 1 },
2191 { "G723", 8000, 1 },
2192 { "DVI4", 8000, 1 },
2193 { "DVI4", 16000, 1 },
2194 { "LPC", 8000, 1 },
2195 { "PCMA", 8000, 1 },
2196 { "G722", 8000, 1 },
2197 { "L16", 44100, 2 },
2198 { "L16", 44100, 1 },
2199 { "QCELP", 8000, 1 },
2200 { "CN", 8000, 1 },
2201 { "MPA", 90000, 1 },
2202 { "G728", 8000, 1 },
2203 { "DVI4", 11025, 1 },
2204 { "DVI4", 22050, 1 },
2205 { "G729", 8000, 1 },
2206};
2207
2208void MaybeCreateStaticPayloadAudioCodecs(
2209 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2210 if (!media_desc) {
2211 return;
2212 }
deadbeef67cf2c12016-04-13 10:07:16 -07002213 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002214 for (int payload_type : fmts) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002215 if (!media_desc->HasCodec(payload_type) &&
2216 payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002217 static_cast<uint32_t>(payload_type) <
2218 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002219 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2220 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002221 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002222 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002223 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002224 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002225 }
2226}
2227
2228template <class C>
2229static C* ParseContentDescription(const std::string& message,
2230 const MediaType media_type,
2231 int mline_index,
2232 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002233 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002234 size_t* pos,
2235 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002236 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002237 TransportDescription* transport,
2238 std::vector<JsepIceCandidate*>* candidates,
2239 webrtc::SdpParseError* error) {
2240 C* media_desc = new C();
2241 switch (media_type) {
2242 case cricket::MEDIA_TYPE_AUDIO:
2243 *content_name = cricket::CN_AUDIO;
2244 break;
2245 case cricket::MEDIA_TYPE_VIDEO:
2246 *content_name = cricket::CN_VIDEO;
2247 break;
2248 case cricket::MEDIA_TYPE_DATA:
2249 *content_name = cricket::CN_DATA;
2250 break;
2251 default:
2252 ASSERT(false);
2253 break;
2254 }
deadbeef67cf2c12016-04-13 10:07:16 -07002255 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
deadbeef25ed4352016-12-12 18:37:36 -08002256 pos, content_name, bundle_only, media_desc, transport,
2257 candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002258 delete media_desc;
2259 return NULL;
2260 }
2261 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002262 std::unordered_map<int, int> payload_type_preferences;
2263 // "size + 1" so that the lowest preference payload type has a preference of
2264 // 1, which is greater than the default (0) for payload types not in the fmt
2265 // list.
2266 int preference = static_cast<int>(payload_types.size() + 1);
2267 for (int pt : payload_types) {
2268 payload_type_preferences[pt] = preference--;
2269 }
2270 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2271 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2272 const typename C::CodecType& a,
2273 const typename C::CodecType& b) {
2274 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2275 });
2276 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002277 return media_desc;
2278}
2279
2280bool ParseMediaDescription(const std::string& message,
2281 const TransportDescription& session_td,
2282 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002283 size_t* pos,
2284 cricket::SessionDescription* desc,
2285 std::vector<JsepIceCandidate*>* candidates,
2286 SdpParseError* error) {
2287 ASSERT(desc != NULL);
2288 std::string line;
2289 int mline_index = -1;
2290
2291 // Zero or more media descriptions
2292 // RFC 4566
2293 // m=<media> <port> <proto> <fmt>
2294 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2295 ++mline_index;
2296
2297 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002298 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002299 kSdpDelimiterSpace, &fields);
2300 const size_t expected_min_fields = 4;
2301 if (fields.size() < expected_min_fields) {
2302 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2303 }
deadbeef25ed4352016-12-12 18:37:36 -08002304 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002305 // RFC 3264
2306 // To reject an offered stream, the port number in the corresponding stream
2307 // in the answer MUST be set to zero.
2308 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-12 18:37:36 -08002309 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002310 }
2311
2312 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002313
2314 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002315 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002316 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002317 for (size_t j = 3 ; j < fields.size(); ++j) {
2318 // TODO(wu): Remove when below bug is fixed.
2319 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002320 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002321 continue;
2322 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002323
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002324 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002325 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002326 return false;
2327 }
deadbeef67cf2c12016-04-13 10:07:16 -07002328 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002329 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002330 }
2331
2332 // Make a temporary TransportDescription based on |session_td|.
2333 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002334 TransportDescription transport(
2335 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2336 session_td.ice_mode, session_td.connection_role,
2337 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002338
kwibergd1fe2812016-04-27 06:47:29 -07002339 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002340 std::string content_name;
deadbeef25ed4352016-12-12 18:37:36 -08002341 bool bundle_only = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002342 if (HasAttribute(line, kMediaTypeVideo)) {
2343 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002344 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002345 payload_types, pos, &content_name, &bundle_only, &transport,
2346 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002347 } else if (HasAttribute(line, kMediaTypeAudio)) {
2348 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002349 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002350 payload_types, pos, &content_name, &bundle_only, &transport,
2351 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002352 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002353 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002354 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002355 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002356 payload_types, pos, &content_name, &bundle_only, &transport,
2357 candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002358 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002359
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002360 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002361 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002362 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002363 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002364 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002365 } else {
2366 LOG(LS_WARNING) << "Unsupported media type: " << line;
2367 continue;
2368 }
2369 if (!content.get()) {
2370 // ParseContentDescription returns NULL if failed.
2371 return false;
2372 }
2373
deadbeef25ed4352016-12-12 18:37:36 -08002374 bool content_rejected = false;
deadbeef12771a12017-01-03 13:53:47 -08002375 // A port of 0 is not interpreted as a rejected m= section when it's
2376 // used along with a=bundle-only.
deadbeef25ed4352016-12-12 18:37:36 -08002377 if (bundle_only) {
deadbeef25ed4352016-12-12 18:37:36 -08002378 if (!port_rejected) {
deadbeef12771a12017-01-03 13:53:47 -08002379 // Usage of bundle-only with a nonzero port is unspecified. So just
2380 // ignore bundle-only if we see this.
2381 bundle_only = false;
2382 LOG(LS_WARNING)
2383 << "a=bundle-only attribute observed with a nonzero "
2384 << "port; this usage is unspecified so the attribute is being "
2385 << "ignored.";
deadbeef25ed4352016-12-12 18:37:36 -08002386 }
2387 } else {
2388 // If not using bundle-only, interpret port 0 in the normal way; the m=
2389 // section is being rejected.
2390 content_rejected = port_rejected;
2391 }
2392
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002393 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002394 // Set the extmap.
2395 if (!session_extmaps.empty() &&
2396 !content->rtp_header_extensions().empty()) {
2397 return ParseFailed("",
2398 "The a=extmap MUST be either all session level or "
2399 "all media level.",
2400 error);
2401 }
2402 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2403 content->AddRtpHeaderExtension(session_extmaps[i]);
2404 }
2405 }
2406 content->set_protocol(protocol);
2407 desc->AddContent(content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002408 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP
2409 : cricket::NS_JINGLE_RTP,
2410 content_rejected, bundle_only, content.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002411 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2412 TransportInfo transport_info(content_name, transport);
2413
2414 if (!desc->AddTransportInfo(transport_info)) {
2415 std::ostringstream description;
2416 description << "Failed to AddTransportInfo with content name: "
2417 << content_name;
2418 return ParseFailed("", description.str(), error);
2419 }
2420 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002421
2422 size_t end_of_message = message.size();
2423 if (mline_index == -1 && *pos != end_of_message) {
2424 ParseFailed(message, *pos, "Expects m line.", error);
2425 return false;
2426 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002427 return true;
2428}
2429
2430bool VerifyCodec(const cricket::Codec& codec) {
2431 // Codec has not been populated correctly unless the name has been set. This
2432 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2433 // have a corresponding "rtpmap" line.
htab39db842016-12-08 01:50:48 -08002434 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002435}
2436
2437bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2438 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2439 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2440 iter != codecs.end(); ++iter) {
2441 if (!VerifyCodec(*iter)) {
2442 return false;
2443 }
2444 }
2445 return true;
2446}
2447
2448bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2449 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2450 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2451 iter != codecs.end(); ++iter) {
2452 if (!VerifyCodec(*iter)) {
2453 return false;
2454 }
2455 }
2456 return true;
2457}
2458
2459void AddParameters(const cricket::CodecParameterMap& parameters,
2460 cricket::Codec* codec) {
2461 for (cricket::CodecParameterMap::const_iterator iter =
2462 parameters.begin(); iter != parameters.end(); ++iter) {
2463 codec->SetParam(iter->first, iter->second);
2464 }
2465}
2466
2467void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2468 cricket::Codec* codec) {
2469 codec->AddFeedbackParam(feedback_param);
2470}
2471
2472void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2473 cricket::Codec* codec) {
2474 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2475 feedback_params.params().begin();
2476 iter != feedback_params.params().end(); ++iter) {
2477 codec->AddFeedbackParam(*iter);
2478 }
2479}
2480
2481// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002482// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002483// with that payload type.
2484template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002485T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 04:00:16 -08002486 const T* codec = FindCodecById(codecs, payload_type);
2487 if (codec)
2488 return *codec;
2489 // Return empty codec with |payload_type|.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002490 T ret_val;
magjedb05fa242016-11-11 04:00:16 -08002491 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002492 return ret_val;
2493}
2494
2495// Updates or creates a new codec entry in the audio description.
2496template <class T, class U>
2497void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2498 T* desc = static_cast<T*>(content_desc);
2499 std::vector<U> codecs = desc->codecs();
2500 bool found = false;
2501
2502 typename std::vector<U>::iterator iter;
2503 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2504 if (iter->id == codec.id) {
2505 *iter = codec;
2506 found = true;
2507 break;
2508 }
2509 }
2510 if (!found) {
2511 desc->AddCodec(codec);
2512 return;
2513 }
2514 desc->set_codecs(codecs);
2515}
2516
2517// Adds or updates existing codec corresponding to |payload_type| according
2518// to |parameters|.
2519template <class T, class U>
2520void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2521 const cricket::CodecParameterMap& parameters) {
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 AddParameters(parameters, &new_codec);
2526 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2527}
2528
2529// Adds or updates existing codec corresponding to |payload_type| according
2530// to |feedback_param|.
2531template <class T, class U>
2532void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2533 const cricket::FeedbackParam& feedback_param) {
2534 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002535 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2536 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002537 AddFeedbackParameter(feedback_param, &new_codec);
2538 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2539}
2540
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002541template <class T>
2542bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2543 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002544 if (iter->id == kWildcardPayloadType) {
2545 *wildcard_codec = *iter;
2546 codecs->erase(iter);
2547 return true;
2548 }
2549 }
2550 return false;
2551}
2552
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002553template<class T>
2554void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2555 auto codecs = desc->codecs();
2556 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002557 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2558 return;
2559 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002560 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002561 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2562 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002563 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002564}
2565
2566void AddAudioAttribute(const std::string& name, const std::string& value,
2567 AudioContentDescription* audio_desc) {
2568 if (value.empty()) {
2569 return;
2570 }
2571 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2572 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2573 iter != codecs.end(); ++iter) {
2574 iter->params[name] = value;
2575 }
2576 audio_desc->set_codecs(codecs);
2577}
2578
2579bool ParseContent(const std::string& message,
2580 const MediaType media_type,
2581 int mline_index,
2582 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002583 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002584 size_t* pos,
2585 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002586 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002587 MediaContentDescription* media_desc,
2588 TransportDescription* transport,
2589 std::vector<JsepIceCandidate*>* candidates,
2590 SdpParseError* error) {
2591 ASSERT(media_desc != NULL);
2592 ASSERT(content_name != NULL);
2593 ASSERT(transport != NULL);
2594
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002595 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2596 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002597 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002598 }
2599
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002600 // The media level "ice-ufrag" and "ice-pwd".
2601 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2602 Candidates candidates_orig;
2603 std::string line;
2604 std::string mline_id;
2605 // Tracks created out of the ssrc attributes.
2606 StreamParamsVec tracks;
2607 SsrcInfoVec ssrc_infos;
2608 SsrcGroupVec ssrc_groups;
2609 std::string maxptime_as_string;
2610 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002611 std::string stream_id;
2612 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002613
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002614 // Loop until the next m line
2615 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2616 if (!GetLine(message, pos, &line)) {
2617 if (*pos >= message.size()) {
2618 break; // Done parsing
2619 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002620 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002621 }
2622 }
2623
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002624 // RFC 4566
2625 // b=* (zero or more bandwidth information lines)
2626 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2627 std::string bandwidth;
2628 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2629 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2630 return false;
2631 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002632 int b = 0;
2633 if (!GetValueFromString(line, bandwidth, &b, error)) {
2634 return false;
2635 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002636 // We should never use more than the default bandwidth for RTP-based
2637 // data channels. Don't allow SDP to set the bandwidth, because
2638 // that would give JS the opportunity to "break the Internet".
2639 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2640 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2641 b > cricket::kDataMaxBandwidth / 1000) {
2642 std::ostringstream description;
2643 description << "RTP-based data channels may not send more than "
2644 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2645 return ParseFailed(line, description.str(), error);
2646 }
deadbeefb2362572016-12-13 16:37:06 -08002647 // Prevent integer overflow.
2648 b = std::min(b, INT_MAX / 1000);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002649 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002650 }
2651 }
2652 continue;
2653 }
2654
2655 if (!IsLineType(line, kLineTypeAttributes)) {
2656 // TODO: Handle other lines if needed.
2657 LOG(LS_INFO) << "Ignored line: " << line;
2658 continue;
2659 }
2660
2661 // Handle attributes common to SCTP and RTP.
2662 if (HasAttribute(line, kAttributeMid)) {
2663 // RFC 3388
2664 // mid-attribute = "a=mid:" identification-tag
2665 // identification-tag = token
2666 // Use the mid identification-tag as the content name.
2667 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2668 return false;
2669 }
2670 *content_name = mline_id;
deadbeef25ed4352016-12-12 18:37:36 -08002671 } else if (HasAttribute(line, kAttributeBundleOnly)) {
2672 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002673 } else if (HasAttribute(line, kAttributeCandidate)) {
2674 Candidate candidate;
2675 if (!ParseCandidate(line, &candidate, error, false)) {
2676 return false;
2677 }
2678 candidates_orig.push_back(candidate);
2679 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2680 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2681 return false;
2682 }
2683 } else if (HasAttribute(line, kAttributeIcePwd)) {
2684 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2685 return false;
2686 }
2687 } else if (HasAttribute(line, kAttributeIceOption)) {
2688 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2689 return false;
2690 }
2691 } else if (HasAttribute(line, kAttributeFmtp)) {
2692 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2693 return false;
2694 }
2695 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002696 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002697
2698 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2699 return false;
2700 }
2701 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002702 } else if (HasAttribute(line, kAttributeSetup)) {
2703 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2704 return false;
2705 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002706 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 10:04:34 -07002707 if (media_type != cricket::MEDIA_TYPE_DATA) {
2708 return ParseFailed(
2709 line, "sctp-port attribute found in non-data media description.",
2710 error);
2711 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002712 int sctp_port;
2713 if (!ParseSctpPort(line, &sctp_port, error)) {
2714 return false;
2715 }
2716 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2717 sctp_port)) {
2718 return false;
2719 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002720 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002721 //
2722 // RTP specific attrubtes
2723 //
2724 if (HasAttribute(line, kAttributeRtcpMux)) {
2725 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002726 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2727 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002728 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2729 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2730 return false;
2731 }
2732 } else if (HasAttribute(line, kAttributeSsrc)) {
2733 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2734 return false;
2735 }
2736 } else if (HasAttribute(line, kAttributeCrypto)) {
2737 if (!ParseCryptoAttribute(line, media_desc, error)) {
2738 return false;
2739 }
2740 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002741 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2742 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002743 return false;
2744 }
2745 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2746 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2747 return false;
2748 }
2749 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2750 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2751 return false;
2752 }
2753 } else if (HasAttribute(line, kCodecParamPTime)) {
2754 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2755 return false;
2756 }
2757 } else if (HasAttribute(line, kAttributeSendOnly)) {
2758 media_desc->set_direction(cricket::MD_SENDONLY);
2759 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2760 media_desc->set_direction(cricket::MD_RECVONLY);
2761 } else if (HasAttribute(line, kAttributeInactive)) {
2762 media_desc->set_direction(cricket::MD_INACTIVE);
2763 } else if (HasAttribute(line, kAttributeSendRecv)) {
2764 media_desc->set_direction(cricket::MD_SENDRECV);
2765 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002766 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002767 if (!ParseExtmap(line, &extmap, error)) {
2768 return false;
2769 }
2770 media_desc->AddRtpHeaderExtension(extmap);
2771 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2772 // Experimental attribute. Conference mode activates more aggressive
2773 // AEC and NS settings.
2774 // TODO: expose API to set these directly.
2775 std::string flag_value;
2776 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2777 return false;
2778 }
2779 if (flag_value.compare(kValueConference) == 0)
2780 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002781 } else if (HasAttribute(line, kAttributeMsid)) {
2782 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2783 return false;
2784 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002785 }
2786 } else {
2787 // Only parse lines that we are interested of.
2788 LOG(LS_INFO) << "Ignored line: " << line;
2789 continue;
2790 }
2791 }
2792
2793 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002794 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2795 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2796 // the m= section.
2797 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002798
2799 // Add the ssrc group to the track.
2800 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2801 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2802 if (ssrc_group->ssrcs.empty()) {
2803 continue;
2804 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002805 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002806 for (StreamParamsVec::iterator track = tracks.begin();
2807 track != tracks.end(); ++track) {
2808 if (track->has_ssrc(ssrc)) {
2809 track->ssrc_groups.push_back(*ssrc_group);
2810 }
2811 }
2812 }
2813
2814 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002815 for (StreamParams& track : tracks) {
2816 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002817 }
2818
2819 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2820 AudioContentDescription* audio_desc =
2821 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002822 UpdateFromWildcardCodecs(audio_desc);
2823
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002824 // Verify audio codec ensures that no audio codec has been populated with
2825 // only fmtp.
2826 if (!VerifyAudioCodecs(audio_desc)) {
2827 return ParseFailed("Failed to parse audio codecs correctly.", error);
2828 }
2829 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2830 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2831 }
2832
2833 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002834 VideoContentDescription* video_desc =
2835 static_cast<VideoContentDescription*>(media_desc);
2836 UpdateFromWildcardCodecs(video_desc);
2837 // Verify video codec ensures that no video codec has been populated with
2838 // only rtcp-fb.
2839 if (!VerifyVideoCodecs(video_desc)) {
2840 return ParseFailed("Failed to parse video codecs correctly.", error);
2841 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002842 }
2843
2844 // RFC 5245
2845 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2846 for (Candidates::iterator it = candidates_orig.begin();
2847 it != candidates_orig.end(); ++it) {
honghaiza54a0802015-12-16 18:37:23 -08002848 ASSERT((*it).username().empty() ||
2849 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002850 (*it).set_username(transport->ice_ufrag);
2851 ASSERT((*it).password().empty());
2852 (*it).set_password(transport->ice_pwd);
2853 candidates->push_back(
2854 new JsepIceCandidate(mline_id, mline_index, *it));
2855 }
2856 return true;
2857}
2858
2859bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2860 SdpParseError* error) {
2861 ASSERT(ssrc_infos != NULL);
2862 // RFC 5576
2863 // a=ssrc:<ssrc-id> <attribute>
2864 // a=ssrc:<ssrc-id> <attribute>:<value>
2865 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002866 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2867 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002868 const size_t expected_fields = 2;
2869 return ParseFailedExpectFieldNum(line, expected_fields, error);
2870 }
2871
2872 // ssrc:<ssrc-id>
2873 std::string ssrc_id_s;
2874 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2875 return false;
2876 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002877 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002878 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2879 return false;
2880 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002881
2882 std::string attribute;
2883 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002884 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002885 std::ostringstream description;
2886 description << "Failed to get the ssrc attribute value from " << field2
2887 << ". Expected format <attribute>:<value>.";
2888 return ParseFailed(line, description.str(), error);
2889 }
2890
2891 // Check if there's already an item for this |ssrc_id|. Create a new one if
2892 // there isn't.
2893 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2894 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2895 if (ssrc_info->ssrc_id == ssrc_id) {
2896 break;
2897 }
2898 }
2899 if (ssrc_info == ssrc_infos->end()) {
2900 SsrcInfo info;
2901 info.ssrc_id = ssrc_id;
2902 ssrc_infos->push_back(info);
2903 ssrc_info = ssrc_infos->end() - 1;
2904 }
2905
2906 // Store the info to the |ssrc_info|.
2907 if (attribute == kSsrcAttributeCname) {
2908 // RFC 5576
2909 // cname:<value>
2910 ssrc_info->cname = value;
2911 } else if (attribute == kSsrcAttributeMsid) {
2912 // draft-alvestrand-mmusic-msid-00
2913 // "msid:" identifier [ " " appdata ]
2914 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002915 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002916 if (fields.size() < 1 || fields.size() > 2) {
2917 return ParseFailed(line,
2918 "Expected format \"msid:<identifier>[ <appdata>]\".",
2919 error);
2920 }
deadbeef9d3584c2016-02-16 17:54:10 -08002921 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002922 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002923 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002924 }
2925 } else if (attribute == kSsrcAttributeMslabel) {
2926 // draft-alvestrand-rtcweb-mid-01
2927 // mslabel:<value>
2928 ssrc_info->mslabel = value;
2929 } else if (attribute == kSSrcAttributeLabel) {
2930 // The label isn't defined.
2931 // label:<value>
2932 ssrc_info->label = value;
2933 }
2934 return true;
2935}
2936
2937bool ParseSsrcGroupAttribute(const std::string& line,
2938 SsrcGroupVec* ssrc_groups,
2939 SdpParseError* error) {
2940 ASSERT(ssrc_groups != NULL);
2941 // RFC 5576
2942 // a=ssrc-group:<semantics> <ssrc-id> ...
2943 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002944 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002945 kSdpDelimiterSpace, &fields);
2946 const size_t expected_min_fields = 2;
2947 if (fields.size() < expected_min_fields) {
2948 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2949 }
2950 std::string semantics;
2951 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2952 return false;
2953 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002954 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002955 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002956 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002957 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2958 return false;
2959 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002960 ssrcs.push_back(ssrc);
2961 }
2962 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2963 return true;
2964}
2965
2966bool ParseCryptoAttribute(const std::string& line,
2967 MediaContentDescription* media_desc,
2968 SdpParseError* error) {
2969 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002970 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002971 kSdpDelimiterSpace, &fields);
2972 // RFC 4568
2973 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2974 const size_t expected_min_fields = 3;
2975 if (fields.size() < expected_min_fields) {
2976 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2977 }
2978 std::string tag_value;
2979 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2980 return false;
2981 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002982 int tag = 0;
2983 if (!GetValueFromString(line, tag_value, &tag, error)) {
2984 return false;
2985 }
jbauch083b73f2015-07-16 02:46:32 -07002986 const std::string& crypto_suite = fields[1];
2987 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002988 std::string session_params;
2989 if (fields.size() > 3) {
2990 session_params = fields[3];
2991 }
2992 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2993 session_params));
2994 return true;
2995}
2996
2997// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07002998// to |name|, |clockrate|, |bitrate|, and |channels|.
2999void UpdateCodec(int payload_type,
3000 const std::string& name,
3001 int clockrate,
3002 int bitrate,
3003 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003004 AudioContentDescription* audio_desc) {
3005 // Codec may already be populated with (only) optional parameters
3006 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003007 cricket::AudioCodec codec =
3008 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003009 codec.name = name;
3010 codec.clockrate = clockrate;
3011 codec.bitrate = bitrate;
3012 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003013 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3014 codec);
3015}
3016
3017// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07003018// |name|, |width|, |height|, and |framerate|.
3019void UpdateCodec(int payload_type,
3020 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003021 VideoContentDescription* video_desc) {
3022 // Codec may already be populated with (only) optional parameters
3023 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003024 cricket::VideoCodec codec =
3025 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003026 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003027 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3028 codec);
3029}
3030
3031bool ParseRtpmapAttribute(const std::string& line,
3032 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07003033 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003034 MediaContentDescription* media_desc,
3035 SdpParseError* error) {
3036 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003037 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003038 kSdpDelimiterSpace, &fields);
3039 // RFC 4566
3040 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3041 const size_t expected_min_fields = 2;
3042 if (fields.size() < expected_min_fields) {
3043 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3044 }
3045 std::string payload_type_value;
3046 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3047 return false;
3048 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003049 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003050 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3051 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003052 return false;
3053 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003054
deadbeef67cf2c12016-04-13 10:07:16 -07003055 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3056 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003057 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3058 << "<fmt> of the m-line: " << line;
3059 return true;
3060 }
jbauch083b73f2015-07-16 02:46:32 -07003061 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003062 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003063 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003064 // <encoding name>/<clock rate>[/<encodingparameters>]
3065 // 2 mandatory fields
3066 if (codec_params.size() < 2 || codec_params.size() > 3) {
3067 return ParseFailed(line,
3068 "Expected format \"<encoding name>/<clock rate>"
3069 "[/<encodingparameters>]\".",
3070 error);
3071 }
jbauch083b73f2015-07-16 02:46:32 -07003072 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003073 int clock_rate = 0;
3074 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3075 return false;
3076 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003077 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3078 VideoContentDescription* video_desc =
3079 static_cast<VideoContentDescription*>(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003080 UpdateCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07003081 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003082 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3083 // RFC 4566
3084 // For audio streams, <encoding parameters> indicates the number
3085 // of audio channels. This parameter is OPTIONAL and may be
3086 // omitted if the number of channels is one, provided that no
3087 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003088 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003089 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003090 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3091 return false;
3092 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003093 }
3094 int bitrate = 0;
3095 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3096 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3097 // The bandwidth adaptation doesn't always work well, so this code
3098 // sets a fixed target bitrate instead.
3099 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3100 if (clock_rate <= 16000) {
3101 bitrate = kIsacWbDefaultRate;
3102 } else {
3103 bitrate = kIsacSwbDefaultRate;
3104 }
3105 }
3106 AudioContentDescription* audio_desc =
3107 static_cast<AudioContentDescription*>(media_desc);
3108 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003109 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003110 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3111 DataContentDescription* data_desc =
3112 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003113 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003114 }
3115 return true;
3116}
3117
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003118bool ParseFmtpParam(const std::string& line, std::string* parameter,
3119 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003120 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003121 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3122 return false;
3123 }
3124 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003125 return true;
3126}
3127
3128bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3129 MediaContentDescription* media_desc,
3130 SdpParseError* error) {
3131 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3132 media_type != cricket::MEDIA_TYPE_VIDEO) {
3133 return true;
3134 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003135
3136 std::string line_payload;
3137 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003138
3139 // RFC 5576
3140 // a=fmtp:<format> <format specific parameters>
3141 // At least two fields, whereas the second one is any of the optional
3142 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003143 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3144 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003145 ParseFailedExpectMinFieldNum(line, 2, error);
3146 return false;
3147 }
3148
Donald Curtis0e07f922015-05-15 09:21:23 -07003149 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003150 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003151 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003152 return false;
3153 }
3154
Donald Curtis0e07f922015-05-15 09:21:23 -07003155 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003156 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3157 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003158 return false;
3159 }
3160
3161 // Parse out format specific parameters.
3162 std::vector<std::string> fields;
3163 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3164
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003165 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003166 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003167 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003168 // Only fmtps with equals are currently supported. Other fmtp types
3169 // should be ignored. Unknown fmtps do not constitute an error.
3170 continue;
3171 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003172
3173 std::string name;
3174 std::string value;
3175 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003176 return false;
3177 }
3178 codec_params[name] = value;
3179 }
3180
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003181 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3182 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003183 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003184 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3185 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003186 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003187 }
3188 return true;
3189}
3190
3191bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3192 MediaContentDescription* media_desc,
3193 SdpParseError* error) {
3194 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3195 media_type != cricket::MEDIA_TYPE_VIDEO) {
3196 return true;
3197 }
3198 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003199 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003200 if (rtcp_fb_fields.size() < 2) {
3201 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3202 }
3203 std::string payload_type_string;
3204 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3205 error)) {
3206 return false;
3207 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003208 int payload_type = kWildcardPayloadType;
3209 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003210 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3211 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003212 return false;
3213 }
3214 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003215 std::string id = rtcp_fb_fields[1];
3216 std::string param = "";
3217 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3218 iter != rtcp_fb_fields.end(); ++iter) {
3219 param.append(*iter);
3220 }
3221 const cricket::FeedbackParam feedback_param(id, param);
3222
3223 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003224 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3225 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003226 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003227 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3228 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003229 }
3230 return true;
3231}
3232
3233} // namespace webrtc