blob: c79c851c3a1465e2ad2d4d46047e46535cba1bc1 [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"
nissec80e7412017-01-11 05:56:46 -080026#include "webrtc/base/checks.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000027#include "webrtc/base/common.h"
28#include "webrtc/base/logging.h"
29#include "webrtc/base/messagedigest.h"
30#include "webrtc/base/stringutils.h"
isheriff6f8d6862016-05-26 11:24:55 -070031// for RtpExtension
32#include "webrtc/config.h"
kjellandera96e2d72016-02-04 23:52:28 -080033#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-04 23:52:28 -080034#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080035#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080036#include "webrtc/media/base/rtputils.h"
deadbeef953c2ce2017-01-09 14:53:41 -080037#include "webrtc/media/sctp/sctptransportinternal.h"
kjellandera96e2d72016-02-04 23:52:28 -080038#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080039#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080040#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010041#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042
43using cricket::AudioContentDescription;
44using cricket::Candidate;
45using cricket::Candidates;
46using cricket::ContentDescription;
47using cricket::ContentInfo;
48using cricket::CryptoParams;
49using cricket::DataContentDescription;
50using cricket::ICE_CANDIDATE_COMPONENT_RTP;
51using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
52using cricket::kCodecParamMaxBitrate;
53using cricket::kCodecParamMaxPTime;
54using cricket::kCodecParamMaxQuantization;
55using cricket::kCodecParamMinBitrate;
56using cricket::kCodecParamMinPTime;
57using cricket::kCodecParamPTime;
58using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000059using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000060using cricket::kCodecParamStereo;
61using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010062using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000063using cricket::kCodecParamSctpProtocol;
64using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000065using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000066using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000067using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000068using cricket::MediaContentDescription;
69using cricket::MediaType;
isheriff6f8d6862016-05-26 11:24:55 -070070using cricket::RtpHeaderExtensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000071using cricket::SsrcGroup;
72using cricket::StreamParams;
73using cricket::StreamParamsVec;
74using cricket::TransportDescription;
75using cricket::TransportInfo;
76using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000077using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078
henrike@webrtc.org28e20752013-07-10 00:45:36 +000079namespace cricket {
80class SessionDescription;
81}
82
83namespace webrtc {
84
85// Line type
86// RFC 4566
87// An SDP session description consists of a number of lines of text of
88// the form:
89// <type>=<value>
90// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -080091static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092static const char kLineTypeVersion = 'v';
93static const char kLineTypeOrigin = 'o';
94static const char kLineTypeSessionName = 's';
95static const char kLineTypeSessionInfo = 'i';
96static const char kLineTypeSessionUri = 'u';
97static const char kLineTypeSessionEmail = 'e';
98static const char kLineTypeSessionPhone = 'p';
99static const char kLineTypeSessionBandwidth = 'b';
100static const char kLineTypeTiming = 't';
101static const char kLineTypeRepeatTimes = 'r';
102static const char kLineTypeTimeZone = 'z';
103static const char kLineTypeEncryptionKey = 'k';
104static const char kLineTypeMedia = 'm';
105static const char kLineTypeConnection = 'c';
106static const char kLineTypeAttributes = 'a';
107
108// Attributes
109static const char kAttributeGroup[] = "group";
110static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800111static const char kAttributeMsid[] = "msid";
deadbeef25ed4352016-12-12 18:37:36 -0800112static const char kAttributeBundleOnly[] = "bundle-only";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800114static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115static const char kAttributeSsrc[] = "ssrc";
116static const char kSsrcAttributeCname[] = "cname";
117static const char kAttributeExtmap[] = "extmap";
118// draft-alvestrand-mmusic-msid-01
119// a=msid-semantic: WMS
120static const char kAttributeMsidSemantics[] = "msid-semantic";
121static const char kMediaStreamSemantic[] = "WMS";
122static const char kSsrcAttributeMsid[] = "msid";
123static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124static const char kSsrcAttributeMslabel[] = "mslabel";
125static const char kSSrcAttributeLabel[] = "label";
126static const char kAttributeSsrcGroup[] = "ssrc-group";
127static const char kAttributeCrypto[] = "crypto";
128static const char kAttributeCandidate[] = "candidate";
129static const char kAttributeCandidateTyp[] = "typ";
130static const char kAttributeCandidateRaddr[] = "raddr";
131static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800132static const char kAttributeCandidateUfrag[] = "ufrag";
133static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000134static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700135static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800136static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000137static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000138static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000139static const char kAttributeFmtp[] = "fmtp";
140static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000141static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000142static const char kAttributeRtcp[] = "rtcp";
143static const char kAttributeIceUfrag[] = "ice-ufrag";
144static const char kAttributeIcePwd[] = "ice-pwd";
145static const char kAttributeIceLite[] = "ice-lite";
146static const char kAttributeIceOption[] = "ice-options";
147static const char kAttributeSendOnly[] = "sendonly";
148static const char kAttributeRecvOnly[] = "recvonly";
149static const char kAttributeRtcpFb[] = "rtcp-fb";
150static const char kAttributeSendRecv[] = "sendrecv";
151static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000152// draft-ietf-mmusic-sctp-sdp-07
153// a=sctp-port
154static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000155
156// Experimental flags
157static const char kAttributeXGoogleFlag[] = "x-google-flag";
158static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000159
160// Candidate
161static const char kCandidateHost[] = "host";
162static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700163static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000165static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000166
167static const char kSdpDelimiterEqual = '=';
168static const char kSdpDelimiterSpace = ' ';
169static const char kSdpDelimiterColon = ':';
170static const char kSdpDelimiterSemicolon = ';';
171static const char kSdpDelimiterSlash = '/';
172static const char kNewLine = '\n';
173static const char kReturn = '\r';
174static const char kLineBreak[] = "\r\n";
175
176// TODO: Generate the Session and Time description
177// instead of hardcoding.
178static const char kSessionVersion[] = "v=0";
179// RFC 4566
180static const char kSessionOriginUsername[] = "-";
181static const char kSessionOriginSessionId[] = "0";
182static const char kSessionOriginSessionVersion[] = "0";
183static const char kSessionOriginNettype[] = "IN";
184static const char kSessionOriginAddrtype[] = "IP4";
185static const char kSessionOriginAddress[] = "127.0.0.1";
186static const char kSessionName[] = "s=-";
187static const char kTimeDescription[] = "t=0 0";
188static const char kAttrGroup[] = "a=group:BUNDLE";
189static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000190static const char kConnectionIpv4Addrtype[] = "IP4";
191static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000192static const char kMediaTypeVideo[] = "video";
193static const char kMediaTypeAudio[] = "audio";
194static const char kMediaTypeData[] = "application";
195static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000196// draft-ietf-mmusic-trickle-ice-01
197// When no candidates have been gathered, set the connection
198// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000199// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
200// Use IPV4 per default.
201static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000202static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000203// RFC 3556
204static const char kApplicationSpecificMaximum[] = "AS";
205
206static const int kDefaultVideoClockrate = 90000;
207
208// ISAC special-case.
209static const char kIsacCodecName[] = "ISAC"; // From webrtcvoiceengine.cc
210static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h
211static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h
212
wu@webrtc.org78187522013-10-07 23:32:02 +0000213static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000214
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000215// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
216// types.
217const int kWildcardPayloadType = -1;
218
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000219struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200220 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000221 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800222 std::string stream_id;
223 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000224
225 // For backward compatibility.
226 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
227 std::string label;
228 std::string mslabel;
229};
230typedef std::vector<SsrcInfo> SsrcInfoVec;
231typedef std::vector<SsrcGroup> SsrcGroupVec;
232
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000233template <class T>
234static void AddFmtpLine(const T& codec, std::string* message);
235static void BuildMediaDescription(const ContentInfo* content_info,
236 const TransportInfo* transport_info,
237 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000238 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800239 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000240 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000241static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800242static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
243 const MediaType media_type,
244 bool unified_plan_sdp,
245 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000246static void BuildRtpMap(const MediaContentDescription* media_desc,
247 const MediaType media_type,
248 std::string* message);
249static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800250 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251 std::string* message);
252static void BuildIceOptions(const std::vector<std::string>& transport_options,
253 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000254static bool IsRtp(const std::string& protocol);
255static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000256static bool ParseSessionDescription(const std::string& message, size_t* pos,
257 std::string* session_id,
258 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259 TransportDescription* session_td,
260 RtpHeaderExtensions* session_extmaps,
261 cricket::SessionDescription* desc,
262 SdpParseError* error);
263static bool ParseGroupAttribute(const std::string& line,
264 cricket::SessionDescription* desc,
265 SdpParseError* error);
266static bool ParseMediaDescription(
267 const std::string& message,
268 const TransportDescription& session_td,
269 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000270 size_t* pos, cricket::SessionDescription* desc,
271 std::vector<JsepIceCandidate*>* candidates,
272 SdpParseError* error);
273static bool ParseContent(const std::string& message,
274 const MediaType media_type,
275 int mline_index,
276 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700277 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278 size_t* pos,
279 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -0800280 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000281 MediaContentDescription* media_desc,
282 TransportDescription* transport,
283 std::vector<JsepIceCandidate*>* candidates,
284 SdpParseError* error);
285static bool ParseSsrcAttribute(const std::string& line,
286 SsrcInfoVec* ssrc_infos,
287 SdpParseError* error);
288static bool ParseSsrcGroupAttribute(const std::string& line,
289 SsrcGroupVec* ssrc_groups,
290 SdpParseError* error);
291static bool ParseCryptoAttribute(const std::string& line,
292 MediaContentDescription* media_desc,
293 SdpParseError* error);
294static bool ParseRtpmapAttribute(const std::string& line,
295 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700296 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000297 MediaContentDescription* media_desc,
298 SdpParseError* error);
299static bool ParseFmtpAttributes(const std::string& line,
300 const MediaType media_type,
301 MediaContentDescription* media_desc,
302 SdpParseError* error);
303static bool ParseFmtpParam(const std::string& line, std::string* parameter,
304 std::string* value, SdpParseError* error);
305static bool ParseCandidate(const std::string& message, Candidate* candidate,
306 SdpParseError* error, bool is_raw);
307static bool ParseRtcpFbAttribute(const std::string& line,
308 const MediaType media_type,
309 MediaContentDescription* media_desc,
310 SdpParseError* error);
311static bool ParseIceOptions(const std::string& line,
312 std::vector<std::string>* transport_options,
313 SdpParseError* error);
314static bool ParseExtmap(const std::string& line,
isheriff6f8d6862016-05-26 11:24:55 -0700315 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000316 SdpParseError* error);
317static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000318 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000319 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000320static bool ParseDtlsSetup(const std::string& line,
321 cricket::ConnectionRole* role,
322 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800323static bool ParseMsidAttribute(const std::string& line,
324 std::string* stream_id,
325 std::string* track_id,
326 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000327
328// Helper functions
329
330// Below ParseFailed*** functions output the line that caused the parsing
331// failure and the detailed reason (|description|) of the failure to |error|.
332// The functions always return false so that they can be used directly in the
333// following way when error happens:
334// "return ParseFailed***(...);"
335
336// The line starting at |line_start| of |message| is the failing line.
337// The reason for the failure should be provided in the |description|.
338// An example of a description could be "unknown character".
339static bool ParseFailed(const std::string& message,
340 size_t line_start,
341 const std::string& description,
342 SdpParseError* error) {
343 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000344 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000345 size_t line_end = message.find(kNewLine, line_start);
346 if (line_end != std::string::npos) {
347 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
348 --line_end;
349 }
350 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000351 } else {
352 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000353 }
354
355 if (error) {
356 error->line = first_line;
357 error->description = description;
358 }
359 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
360 << "\". Reason: " << description;
361 return false;
362}
363
364// |line| is the failing line. The reason for the failure should be
365// provided in the |description|.
366static bool ParseFailed(const std::string& line,
367 const std::string& description,
368 SdpParseError* error) {
369 return ParseFailed(line, 0, description, error);
370}
371
372// Parses failure where the failing SDP line isn't know or there are multiple
373// failing lines.
374static bool ParseFailed(const std::string& description,
375 SdpParseError* error) {
376 return ParseFailed("", description, error);
377}
378
379// |line| is the failing line. The failure is due to the fact that |line|
380// doesn't have |expected_fields| fields.
381static bool ParseFailedExpectFieldNum(const std::string& line,
382 int expected_fields,
383 SdpParseError* error) {
384 std::ostringstream description;
385 description << "Expects " << expected_fields << " fields.";
386 return ParseFailed(line, description.str(), error);
387}
388
389// |line| is the failing line. The failure is due to the fact that |line| has
390// less than |expected_min_fields| fields.
391static bool ParseFailedExpectMinFieldNum(const std::string& line,
392 int expected_min_fields,
393 SdpParseError* error) {
394 std::ostringstream description;
395 description << "Expects at least " << expected_min_fields << " fields.";
396 return ParseFailed(line, description.str(), error);
397}
398
399// |line| is the failing line. The failure is due to the fact that it failed to
400// get the value of |attribute|.
401static bool ParseFailedGetValue(const std::string& line,
402 const std::string& attribute,
403 SdpParseError* error) {
404 std::ostringstream description;
405 description << "Failed to get the value of attribute: " << attribute;
406 return ParseFailed(line, description.str(), error);
407}
408
409// The line starting at |line_start| of |message| is the failing line. The
410// failure is due to the line type (e.g. the "m" part of the "m-line")
411// not matching what is expected. The expected line type should be
412// provided as |line_type|.
413static bool ParseFailedExpectLine(const std::string& message,
414 size_t line_start,
415 const char line_type,
416 const std::string& line_value,
417 SdpParseError* error) {
418 std::ostringstream description;
419 description << "Expect line: " << line_type << "=" << line_value;
420 return ParseFailed(message, line_start, description.str(), error);
421}
422
423static bool AddLine(const std::string& line, std::string* message) {
424 if (!message)
425 return false;
426
427 message->append(line);
428 message->append(kLineBreak);
429 return true;
430}
431
432static bool GetLine(const std::string& message,
433 size_t* pos,
434 std::string* line) {
435 size_t line_begin = *pos;
436 size_t line_end = message.find(kNewLine, line_begin);
437 if (line_end == std::string::npos) {
438 return false;
439 }
440 // Update the new start position
441 *pos = line_end + 1;
442 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
443 --line_end;
444 }
445 *line = message.substr(line_begin, (line_end - line_begin));
446 const char* cline = line->c_str();
447 // RFC 4566
448 // An SDP session description consists of a number of lines of text of
449 // the form:
450 // <type>=<value>
451 // where <type> MUST be exactly one case-significant character and
452 // <value> is structured text whose format depends on <type>.
453 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000454 if (line->length() < 3 ||
455 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000456 cline[1] != kSdpDelimiterEqual ||
457 cline[2] == kSdpDelimiterSpace) {
458 *pos = line_begin;
459 return false;
460 }
461 return true;
462}
463
464// Init |os| to "|type|=|value|".
465static void InitLine(const char type,
466 const std::string& value,
467 std::ostringstream* os) {
468 os->str("");
469 *os << type << kSdpDelimiterEqual << value;
470}
471
472// Init |os| to "a=|attribute|".
473static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
474 InitLine(kLineTypeAttributes, attribute, os);
475}
476
477// Writes a SDP attribute line based on |attribute| and |value| to |message|.
478static void AddAttributeLine(const std::string& attribute, int value,
479 std::string* message) {
480 std::ostringstream os;
481 InitAttrLine(attribute, &os);
482 os << kSdpDelimiterColon << value;
483 AddLine(os.str(), message);
484}
485
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000486static bool IsLineType(const std::string& message,
487 const char type,
488 size_t line_start) {
489 if (message.size() < line_start + kLinePrefixLength) {
490 return false;
491 }
492 const char* cmessage = message.c_str();
493 return (cmessage[line_start] == type &&
494 cmessage[line_start + 1] == kSdpDelimiterEqual);
495}
496
497static bool IsLineType(const std::string& line,
498 const char type) {
499 return IsLineType(line, type, 0);
500}
501
502static bool GetLineWithType(const std::string& message, size_t* pos,
503 std::string* line, const char type) {
504 if (!IsLineType(message, type, *pos)) {
505 return false;
506 }
507
508 if (!GetLine(message, pos, line))
509 return false;
510
511 return true;
512}
513
514static bool HasAttribute(const std::string& line,
515 const std::string& attribute) {
516 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
517}
518
Peter Boström0c4e06b2015-10-07 12:23:21 +0200519static bool AddSsrcLine(uint32_t ssrc_id,
520 const std::string& attribute,
521 const std::string& value,
522 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000523 // RFC 5576
524 // a=ssrc:<ssrc-id> <attribute>:<value>
525 std::ostringstream os;
526 InitAttrLine(kAttributeSsrc, &os);
527 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
528 << attribute << kSdpDelimiterColon << value;
529 return AddLine(os.str(), message);
530}
531
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000532// Get value only from <attribute>:<value>.
533static bool GetValue(const std::string& message, const std::string& attribute,
534 std::string* value, SdpParseError* error) {
535 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700536 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000537 return ParseFailedGetValue(message, attribute, error);
538 }
539 // The left part should end with the expected attribute.
540 if (leftpart.length() < attribute.length() ||
541 leftpart.compare(leftpart.length() - attribute.length(),
542 attribute.length(), attribute) != 0) {
543 return ParseFailedGetValue(message, attribute, error);
544 }
545 return true;
546}
547
548static bool CaseInsensitiveFind(std::string str1, std::string str2) {
549 std::transform(str1.begin(), str1.end(), str1.begin(),
550 ::tolower);
551 std::transform(str2.begin(), str2.end(), str2.begin(),
552 ::tolower);
553 return str1.find(str2) != std::string::npos;
554}
555
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000556template <class T>
557static bool GetValueFromString(const std::string& line,
558 const std::string& s,
559 T* t,
560 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000561 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000562 std::ostringstream description;
563 description << "Invalid value: " << s << ".";
564 return ParseFailed(line, description.str(), error);
565 }
566 return true;
567}
568
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000569static bool GetPayloadTypeFromString(const std::string& line,
570 const std::string& s,
571 int* payload_type,
572 SdpParseError* error) {
573 return GetValueFromString(line, s, payload_type, error) &&
574 cricket::IsValidRtpPayloadType(*payload_type);
575}
576
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800577// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
578// "a=msid" attribute, if it exists. They are empty if the attribute does not
579// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000580void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800581 const std::string& msid_stream_id,
582 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000583 StreamParamsVec* tracks) {
nisseede5da42017-01-12 05:15:36 -0800584 RTC_DCHECK(tracks != NULL);
585 RTC_DCHECK(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000586 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
587 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
588 if (ssrc_info->cname.empty()) {
589 continue;
590 }
591
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800592 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000593 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800594 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000595 // If there's no msid and there's mslabel, we consider this is a sdp from
596 // a older version of client that doesn't support msid.
597 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800598 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000599 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800600 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
601 // If there's no msid in the SSRC attributes, but there's a global one
602 // (from a=msid), use that. This is the case with unified plan SDP.
603 stream_id = msid_stream_id;
604 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000605 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800606 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800607 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000608 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800609 // If a stream/track ID wasn't populated from the SSRC attributes OR the
610 // msid attribute, use default/random values.
611 if (stream_id.empty()) {
612 stream_id = kDefaultMsid;
613 }
614 if (track_id.empty()) {
615 // TODO(ronghuawu): What should we do if the track id doesn't appear?
616 // Create random string (which will be used as track label later)?
617 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000618 }
619
620 StreamParamsVec::iterator track = tracks->begin();
621 for (; track != tracks->end(); ++track) {
622 if (track->id == track_id) {
623 break;
624 }
625 }
626 if (track == tracks->end()) {
627 // If we don't find an existing track, create a new one.
628 tracks->push_back(StreamParams());
629 track = tracks->end() - 1;
630 }
631 track->add_ssrc(ssrc_info->ssrc_id);
632 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800633 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000634 track->id = track_id;
635 }
636}
637
638void GetMediaStreamLabels(const ContentInfo* content,
639 std::set<std::string>* labels) {
640 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000641 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000642 content->description);
643 const cricket::StreamParamsVec& streams = media_desc->streams();
644 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
645 it != streams.end(); ++it) {
646 labels->insert(it->sync_label);
647 }
648}
649
650// RFC 5245
651// It is RECOMMENDED that default candidates be chosen based on the
652// likelihood of those candidates to work with the peer that is being
653// contacted. It is RECOMMENDED that relayed > reflexive > host.
654static const int kPreferenceUnknown = 0;
655static const int kPreferenceHost = 1;
656static const int kPreferenceReflexive = 2;
657static const int kPreferenceRelayed = 3;
658
659static int GetCandidatePreferenceFromType(const std::string& type) {
660 int preference = kPreferenceUnknown;
661 if (type == cricket::LOCAL_PORT_TYPE) {
662 preference = kPreferenceHost;
663 } else if (type == cricket::STUN_PORT_TYPE) {
664 preference = kPreferenceReflexive;
665 } else if (type == cricket::RELAY_PORT_TYPE) {
666 preference = kPreferenceRelayed;
667 } else {
nissec80e7412017-01-11 05:56:46 -0800668 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000669 }
670 return preference;
671}
672
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000673// Get ip and port of the default destination from the |candidates| with the
674// given value of |component_id|. The default candidate should be the one most
675// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000676// RFC 5245
677// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
678// TODO: Decide the default destination in webrtcsession and
679// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000680static void GetDefaultDestination(
681 const std::vector<Candidate>& candidates,
682 int component_id, std::string* port,
683 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000684 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000685 *port = kDummyPort;
686 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000687 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000688 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000689 for (std::vector<Candidate>::const_iterator it = candidates.begin();
690 it != candidates.end(); ++it) {
691 if (it->component() != component_id) {
692 continue;
693 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000694 // Default destination should be UDP only.
695 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000696 continue;
697 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000698 const int preference = GetCandidatePreferenceFromType(it->type());
699 const int family = it->address().ipaddr().family();
700 // See if this candidate is more preferable then the current one if it's the
701 // same family. Or if the current family is IPv4 already so we could safely
702 // ignore all IPv6 ones. WebRTC bug 4269.
703 // http://code.google.com/p/webrtc/issues/detail?id=4269
704 if ((preference <= current_preference && current_family == family) ||
705 (current_family == AF_INET && family == AF_INET6)) {
706 continue;
707 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000708 if (family == AF_INET) {
709 addr_type->assign(kConnectionIpv4Addrtype);
710 } else if (family == AF_INET6) {
711 addr_type->assign(kConnectionIpv6Addrtype);
712 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000713 current_preference = preference;
714 current_family = family;
715 *port = it->address().PortAsString();
716 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000717 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000718}
719
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000720// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000721static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000722 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700723 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000724 std::string* message) {
725 std::string new_lines;
726 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000727 // RFC 4566
728 // m=<media> <port> <proto> <fmt> ...
729 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000730 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000731 if (fields.size() < 3) {
732 return;
733 }
734
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000735 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000736 std::string rtp_port, rtp_ip, addr_type;
737 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
738 &rtp_port, &rtp_ip, &addr_type);
739 // Found default RTP candidate.
740 // RFC 5245
741 // The default candidates are added to the SDP as the default
742 // destination for media. For streams based on RTP, this is done by
743 // placing the IP address and port of the RTP candidate into the c and m
744 // lines, respectively.
745 // Update the port in the m line.
746 // If this is a m-line with port equal to 0, we don't change it.
747 if (fields[1] != kMediaPortRejected) {
748 new_lines.replace(fields[0].size() + 1,
749 fields[1].size(),
750 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000751 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000752 // Add the c line.
753 // RFC 4566
754 // c=<nettype> <addrtype> <connection-address>
755 InitLine(kLineTypeConnection, kConnectionNettype, &os);
756 os << " " << addr_type << " " << rtp_ip;
757 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000758 message->append(new_lines);
759}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000760
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000761// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
762static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000763 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
764 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
765 &rtcp_port, &rtcp_ip, &addr_type);
766 // Found default RTCP candidate.
767 // RFC 5245
768 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
769 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000770
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000771 // RFC 3605
772 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
773 // connection-address] CRLF
774 std::ostringstream os;
775 InitAttrLine(kAttributeRtcp, &os);
776 os << kSdpDelimiterColon
777 << rtcp_port << " "
778 << kConnectionNettype << " "
779 << addr_type << " "
780 << rtcp_ip;
781 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000782 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000783}
784
785// Get candidates according to the mline index from SessionDescriptionInterface.
786static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
787 int mline_index,
788 std::vector<Candidate>* candidates) {
789 if (!candidates) {
790 return;
791 }
792 const IceCandidateCollection* cc = desci.candidates(mline_index);
793 for (size_t i = 0; i < cc->count(); ++i) {
794 const IceCandidateInterface* candidate = cc->at(i);
795 candidates->push_back(candidate->candidate());
796 }
797}
798
deadbeef9d3584c2016-02-16 17:54:10 -0800799std::string SdpSerialize(const JsepSessionDescription& jdesc,
800 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000801 const cricket::SessionDescription* desc = jdesc.description();
802 if (!desc) {
803 return "";
804 }
805
806 std::string message;
807
808 // Session Description.
809 AddLine(kSessionVersion, &message);
810 // Session Origin
811 // RFC 4566
812 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
813 // <unicast-address>
814 std::ostringstream os;
815 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700816 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000817 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700818 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000819 kSessionOriginSessionVersion : jdesc.session_version();
820 os << " " << session_id << " " << session_version << " "
821 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
822 << kSessionOriginAddress;
823 AddLine(os.str(), &message);
824 AddLine(kSessionName, &message);
825
826 // Time Description.
827 AddLine(kTimeDescription, &message);
828
829 // Group
830 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
831 std::string group_line = kAttrGroup;
832 const cricket::ContentGroup* group =
833 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
nisseede5da42017-01-12 05:15:36 -0800834 RTC_DCHECK(group != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000835 const cricket::ContentNames& content_names = group->content_names();
836 for (cricket::ContentNames::const_iterator it = content_names.begin();
837 it != content_names.end(); ++it) {
838 group_line.append(" ");
839 group_line.append(*it);
840 }
841 AddLine(group_line, &message);
842 }
843
844 // MediaStream semantics
845 InitAttrLine(kAttributeMsidSemantics, &os);
846 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000847
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000848 std::set<std::string> media_stream_labels;
849 const ContentInfo* audio_content = GetFirstAudioContent(desc);
850 if (audio_content)
851 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000852
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000853 const ContentInfo* video_content = GetFirstVideoContent(desc);
854 if (video_content)
855 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000856
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000857 for (std::set<std::string>::const_iterator it =
858 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
859 os << " " << *it;
860 }
861 AddLine(os.str(), &message);
862
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000863 // Preserve the order of the media contents.
864 int mline_index = -1;
865 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
866 it != desc->contents().end(); ++it) {
867 const MediaContentDescription* mdesc =
868 static_cast<const MediaContentDescription*>(it->description);
869 std::vector<Candidate> candidates;
870 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800871 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
872 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000873 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000874 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000875 return message;
876}
877
878// Serializes the passed in IceCandidateInterface to a SDP string.
879// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700880std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
881 return SdpSerializeCandidate(candidate.candidate());
882}
883
884// Serializes a cricket Candidate.
885std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000886 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700887 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800888 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000889 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
890 // just candidate:<candidate> not a=candidate:<blah>CRLF
nisseede5da42017-01-12 05:15:36 -0800891 RTC_DCHECK(message.find("a=") == 0);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000892 message.erase(0, 2);
nisseede5da42017-01-12 05:15:36 -0800893 RTC_DCHECK(message.find(kLineBreak) == message.size() - 2);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000894 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000895 return message;
896}
897
898bool SdpDeserialize(const std::string& message,
899 JsepSessionDescription* jdesc,
900 SdpParseError* error) {
901 std::string session_id;
902 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700903 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000904 RtpHeaderExtensions session_extmaps;
905 cricket::SessionDescription* desc = new cricket::SessionDescription();
906 std::vector<JsepIceCandidate*> candidates;
907 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000908
909 // Session Description
910 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700911 &session_version, &session_td, &session_extmaps,
912 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000913 delete desc;
914 return false;
915 }
916
917 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700918 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
919 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000920 delete desc;
921 for (std::vector<JsepIceCandidate*>::const_iterator
922 it = candidates.begin(); it != candidates.end(); ++it) {
923 delete *it;
924 }
925 return false;
926 }
927
928 jdesc->Initialize(desc, session_id, session_version);
929
930 for (std::vector<JsepIceCandidate*>::const_iterator
931 it = candidates.begin(); it != candidates.end(); ++it) {
932 jdesc->AddCandidate(*it);
933 delete *it;
934 }
935 return true;
936}
937
938bool SdpDeserializeCandidate(const std::string& message,
939 JsepIceCandidate* jcandidate,
940 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -0800941 RTC_DCHECK(jcandidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000942 Candidate candidate;
943 if (!ParseCandidate(message, &candidate, error, true)) {
944 return false;
945 }
946 jcandidate->SetCandidate(candidate);
947 return true;
948}
949
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700950bool SdpDeserializeCandidate(const std::string& transport_name,
951 const std::string& message,
952 cricket::Candidate* candidate,
953 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -0800954 RTC_DCHECK(candidate != nullptr);
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700955 if (!ParseCandidate(message, candidate, error, true)) {
956 return false;
957 }
958 candidate->set_transport_name(transport_name);
959 return true;
960}
961
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000962bool ParseCandidate(const std::string& message, Candidate* candidate,
963 SdpParseError* error, bool is_raw) {
nisseede5da42017-01-12 05:15:36 -0800964 RTC_DCHECK(candidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000965
966 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000967 std::string first_line = message;
968 size_t pos = 0;
969 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000970
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000971 // Makes sure |message| contains only one line.
972 if (message.size() > first_line.size()) {
973 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700974 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
975 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000976 return ParseFailed(message, 0, "Expect one line only", error);
977 }
978 }
979
980 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
981 // candidate:<candidate> when trickled, but we still support
982 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
983 // from the SDP.
984 if (IsLineType(first_line, kLineTypeAttributes)) {
985 first_line = first_line.substr(kLinePrefixLength);
986 }
987
988 std::string attribute_candidate;
989 std::string candidate_value;
990
991 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700992 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
993 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000994 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000995 if (is_raw) {
996 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000997 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000998 << ":" << "<candidate-str>";
999 return ParseFailed(first_line, 0, description.str(), error);
1000 } else {
1001 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
1002 kAttributeCandidate, error);
1003 }
1004 }
1005
1006 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001007 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1008
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001009 // RFC 5245
1010 // a=candidate:<foundation> <component-id> <transport> <priority>
1011 // <connection-address> <port> typ <candidate-types>
1012 // [raddr <connection-address>] [rport <port>]
1013 // *(SP extension-att-name SP extension-att-value)
1014 const size_t expected_min_fields = 8;
1015 if (fields.size() < expected_min_fields ||
1016 (fields[6] != kAttributeCandidateTyp)) {
1017 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1018 }
jbauch083b73f2015-07-16 02:46:32 -07001019 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001020
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001021 int component_id = 0;
1022 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1023 return false;
1024 }
jbauch083b73f2015-07-16 02:46:32 -07001025 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001026 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001027 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1028 return false;
1029 }
jbauch083b73f2015-07-16 02:46:32 -07001030 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001031 int port = 0;
1032 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1033 return false;
1034 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001035 SocketAddress address(connection_address, port);
1036
1037 cricket::ProtocolType protocol;
hnslb68cc752016-12-13 10:33:41 -08001038 if (!StringToProto(transport.c_str(), &protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001039 return ParseFailed(first_line, "Unsupported transport type.", error);
1040 }
hnslb68cc752016-12-13 10:33:41 -08001041 switch (protocol) {
1042 case cricket::PROTO_UDP:
1043 case cricket::PROTO_TCP:
1044 case cricket::PROTO_SSLTCP:
1045 // Supported protocol.
1046 break;
1047 default:
1048 return ParseFailed(first_line, "Unsupported transport type.", error);
1049 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001050
1051 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001052 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001053 if (type == kCandidateHost) {
1054 candidate_type = cricket::LOCAL_PORT_TYPE;
1055 } else if (type == kCandidateSrflx) {
1056 candidate_type = cricket::STUN_PORT_TYPE;
1057 } else if (type == kCandidateRelay) {
1058 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001059 } else if (type == kCandidatePrflx) {
1060 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001061 } else {
1062 return ParseFailed(first_line, "Unsupported candidate type.", error);
1063 }
1064
1065 size_t current_position = expected_min_fields;
1066 SocketAddress related_address;
1067 // The 2 optional fields for related address
1068 // [raddr <connection-address>] [rport <port>]
1069 if (fields.size() >= (current_position + 2) &&
1070 fields[current_position] == kAttributeCandidateRaddr) {
1071 related_address.SetIP(fields[++current_position]);
1072 ++current_position;
1073 }
1074 if (fields.size() >= (current_position + 2) &&
1075 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001076 int port = 0;
1077 if (!GetValueFromString(
1078 first_line, fields[++current_position], &port, error)) {
1079 return false;
1080 }
1081 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001082 ++current_position;
1083 }
1084
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001085 // If this is a TCP candidate, it has additional extension as defined in
1086 // RFC 6544.
1087 std::string tcptype;
1088 if (fields.size() >= (current_position + 2) &&
1089 fields[current_position] == kTcpCandidateType) {
1090 tcptype = fields[++current_position];
1091 ++current_position;
1092
1093 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1094 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1095 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1096 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1097 }
1098
1099 if (protocol != cricket::PROTO_TCP) {
1100 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1101 }
1102 }
1103
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001104 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001105 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1106 // the candidate to avoid issues with confusing which generation a candidate
1107 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001108 std::string username;
1109 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001110 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001111 uint16_t network_id = 0;
1112 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001113 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1114 // RFC 5245
1115 // *(SP extension-att-name SP extension-att-value)
1116 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001117 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1118 return false;
1119 }
honghaiza54a0802015-12-16 18:37:23 -08001120 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001121 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001122 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001123 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001124 } else if (fields[i] == kAttributeCandidateNetworkId) {
1125 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1126 return false;
1127 }
honghaize1a0c942016-02-16 14:54:56 -08001128 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1129 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1130 return false;
1131 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001132 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001133 } else {
1134 // Skip the unknown extension.
1135 ++i;
1136 }
1137 }
1138
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001139 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001140 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001141 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001142 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001143 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001144 return true;
1145}
1146
1147bool ParseIceOptions(const std::string& line,
1148 std::vector<std::string>* transport_options,
1149 SdpParseError* error) {
1150 std::string ice_options;
1151 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1152 return false;
1153 }
1154 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001155 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001156 for (size_t i = 0; i < fields.size(); ++i) {
1157 transport_options->push_back(fields[i]);
1158 }
1159 return true;
1160}
1161
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001162bool ParseSctpPort(const std::string& line,
1163 int* sctp_port,
1164 SdpParseError* error) {
1165 // draft-ietf-mmusic-sctp-sdp-07
1166 // a=sctp-port
1167 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001168 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001169 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1170 if (fields.size() < expected_min_fields) {
1171 fields.resize(0);
1172 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1173 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001174 if (fields.size() < expected_min_fields) {
1175 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1176 }
1177 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001178 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001179 }
1180 return true;
1181}
1182
isheriff6f8d6862016-05-26 11:24:55 -07001183bool ParseExtmap(const std::string& line,
1184 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001185 SdpParseError* error) {
1186 // RFC 5285
1187 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1188 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001189 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001190 kSdpDelimiterSpace, &fields);
1191 const size_t expected_min_fields = 2;
1192 if (fields.size() < expected_min_fields) {
1193 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1194 }
1195 std::string uri = fields[1];
1196
1197 std::string value_direction;
1198 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1199 return false;
1200 }
1201 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001202 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001203 int value = 0;
1204 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1205 return false;
1206 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001207
isheriff6f8d6862016-05-26 11:24:55 -07001208 *extmap = RtpExtension(uri, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001209 return true;
1210}
1211
1212void BuildMediaDescription(const ContentInfo* content_info,
1213 const TransportInfo* transport_info,
1214 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001215 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001216 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001217 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001218 RTC_DCHECK(message != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001219 if (content_info == NULL || message == NULL) {
1220 return;
1221 }
1222 // TODO: Rethink if we should use sprintfn instead of stringstream.
1223 // According to the style guide, streams should only be used for logging.
1224 // http://google-styleguide.googlecode.com/svn/
1225 // trunk/cppguide.xml?showone=Streams#Streams
1226 std::ostringstream os;
1227 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001228 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001229 content_info->description);
nisseede5da42017-01-12 05:15:36 -08001230 RTC_DCHECK(media_desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001231
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001232 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001233
1234 // RFC 4566
1235 // m=<media> <port> <proto> <fmt>
1236 // fmt is a list of payload type numbers that MAY be used in the session.
1237 const char* type = NULL;
1238 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1239 type = kMediaTypeAudio;
1240 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1241 type = kMediaTypeVideo;
1242 else if (media_type == cricket::MEDIA_TYPE_DATA)
1243 type = kMediaTypeData;
1244 else
nissec80e7412017-01-11 05:56:46 -08001245 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001246
1247 std::string fmt;
1248 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1249 const VideoContentDescription* video_desc =
1250 static_cast<const VideoContentDescription*>(media_desc);
1251 for (std::vector<cricket::VideoCodec>::const_iterator it =
1252 video_desc->codecs().begin();
1253 it != video_desc->codecs().end(); ++it) {
1254 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001255 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001256 }
1257 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1258 const AudioContentDescription* audio_desc =
1259 static_cast<const AudioContentDescription*>(media_desc);
1260 for (std::vector<cricket::AudioCodec>::const_iterator it =
1261 audio_desc->codecs().begin();
1262 it != audio_desc->codecs().end(); ++it) {
1263 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001264 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001265 }
1266 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001267 const DataContentDescription* data_desc =
1268 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001269 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001270 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001271
1272 for (std::vector<cricket::DataCodec>::const_iterator it =
1273 data_desc->codecs().begin();
1274 it != data_desc->codecs().end(); ++it) {
solenberg9fa49752016-10-08 13:02:44 -07001275 if (cricket::CodecNamesEq(it->name, cricket::kGoogleSctpDataCodecName)
1276 && it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001277 break;
1278 }
1279 }
1280
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001281 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001282 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001283 for (std::vector<cricket::DataCodec>::const_iterator it =
1284 data_desc->codecs().begin();
1285 it != data_desc->codecs().end(); ++it) {
1286 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001287 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001288 }
1289 }
1290 }
1291 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1292 // to 0.
1293 if (fmt.empty()) {
1294 fmt = " 0";
1295 }
1296
deadbeef25ed4352016-12-12 18:37:36 -08001297 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001298 // the candidates.
deadbeef25ed4352016-12-12 18:37:36 -08001299 //
1300 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001301 // RFC 3264
1302 // To reject an offered stream, the port number in the corresponding stream in
1303 // the answer MUST be set to zero.
deadbeef25ed4352016-12-12 18:37:36 -08001304 //
1305 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1306 // with a=bundle-only.
1307 const std::string& port =
1308 (content_info->rejected || content_info->bundle_only) ? kMediaPortRejected
1309 : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001310
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001311 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001312 transport_info->description.identity_fingerprint.get() : NULL;
1313
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001314 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001315 InitLine(kLineTypeMedia, type, &os);
1316 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001317 std::string mline = os.str();
1318 UpdateMediaDefaultDestination(candidates, mline, message);
1319
1320 // RFC 4566
1321 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001322 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001323 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1324 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1325 AddLine(os.str(), message);
1326 }
1327
deadbeef25ed4352016-12-12 18:37:36 -08001328 // Add the a=bundle-only line.
1329 if (content_info->bundle_only) {
1330 InitAttrLine(kAttributeBundleOnly, &os);
1331 AddLine(os.str(), message);
1332 }
1333
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001334 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001335 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001336 std::string rtcp_line = GetRtcpLine(candidates);
1337 if (!rtcp_line.empty()) {
1338 AddLine(rtcp_line, message);
1339 }
1340 }
1341
honghaiza54a0802015-12-16 18:37:23 -08001342 // Build the a=candidate lines. We don't include ufrag and pwd in the
1343 // candidates in the SDP to avoid redundancy.
1344 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001345
1346 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1347 if (transport_info) {
1348 // RFC 5245
1349 // ice-pwd-att = "ice-pwd" ":" password
1350 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1351 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001352 if (!transport_info->description.ice_ufrag.empty()) {
1353 InitAttrLine(kAttributeIceUfrag, &os);
1354 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1355 AddLine(os.str(), message);
1356 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001357 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001358 if (!transport_info->description.ice_pwd.empty()) {
1359 InitAttrLine(kAttributeIcePwd, &os);
1360 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1361 AddLine(os.str(), message);
1362 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001363
1364 // draft-petithuguenin-mmusic-ice-attributes-level-03
1365 BuildIceOptions(transport_info->description.transport_options, message);
1366
1367 // RFC 4572
1368 // fingerprint-attribute =
1369 // "fingerprint" ":" hash-func SP fingerprint
1370 if (fp) {
1371 // Insert the fingerprint attribute.
1372 InitAttrLine(kAttributeFingerprint, &os);
1373 os << kSdpDelimiterColon
1374 << fp->algorithm << kSdpDelimiterSpace
1375 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001376 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001377
1378 // Inserting setup attribute.
1379 if (transport_info->description.connection_role !=
1380 cricket::CONNECTIONROLE_NONE) {
1381 // Making sure we are not using "passive" mode.
1382 cricket::ConnectionRole role =
1383 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001384 std::string dtls_role_str;
1385 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001386 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001387 os << kSdpDelimiterColon << dtls_role_str;
1388 AddLine(os.str(), message);
1389 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001390 }
1391 }
1392
1393 // RFC 3388
1394 // mid-attribute = "a=mid:" identification-tag
1395 // identification-tag = token
1396 // Use the content name as the mid identification-tag.
1397 InitAttrLine(kAttributeMid, &os);
1398 os << kSdpDelimiterColon << content_info->name;
1399 AddLine(os.str(), message);
1400
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001401 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001402 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001403 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001404 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1405 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001406 }
1407}
1408
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001409void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001410 // draft-ietf-mmusic-sctp-sdp-04
1411 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001412 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1413 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001414 std::ostringstream os;
1415 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001416 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001417 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -07001418 << cricket::kMaxSctpStreams;
wu@webrtc.org78187522013-10-07 23:32:02 +00001419 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001420}
1421
deadbeef9d3584c2016-02-16 17:54:10 -08001422// If unified_plan_sdp is true, will use "a=msid".
1423void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1424 const MediaType media_type,
1425 bool unified_plan_sdp,
1426 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001427 std::ostringstream os;
1428 // RFC 5285
1429 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1430 // The definitions MUST be either all session level or all media level. This
1431 // implementation uses all media level.
1432 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1433 InitAttrLine(kAttributeExtmap, &os);
1434 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1435 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1436 AddLine(os.str(), message);
1437 }
1438
1439 // RFC 3264
1440 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001441 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001442 case cricket::MD_INACTIVE:
1443 InitAttrLine(kAttributeInactive, &os);
1444 break;
1445 case cricket::MD_SENDONLY:
1446 InitAttrLine(kAttributeSendOnly, &os);
1447 break;
1448 case cricket::MD_RECVONLY:
1449 InitAttrLine(kAttributeRecvOnly, &os);
1450 break;
1451 case cricket::MD_SENDRECV:
1452 default:
1453 InitAttrLine(kAttributeSendRecv, &os);
1454 break;
1455 }
1456 AddLine(os.str(), message);
1457
deadbeef9d3584c2016-02-16 17:54:10 -08001458 // draft-ietf-mmusic-msid-11
1459 // a=msid:<stream id> <track id>
1460 if (unified_plan_sdp && !media_desc->streams().empty()) {
1461 if (media_desc->streams().size() > 1u) {
1462 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1463 << "one track in a media section. Omitting 'a=msid'.";
1464 } else {
1465 auto track = media_desc->streams().begin();
1466 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001467 InitAttrLine(kAttributeMsid, &os);
1468 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1469 AddLine(os.str(), message);
1470 }
1471 }
1472
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001473 // RFC 5761
1474 // a=rtcp-mux
1475 if (media_desc->rtcp_mux()) {
1476 InitAttrLine(kAttributeRtcpMux, &os);
1477 AddLine(os.str(), message);
1478 }
1479
deadbeef13871492015-12-09 12:37:51 -08001480 // RFC 5506
1481 // a=rtcp-rsize
1482 if (media_desc->rtcp_reduced_size()) {
1483 InitAttrLine(kAttributeRtcpReducedSize, &os);
1484 AddLine(os.str(), message);
1485 }
1486
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001487 // RFC 4568
1488 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1489 for (std::vector<CryptoParams>::const_iterator it =
1490 media_desc->cryptos().begin();
1491 it != media_desc->cryptos().end(); ++it) {
1492 InitAttrLine(kAttributeCrypto, &os);
1493 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1494 << it->key_params;
1495 if (!it->session_params.empty()) {
1496 os << " " << it->session_params;
1497 }
1498 AddLine(os.str(), message);
1499 }
1500
1501 // RFC 4566
1502 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1503 // [/<encodingparameters>]
1504 BuildRtpMap(media_desc, media_type, message);
1505
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001506 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1507 track != media_desc->streams().end(); ++track) {
1508 // Require that the track belongs to a media stream,
1509 // ie the sync_label is set. This extra check is necessary since the
1510 // MediaContentDescription always contains a streamparam with an ssrc even
1511 // if no track or media stream have been created.
1512 if (track->sync_label.empty()) continue;
1513
1514 // Build the ssrc-group lines.
1515 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1516 // RFC 5576
1517 // a=ssrc-group:<semantics> <ssrc-id> ...
1518 if (track->ssrc_groups[i].ssrcs.empty()) {
1519 continue;
1520 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001521 InitAttrLine(kAttributeSsrcGroup, &os);
1522 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001523 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001524 track->ssrc_groups[i].ssrcs.begin();
1525 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001526 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001527 }
1528 AddLine(os.str(), message);
1529 }
1530 // Build the ssrc lines for each ssrc.
1531 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001532 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001533 // RFC 5576
1534 // a=ssrc:<ssrc-id> cname:<value>
1535 AddSsrcLine(ssrc, kSsrcAttributeCname,
1536 track->cname, message);
1537
1538 // draft-alvestrand-mmusic-msid-00
1539 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001540 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1541 // which corresponds to the "id" attribute of StreamParams.
1542 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001543 InitAttrLine(kAttributeSsrc, &os);
1544 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001545 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1546 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001547 AddLine(os.str(), message);
1548
deadbeef9d3584c2016-02-16 17:54:10 -08001549 // TODO(ronghuawu): Remove below code which is for backward
1550 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001551 // draft-alvestrand-rtcweb-mid-01
1552 // a=ssrc:<ssrc-id> mslabel:<value>
1553 // The label isn't yet defined.
1554 // a=ssrc:<ssrc-id> label:<value>
1555 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1556 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1557 }
1558 }
1559}
1560
1561void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1562 // fmtp header: a=fmtp:|payload_type| <parameters>
1563 // Add a=fmtp
1564 InitAttrLine(kAttributeFmtp, os);
1565 // Add :|payload_type|
1566 *os << kSdpDelimiterColon << payload_type;
1567}
1568
1569void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1570 // rtcp-fb header: a=rtcp-fb:|payload_type|
1571 // <parameters>/<ccm <ccm_parameters>>
1572 // Add a=rtcp-fb
1573 InitAttrLine(kAttributeRtcpFb, os);
1574 // Add :
1575 *os << kSdpDelimiterColon;
1576 if (payload_type == kWildcardPayloadType) {
1577 *os << "*";
1578 } else {
1579 *os << payload_type;
1580 }
1581}
1582
1583void WriteFmtpParameter(const std::string& parameter_name,
1584 const std::string& parameter_value,
1585 std::ostringstream* os) {
1586 // fmtp parameters: |parameter_name|=|parameter_value|
1587 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1588}
1589
1590void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1591 std::ostringstream* os) {
1592 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1593 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 11:02:14 -07001594 // Parameters are a semicolon-separated list, no spaces.
1595 // The list is separated from the header by a space.
1596 if (fmtp == parameters.begin()) {
1597 *os << kSdpDelimiterSpace;
1598 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001599 *os << kSdpDelimiterSemicolon;
1600 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001601 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1602 }
1603}
1604
1605bool IsFmtpParam(const std::string& name) {
1606 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001607 // TODO(hta): Split FMTP parameters apart from parameters in general.
1608 // FMTP parameters are codec specific, not generic.
1609 kCodecParamMinPTime,
1610 kCodecParamSPropStereo,
1611 kCodecParamStereo,
1612 kCodecParamUseInbandFec,
1613 kCodecParamUseDtx,
1614 kCodecParamStartBitrate,
1615 kCodecParamMaxBitrate,
1616 kCodecParamMinBitrate,
1617 kCodecParamMaxQuantization,
1618 kCodecParamSctpProtocol,
1619 kCodecParamSctpStreams,
1620 kCodecParamMaxAverageBitrate,
1621 kCodecParamMaxPlaybackRate,
1622 kCodecParamAssociatedPayloadType,
1623 cricket::kH264FmtpPacketizationMode,
1624 cricket::kH264FmtpLevelAsymmetryAllowed,
brandtr87d7d772016-11-07 03:03:41 -08001625 cricket::kH264FmtpProfileLevelId,
1626 cricket::kFlexfecFmtpRepairWindow};
tfarina5237aaf2015-11-10 23:44:30 -08001627 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001628 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001629 return true;
1630 }
1631 }
1632 return false;
1633}
1634
1635// Retreives fmtp parameters from |params|, which may contain other parameters
1636// as well, and puts them in |fmtp_parameters|.
1637void GetFmtpParams(const cricket::CodecParameterMap& params,
1638 cricket::CodecParameterMap* fmtp_parameters) {
1639 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1640 iter != params.end(); ++iter) {
1641 if (IsFmtpParam(iter->first)) {
1642 (*fmtp_parameters)[iter->first] = iter->second;
1643 }
1644 }
1645}
1646
1647template <class T>
1648void AddFmtpLine(const T& codec, std::string* message) {
1649 cricket::CodecParameterMap fmtp_parameters;
1650 GetFmtpParams(codec.params, &fmtp_parameters);
1651 if (fmtp_parameters.empty()) {
1652 // No need to add an fmtp if it will have no (optional) parameters.
1653 return;
1654 }
1655 std::ostringstream os;
1656 WriteFmtpHeader(codec.id, &os);
1657 WriteFmtpParameters(fmtp_parameters, &os);
1658 AddLine(os.str(), message);
1659 return;
1660}
1661
1662template <class T>
1663void AddRtcpFbLines(const T& codec, std::string* message) {
1664 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1665 codec.feedback_params.params().begin();
1666 iter != codec.feedback_params.params().end(); ++iter) {
1667 std::ostringstream os;
1668 WriteRtcpFbHeader(codec.id, &os);
1669 os << " " << iter->id();
1670 if (!iter->param().empty()) {
1671 os << " " << iter->param();
1672 }
1673 AddLine(os.str(), message);
1674 }
1675}
1676
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001677bool AddSctpDataCodec(DataContentDescription* media_desc,
1678 int sctp_port) {
solenberg9fa49752016-10-08 13:02:44 -07001679 for (const auto& codec : media_desc->codecs()) {
1680 if (cricket::CodecNamesEq(codec.name, cricket::kGoogleSctpDataCodecName)) {
1681 return ParseFailed("",
1682 "Can't have multiple sctp port attributes.",
1683 NULL);
1684 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001685 }
1686 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 13:02:44 -07001687 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 10:07:16 -07001688 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001689 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1690 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1691 << sctp_port;
1692 media_desc->AddCodec(codec_port);
1693 return true;
1694}
1695
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001696bool GetMinValue(const std::vector<int>& values, int* value) {
1697 if (values.empty()) {
1698 return false;
1699 }
1700 std::vector<int>::const_iterator found =
1701 std::min_element(values.begin(), values.end());
1702 *value = *found;
1703 return true;
1704}
1705
1706bool GetParameter(const std::string& name,
1707 const cricket::CodecParameterMap& params, int* value) {
1708 std::map<std::string, std::string>::const_iterator found =
1709 params.find(name);
1710 if (found == params.end()) {
1711 return false;
1712 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001713 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001714 return false;
1715 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001716 return true;
1717}
1718
1719void BuildRtpMap(const MediaContentDescription* media_desc,
1720 const MediaType media_type,
1721 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001722 RTC_DCHECK(message != NULL);
1723 RTC_DCHECK(media_desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001724 std::ostringstream os;
1725 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1726 const VideoContentDescription* video_desc =
1727 static_cast<const VideoContentDescription*>(media_desc);
1728 for (std::vector<cricket::VideoCodec>::const_iterator it =
1729 video_desc->codecs().begin();
1730 it != video_desc->codecs().end(); ++it) {
1731 // RFC 4566
1732 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1733 // [/<encodingparameters>]
1734 if (it->id != kWildcardPayloadType) {
1735 InitAttrLine(kAttributeRtpmap, &os);
1736 os << kSdpDelimiterColon << it->id << " " << it->name
1737 << "/" << kDefaultVideoClockrate;
1738 AddLine(os.str(), message);
1739 }
1740 AddRtcpFbLines(*it, message);
1741 AddFmtpLine(*it, message);
1742 }
1743 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1744 const AudioContentDescription* audio_desc =
1745 static_cast<const AudioContentDescription*>(media_desc);
1746 std::vector<int> ptimes;
1747 std::vector<int> maxptimes;
1748 int max_minptime = 0;
1749 for (std::vector<cricket::AudioCodec>::const_iterator it =
1750 audio_desc->codecs().begin();
1751 it != audio_desc->codecs().end(); ++it) {
nisseede5da42017-01-12 05:15:36 -08001752 RTC_DCHECK(!it->name.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001753 // RFC 4566
1754 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1755 // [/<encodingparameters>]
1756 InitAttrLine(kAttributeRtpmap, &os);
1757 os << kSdpDelimiterColon << it->id << " ";
1758 os << it->name << "/" << it->clockrate;
1759 if (it->channels != 1) {
1760 os << "/" << it->channels;
1761 }
1762 AddLine(os.str(), message);
1763 AddRtcpFbLines(*it, message);
1764 AddFmtpLine(*it, message);
1765 int minptime = 0;
1766 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1767 max_minptime = std::max(minptime, max_minptime);
1768 }
1769 int ptime;
1770 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1771 ptimes.push_back(ptime);
1772 }
1773 int maxptime;
1774 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1775 maxptimes.push_back(maxptime);
1776 }
1777 }
1778 // Populate the maxptime attribute with the smallest maxptime of all codecs
1779 // under the same m-line.
1780 int min_maxptime = INT_MAX;
1781 if (GetMinValue(maxptimes, &min_maxptime)) {
1782 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1783 }
nisseede5da42017-01-12 05:15:36 -08001784 RTC_DCHECK(min_maxptime > max_minptime);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001785 // Populate the ptime attribute with the smallest ptime or the largest
1786 // minptime, whichever is the largest, for all codecs under the same m-line.
1787 int ptime = INT_MAX;
1788 if (GetMinValue(ptimes, &ptime)) {
1789 ptime = std::min(ptime, min_maxptime);
1790 ptime = std::max(ptime, max_minptime);
1791 AddAttributeLine(kCodecParamPTime, ptime, message);
1792 }
1793 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1794 const DataContentDescription* data_desc =
1795 static_cast<const DataContentDescription*>(media_desc);
1796 for (std::vector<cricket::DataCodec>::const_iterator it =
1797 data_desc->codecs().begin();
1798 it != data_desc->codecs().end(); ++it) {
1799 // RFC 4566
1800 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1801 // [/<encodingparameters>]
1802 InitAttrLine(kAttributeRtpmap, &os);
1803 os << kSdpDelimiterColon << it->id << " "
1804 << it->name << "/" << it->clockrate;
1805 AddLine(os.str(), message);
1806 }
1807 }
1808}
1809
1810void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001811 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001812 std::string* message) {
1813 std::ostringstream os;
1814
1815 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1816 it != candidates.end(); ++it) {
1817 // RFC 5245
1818 // a=candidate:<foundation> <component-id> <transport> <priority>
1819 // <connection-address> <port> typ <candidate-types>
1820 // [raddr <connection-address>] [rport <port>]
1821 // *(SP extension-att-name SP extension-att-value)
1822 std::string type;
1823 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1824 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1825 type = kCandidateHost;
1826 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1827 type = kCandidateSrflx;
1828 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1829 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001830 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1831 type = kCandidatePrflx;
1832 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001833 } else {
nissec80e7412017-01-11 05:56:46 -08001834 RTC_NOTREACHED();
Peter Thatcher019087f2015-04-28 09:06:26 -07001835 // Never write out candidates if we don't know the type.
1836 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001837 }
1838
1839 InitAttrLine(kAttributeCandidate, &os);
1840 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001841 << it->foundation() << " "
1842 << it->component() << " "
1843 << it->protocol() << " "
1844 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001845 << it->address().ipaddr().ToString() << " "
1846 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001847 << kAttributeCandidateTyp << " "
1848 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001849
1850 // Related address
1851 if (!it->related_address().IsNil()) {
1852 os << kAttributeCandidateRaddr << " "
1853 << it->related_address().ipaddr().ToString() << " "
1854 << kAttributeCandidateRport << " "
1855 << it->related_address().PortAsString() << " ";
1856 }
1857
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001858 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001859 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001860 }
1861
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001862 // Extensions
1863 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001864 if (include_ufrag && !it->username().empty()) {
1865 os << " " << kAttributeCandidateUfrag << " " << it->username();
1866 }
honghaiza0c44ea2016-03-23 16:07:48 -07001867 if (it->network_id() > 0) {
1868 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1869 }
honghaize1a0c942016-02-16 14:54:56 -08001870 if (it->network_cost() > 0) {
1871 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1872 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001873
1874 AddLine(os.str(), message);
1875 }
1876}
1877
1878void BuildIceOptions(const std::vector<std::string>& transport_options,
1879 std::string* message) {
1880 if (!transport_options.empty()) {
1881 std::ostringstream os;
1882 InitAttrLine(kAttributeIceOption, &os);
1883 os << kSdpDelimiterColon << transport_options[0];
1884 for (size_t i = 1; i < transport_options.size(); ++i) {
1885 os << kSdpDelimiterSpace << transport_options[i];
1886 }
1887 AddLine(os.str(), message);
1888 }
1889}
1890
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001891bool IsRtp(const std::string& protocol) {
1892 return protocol.empty() ||
1893 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1894}
1895
1896bool IsDtlsSctp(const std::string& protocol) {
1897 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001898 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001899}
1900
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001901bool ParseSessionDescription(const std::string& message, size_t* pos,
1902 std::string* session_id,
1903 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001904 TransportDescription* session_td,
1905 RtpHeaderExtensions* session_extmaps,
1906 cricket::SessionDescription* desc,
1907 SdpParseError* error) {
1908 std::string line;
1909
deadbeefc80741f2015-10-22 13:14:45 -07001910 desc->set_msid_supported(false);
1911
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001912 // RFC 4566
1913 // v= (protocol version)
1914 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1915 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1916 std::string(), error);
1917 }
1918 // RFC 4566
1919 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1920 // <unicast-address>
1921 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1922 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1923 std::string(), error);
1924 }
1925 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001926 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001927 kSdpDelimiterSpace, &fields);
1928 const size_t expected_fields = 6;
1929 if (fields.size() != expected_fields) {
1930 return ParseFailedExpectFieldNum(line, expected_fields, error);
1931 }
1932 *session_id = fields[1];
1933 *session_version = fields[2];
1934
1935 // RFC 4566
1936 // s= (session name)
1937 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1938 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1939 std::string(), error);
1940 }
1941
1942 // Optional lines
1943 // Those are the optional lines, so shouldn't return false if not present.
1944 // RFC 4566
1945 // i=* (session information)
1946 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1947
1948 // RFC 4566
1949 // u=* (URI of description)
1950 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1951
1952 // RFC 4566
1953 // e=* (email address)
1954 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1955
1956 // RFC 4566
1957 // p=* (phone number)
1958 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1959
1960 // RFC 4566
1961 // c=* (connection information -- not required if included in
1962 // all media)
1963 GetLineWithType(message, pos, &line, kLineTypeConnection);
1964
1965 // RFC 4566
1966 // b=* (zero or more bandwidth information lines)
1967 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1968 // By pass zero or more b lines.
1969 }
1970
1971 // RFC 4566
1972 // One or more time descriptions ("t=" and "r=" lines; see below)
1973 // t= (time the session is active)
1974 // r=* (zero or more repeat times)
1975 // Ensure there's at least one time description
1976 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1977 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1978 error);
1979 }
1980
1981 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1982 // By pass zero or more r lines.
1983 }
1984
1985 // Go through the rest of the time descriptions
1986 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1987 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1988 // By pass zero or more r lines.
1989 }
1990 }
1991
1992 // RFC 4566
1993 // z=* (time zone adjustments)
1994 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1995
1996 // RFC 4566
1997 // k=* (encryption key)
1998 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1999
2000 // RFC 4566
2001 // a=* (zero or more session attribute lines)
2002 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
2003 if (HasAttribute(line, kAttributeGroup)) {
2004 if (!ParseGroupAttribute(line, desc, error)) {
2005 return false;
2006 }
2007 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2008 if (!GetValue(line, kAttributeIceUfrag,
2009 &(session_td->ice_ufrag), error)) {
2010 return false;
2011 }
2012 } else if (HasAttribute(line, kAttributeIcePwd)) {
2013 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2014 return false;
2015 }
2016 } else if (HasAttribute(line, kAttributeIceLite)) {
2017 session_td->ice_mode = cricket::ICEMODE_LITE;
2018 } else if (HasAttribute(line, kAttributeIceOption)) {
2019 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2020 return false;
2021 }
2022 } else if (HasAttribute(line, kAttributeFingerprint)) {
2023 if (session_td->identity_fingerprint.get()) {
2024 return ParseFailed(
2025 line,
2026 "Can't have multiple fingerprint attributes at the same level.",
2027 error);
2028 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002029 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002030 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2031 return false;
2032 }
2033 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002034 } else if (HasAttribute(line, kAttributeSetup)) {
2035 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2036 return false;
2037 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002038 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2039 std::string semantics;
2040 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2041 return false;
2042 }
deadbeefc80741f2015-10-22 13:14:45 -07002043 desc->set_msid_supported(
2044 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002045 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002046 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002047 if (!ParseExtmap(line, &extmap, error)) {
2048 return false;
2049 }
2050 session_extmaps->push_back(extmap);
2051 }
2052 }
2053
2054 return true;
2055}
2056
2057bool ParseGroupAttribute(const std::string& line,
2058 cricket::SessionDescription* desc,
2059 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002060 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002061
2062 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2063 // a=group:BUNDLE video voice
2064 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002065 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002066 kSdpDelimiterSpace, &fields);
2067 std::string semantics;
2068 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2069 return false;
2070 }
2071 cricket::ContentGroup group(semantics);
2072 for (size_t i = 1; i < fields.size(); ++i) {
2073 group.AddContentName(fields[i]);
2074 }
2075 desc->AddGroup(group);
2076 return true;
2077}
2078
2079static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002080 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002081 SdpParseError* error) {
2082 if (!IsLineType(line, kLineTypeAttributes) ||
2083 !HasAttribute(line, kAttributeFingerprint)) {
2084 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2085 kAttributeFingerprint, error);
2086 }
2087
2088 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002089 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002090 kSdpDelimiterSpace, &fields);
2091 const size_t expected_fields = 2;
2092 if (fields.size() != expected_fields) {
2093 return ParseFailedExpectFieldNum(line, expected_fields, error);
2094 }
2095
2096 // The first field here is "fingerprint:<hash>.
2097 std::string algorithm;
2098 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2099 return false;
2100 }
2101
2102 // Downcase the algorithm. Note that we don't need to downcase the
2103 // fingerprint because hex_decode can handle upper-case.
2104 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2105 ::tolower);
2106
2107 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002108 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002109 algorithm, fields[1]);
2110 if (!*fingerprint) {
2111 return ParseFailed(line,
2112 "Failed to create fingerprint from the digest.",
2113 error);
2114 }
2115
2116 return true;
2117}
2118
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002119static bool ParseDtlsSetup(const std::string& line,
2120 cricket::ConnectionRole* role,
2121 SdpParseError* error) {
2122 // setup-attr = "a=setup:" role
2123 // role = "active" / "passive" / "actpass" / "holdconn"
2124 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002125 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002126 const size_t expected_fields = 2;
2127 if (fields.size() != expected_fields) {
2128 return ParseFailedExpectFieldNum(line, expected_fields, error);
2129 }
2130 std::string role_str = fields[1];
2131 if (!cricket::StringToConnectionRole(role_str, role)) {
2132 return ParseFailed(line, "Invalid attribute value.", error);
2133 }
2134 return true;
2135}
2136
deadbeef9d3584c2016-02-16 17:54:10 -08002137static bool ParseMsidAttribute(const std::string& line,
2138 std::string* stream_id,
2139 std::string* track_id,
2140 SdpParseError* error) {
2141 // draft-ietf-mmusic-msid-11
2142 // a=msid:<stream id> <track id>
2143 // msid-value = msid-id [ SP msid-appdata ]
2144 // msid-id = 1*64token-char ; see RFC 4566
2145 // msid-appdata = 1*64token-char ; see RFC 4566
2146 std::string field1;
2147 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2148 &field1, track_id)) {
2149 const size_t expected_fields = 2;
2150 return ParseFailedExpectFieldNum(line, expected_fields, error);
2151 }
2152
2153 // msid:<msid-id>
2154 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2155 return false;
2156 }
2157 return true;
2158}
2159
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002160// RFC 3551
2161// PT encoding media type clock rate channels
2162// name (Hz)
2163// 0 PCMU A 8,000 1
2164// 1 reserved A
2165// 2 reserved A
2166// 3 GSM A 8,000 1
2167// 4 G723 A 8,000 1
2168// 5 DVI4 A 8,000 1
2169// 6 DVI4 A 16,000 1
2170// 7 LPC A 8,000 1
2171// 8 PCMA A 8,000 1
2172// 9 G722 A 8,000 1
2173// 10 L16 A 44,100 2
2174// 11 L16 A 44,100 1
2175// 12 QCELP A 8,000 1
2176// 13 CN A 8,000 1
2177// 14 MPA A 90,000 (see text)
2178// 15 G728 A 8,000 1
2179// 16 DVI4 A 11,025 1
2180// 17 DVI4 A 22,050 1
2181// 18 G729 A 8,000 1
2182struct StaticPayloadAudioCodec {
2183 const char* name;
2184 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002185 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002186};
2187static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2188 { "PCMU", 8000, 1 },
2189 { "reserved", 0, 0 },
2190 { "reserved", 0, 0 },
2191 { "GSM", 8000, 1 },
2192 { "G723", 8000, 1 },
2193 { "DVI4", 8000, 1 },
2194 { "DVI4", 16000, 1 },
2195 { "LPC", 8000, 1 },
2196 { "PCMA", 8000, 1 },
2197 { "G722", 8000, 1 },
2198 { "L16", 44100, 2 },
2199 { "L16", 44100, 1 },
2200 { "QCELP", 8000, 1 },
2201 { "CN", 8000, 1 },
2202 { "MPA", 90000, 1 },
2203 { "G728", 8000, 1 },
2204 { "DVI4", 11025, 1 },
2205 { "DVI4", 22050, 1 },
2206 { "G729", 8000, 1 },
2207};
2208
2209void MaybeCreateStaticPayloadAudioCodecs(
2210 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2211 if (!media_desc) {
2212 return;
2213 }
deadbeef67cf2c12016-04-13 10:07:16 -07002214 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002215 for (int payload_type : fmts) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002216 if (!media_desc->HasCodec(payload_type) &&
2217 payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002218 static_cast<uint32_t>(payload_type) <
2219 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002220 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2221 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002222 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002223 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002224 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002225 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002226 }
2227}
2228
2229template <class C>
2230static C* ParseContentDescription(const std::string& message,
2231 const MediaType media_type,
2232 int mline_index,
2233 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002234 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002235 size_t* pos,
2236 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002237 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002238 TransportDescription* transport,
2239 std::vector<JsepIceCandidate*>* candidates,
2240 webrtc::SdpParseError* error) {
2241 C* media_desc = new C();
2242 switch (media_type) {
2243 case cricket::MEDIA_TYPE_AUDIO:
2244 *content_name = cricket::CN_AUDIO;
2245 break;
2246 case cricket::MEDIA_TYPE_VIDEO:
2247 *content_name = cricket::CN_VIDEO;
2248 break;
2249 case cricket::MEDIA_TYPE_DATA:
2250 *content_name = cricket::CN_DATA;
2251 break;
2252 default:
nissec80e7412017-01-11 05:56:46 -08002253 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002254 break;
2255 }
deadbeef67cf2c12016-04-13 10:07:16 -07002256 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
deadbeef25ed4352016-12-12 18:37:36 -08002257 pos, content_name, bundle_only, media_desc, transport,
2258 candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002259 delete media_desc;
2260 return NULL;
2261 }
2262 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002263 std::unordered_map<int, int> payload_type_preferences;
2264 // "size + 1" so that the lowest preference payload type has a preference of
2265 // 1, which is greater than the default (0) for payload types not in the fmt
2266 // list.
2267 int preference = static_cast<int>(payload_types.size() + 1);
2268 for (int pt : payload_types) {
2269 payload_type_preferences[pt] = preference--;
2270 }
2271 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2272 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2273 const typename C::CodecType& a,
2274 const typename C::CodecType& b) {
2275 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2276 });
2277 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002278 return media_desc;
2279}
2280
2281bool ParseMediaDescription(const std::string& message,
2282 const TransportDescription& session_td,
2283 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002284 size_t* pos,
2285 cricket::SessionDescription* desc,
2286 std::vector<JsepIceCandidate*>* candidates,
2287 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002288 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002289 std::string line;
2290 int mline_index = -1;
2291
2292 // Zero or more media descriptions
2293 // RFC 4566
2294 // m=<media> <port> <proto> <fmt>
2295 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2296 ++mline_index;
2297
2298 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002299 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002300 kSdpDelimiterSpace, &fields);
2301 const size_t expected_min_fields = 4;
2302 if (fields.size() < expected_min_fields) {
2303 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2304 }
deadbeef25ed4352016-12-12 18:37:36 -08002305 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002306 // RFC 3264
2307 // To reject an offered stream, the port number in the corresponding stream
2308 // in the answer MUST be set to zero.
2309 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-12 18:37:36 -08002310 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002311 }
2312
2313 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002314
2315 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002316 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002317 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002318 for (size_t j = 3 ; j < fields.size(); ++j) {
2319 // TODO(wu): Remove when below bug is fixed.
2320 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002321 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002322 continue;
2323 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002324
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002325 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002326 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002327 return false;
2328 }
deadbeef67cf2c12016-04-13 10:07:16 -07002329 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002330 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002331 }
2332
2333 // Make a temporary TransportDescription based on |session_td|.
2334 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002335 TransportDescription transport(
2336 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2337 session_td.ice_mode, session_td.connection_role,
2338 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002339
kwibergd1fe2812016-04-27 06:47:29 -07002340 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002341 std::string content_name;
deadbeef25ed4352016-12-12 18:37:36 -08002342 bool bundle_only = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002343 if (HasAttribute(line, kMediaTypeVideo)) {
2344 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002345 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002346 payload_types, pos, &content_name, &bundle_only, &transport,
2347 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002348 } else if (HasAttribute(line, kMediaTypeAudio)) {
2349 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002350 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002351 payload_types, pos, &content_name, &bundle_only, &transport,
2352 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002353 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002354 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002355 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002356 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002357 payload_types, pos, &content_name, &bundle_only, &transport,
2358 candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002359 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002360
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002361 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002362 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002363 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002364 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002365 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002366 } else {
2367 LOG(LS_WARNING) << "Unsupported media type: " << line;
2368 continue;
2369 }
2370 if (!content.get()) {
2371 // ParseContentDescription returns NULL if failed.
2372 return false;
2373 }
2374
deadbeef25ed4352016-12-12 18:37:36 -08002375 bool content_rejected = false;
deadbeef12771a12017-01-03 13:53:47 -08002376 // A port of 0 is not interpreted as a rejected m= section when it's
2377 // used along with a=bundle-only.
deadbeef25ed4352016-12-12 18:37:36 -08002378 if (bundle_only) {
deadbeef25ed4352016-12-12 18:37:36 -08002379 if (!port_rejected) {
deadbeef12771a12017-01-03 13:53:47 -08002380 // Usage of bundle-only with a nonzero port is unspecified. So just
2381 // ignore bundle-only if we see this.
2382 bundle_only = false;
2383 LOG(LS_WARNING)
2384 << "a=bundle-only attribute observed with a nonzero "
2385 << "port; this usage is unspecified so the attribute is being "
2386 << "ignored.";
deadbeef25ed4352016-12-12 18:37:36 -08002387 }
2388 } else {
2389 // If not using bundle-only, interpret port 0 in the normal way; the m=
2390 // section is being rejected.
2391 content_rejected = port_rejected;
2392 }
2393
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002394 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002395 // Set the extmap.
2396 if (!session_extmaps.empty() &&
2397 !content->rtp_header_extensions().empty()) {
2398 return ParseFailed("",
2399 "The a=extmap MUST be either all session level or "
2400 "all media level.",
2401 error);
2402 }
2403 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2404 content->AddRtpHeaderExtension(session_extmaps[i]);
2405 }
2406 }
2407 content->set_protocol(protocol);
2408 desc->AddContent(content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002409 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP
2410 : cricket::NS_JINGLE_RTP,
2411 content_rejected, bundle_only, content.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002412 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2413 TransportInfo transport_info(content_name, transport);
2414
2415 if (!desc->AddTransportInfo(transport_info)) {
2416 std::ostringstream description;
2417 description << "Failed to AddTransportInfo with content name: "
2418 << content_name;
2419 return ParseFailed("", description.str(), error);
2420 }
2421 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002422
2423 size_t end_of_message = message.size();
2424 if (mline_index == -1 && *pos != end_of_message) {
2425 ParseFailed(message, *pos, "Expects m line.", error);
2426 return false;
2427 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002428 return true;
2429}
2430
2431bool VerifyCodec(const cricket::Codec& codec) {
2432 // Codec has not been populated correctly unless the name has been set. This
2433 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2434 // have a corresponding "rtpmap" line.
htab39db842016-12-08 01:50:48 -08002435 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002436}
2437
2438bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2439 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2440 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2441 iter != codecs.end(); ++iter) {
2442 if (!VerifyCodec(*iter)) {
2443 return false;
2444 }
2445 }
2446 return true;
2447}
2448
2449bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2450 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2451 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2452 iter != codecs.end(); ++iter) {
2453 if (!VerifyCodec(*iter)) {
2454 return false;
2455 }
2456 }
2457 return true;
2458}
2459
2460void AddParameters(const cricket::CodecParameterMap& parameters,
2461 cricket::Codec* codec) {
2462 for (cricket::CodecParameterMap::const_iterator iter =
2463 parameters.begin(); iter != parameters.end(); ++iter) {
2464 codec->SetParam(iter->first, iter->second);
2465 }
2466}
2467
2468void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2469 cricket::Codec* codec) {
2470 codec->AddFeedbackParam(feedback_param);
2471}
2472
2473void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2474 cricket::Codec* codec) {
2475 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2476 feedback_params.params().begin();
2477 iter != feedback_params.params().end(); ++iter) {
2478 codec->AddFeedbackParam(*iter);
2479 }
2480}
2481
2482// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002483// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002484// with that payload type.
2485template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002486T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 04:00:16 -08002487 const T* codec = FindCodecById(codecs, payload_type);
2488 if (codec)
2489 return *codec;
2490 // Return empty codec with |payload_type|.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002491 T ret_val;
magjedb05fa242016-11-11 04:00:16 -08002492 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002493 return ret_val;
2494}
2495
2496// Updates or creates a new codec entry in the audio description.
2497template <class T, class U>
2498void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2499 T* desc = static_cast<T*>(content_desc);
2500 std::vector<U> codecs = desc->codecs();
2501 bool found = false;
2502
2503 typename std::vector<U>::iterator iter;
2504 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2505 if (iter->id == codec.id) {
2506 *iter = codec;
2507 found = true;
2508 break;
2509 }
2510 }
2511 if (!found) {
2512 desc->AddCodec(codec);
2513 return;
2514 }
2515 desc->set_codecs(codecs);
2516}
2517
2518// Adds or updates existing codec corresponding to |payload_type| according
2519// to |parameters|.
2520template <class T, class U>
2521void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2522 const cricket::CodecParameterMap& parameters) {
2523 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002524 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2525 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002526 AddParameters(parameters, &new_codec);
2527 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2528}
2529
2530// Adds or updates existing codec corresponding to |payload_type| according
2531// to |feedback_param|.
2532template <class T, class U>
2533void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2534 const cricket::FeedbackParam& feedback_param) {
2535 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002536 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2537 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002538 AddFeedbackParameter(feedback_param, &new_codec);
2539 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2540}
2541
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002542template <class T>
2543bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2544 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002545 if (iter->id == kWildcardPayloadType) {
2546 *wildcard_codec = *iter;
2547 codecs->erase(iter);
2548 return true;
2549 }
2550 }
2551 return false;
2552}
2553
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002554template<class T>
2555void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2556 auto codecs = desc->codecs();
2557 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002558 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2559 return;
2560 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002561 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002562 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2563 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002564 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002565}
2566
2567void AddAudioAttribute(const std::string& name, const std::string& value,
2568 AudioContentDescription* audio_desc) {
2569 if (value.empty()) {
2570 return;
2571 }
2572 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2573 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2574 iter != codecs.end(); ++iter) {
2575 iter->params[name] = value;
2576 }
2577 audio_desc->set_codecs(codecs);
2578}
2579
2580bool ParseContent(const std::string& message,
2581 const MediaType media_type,
2582 int mline_index,
2583 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002584 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002585 size_t* pos,
2586 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002587 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002588 MediaContentDescription* media_desc,
2589 TransportDescription* transport,
2590 std::vector<JsepIceCandidate*>* candidates,
2591 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002592 RTC_DCHECK(media_desc != NULL);
2593 RTC_DCHECK(content_name != NULL);
2594 RTC_DCHECK(transport != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002595
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002596 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2597 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002598 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002599 }
2600
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002601 // The media level "ice-ufrag" and "ice-pwd".
2602 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2603 Candidates candidates_orig;
2604 std::string line;
2605 std::string mline_id;
2606 // Tracks created out of the ssrc attributes.
2607 StreamParamsVec tracks;
2608 SsrcInfoVec ssrc_infos;
2609 SsrcGroupVec ssrc_groups;
2610 std::string maxptime_as_string;
2611 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002612 std::string stream_id;
2613 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002614
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002615 // Loop until the next m line
2616 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2617 if (!GetLine(message, pos, &line)) {
2618 if (*pos >= message.size()) {
2619 break; // Done parsing
2620 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002621 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002622 }
2623 }
2624
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002625 // RFC 4566
2626 // b=* (zero or more bandwidth information lines)
2627 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2628 std::string bandwidth;
2629 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2630 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2631 return false;
2632 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002633 int b = 0;
2634 if (!GetValueFromString(line, bandwidth, &b, error)) {
2635 return false;
2636 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002637 // We should never use more than the default bandwidth for RTP-based
2638 // data channels. Don't allow SDP to set the bandwidth, because
2639 // that would give JS the opportunity to "break the Internet".
2640 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2641 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2642 b > cricket::kDataMaxBandwidth / 1000) {
2643 std::ostringstream description;
2644 description << "RTP-based data channels may not send more than "
2645 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2646 return ParseFailed(line, description.str(), error);
2647 }
deadbeefb2362572016-12-13 16:37:06 -08002648 // Prevent integer overflow.
2649 b = std::min(b, INT_MAX / 1000);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002650 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002651 }
2652 }
2653 continue;
2654 }
2655
2656 if (!IsLineType(line, kLineTypeAttributes)) {
2657 // TODO: Handle other lines if needed.
2658 LOG(LS_INFO) << "Ignored line: " << line;
2659 continue;
2660 }
2661
2662 // Handle attributes common to SCTP and RTP.
2663 if (HasAttribute(line, kAttributeMid)) {
2664 // RFC 3388
2665 // mid-attribute = "a=mid:" identification-tag
2666 // identification-tag = token
2667 // Use the mid identification-tag as the content name.
2668 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2669 return false;
2670 }
2671 *content_name = mline_id;
deadbeef25ed4352016-12-12 18:37:36 -08002672 } else if (HasAttribute(line, kAttributeBundleOnly)) {
2673 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002674 } else if (HasAttribute(line, kAttributeCandidate)) {
2675 Candidate candidate;
2676 if (!ParseCandidate(line, &candidate, error, false)) {
2677 return false;
2678 }
deadbeef7bcdb692017-01-20 12:43:58 -08002679 // ParseCandidate will parse non-standard ufrag and password attributes,
2680 // since it's used for candidate trickling, but we only want to process
2681 // the "a=ice-ufrag"/"a=ice-pwd" values in a session description, so
2682 // strip them off at this point.
2683 candidate.set_username(std::string());
2684 candidate.set_password(std::string());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002685 candidates_orig.push_back(candidate);
2686 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2687 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2688 return false;
2689 }
2690 } else if (HasAttribute(line, kAttributeIcePwd)) {
2691 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2692 return false;
2693 }
2694 } else if (HasAttribute(line, kAttributeIceOption)) {
2695 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2696 return false;
2697 }
2698 } else if (HasAttribute(line, kAttributeFmtp)) {
2699 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2700 return false;
2701 }
2702 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002703 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002704
2705 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2706 return false;
2707 }
2708 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002709 } else if (HasAttribute(line, kAttributeSetup)) {
2710 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2711 return false;
2712 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002713 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 10:04:34 -07002714 if (media_type != cricket::MEDIA_TYPE_DATA) {
2715 return ParseFailed(
2716 line, "sctp-port attribute found in non-data media description.",
2717 error);
2718 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002719 int sctp_port;
2720 if (!ParseSctpPort(line, &sctp_port, error)) {
2721 return false;
2722 }
2723 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2724 sctp_port)) {
2725 return false;
2726 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002727 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002728 //
2729 // RTP specific attrubtes
2730 //
2731 if (HasAttribute(line, kAttributeRtcpMux)) {
2732 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002733 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2734 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002735 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2736 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2737 return false;
2738 }
2739 } else if (HasAttribute(line, kAttributeSsrc)) {
2740 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2741 return false;
2742 }
2743 } else if (HasAttribute(line, kAttributeCrypto)) {
2744 if (!ParseCryptoAttribute(line, media_desc, error)) {
2745 return false;
2746 }
2747 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002748 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2749 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002750 return false;
2751 }
2752 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2753 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2754 return false;
2755 }
2756 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2757 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2758 return false;
2759 }
2760 } else if (HasAttribute(line, kCodecParamPTime)) {
2761 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2762 return false;
2763 }
2764 } else if (HasAttribute(line, kAttributeSendOnly)) {
2765 media_desc->set_direction(cricket::MD_SENDONLY);
2766 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2767 media_desc->set_direction(cricket::MD_RECVONLY);
2768 } else if (HasAttribute(line, kAttributeInactive)) {
2769 media_desc->set_direction(cricket::MD_INACTIVE);
2770 } else if (HasAttribute(line, kAttributeSendRecv)) {
2771 media_desc->set_direction(cricket::MD_SENDRECV);
2772 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002773 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002774 if (!ParseExtmap(line, &extmap, error)) {
2775 return false;
2776 }
2777 media_desc->AddRtpHeaderExtension(extmap);
2778 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2779 // Experimental attribute. Conference mode activates more aggressive
2780 // AEC and NS settings.
2781 // TODO: expose API to set these directly.
2782 std::string flag_value;
2783 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2784 return false;
2785 }
2786 if (flag_value.compare(kValueConference) == 0)
2787 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002788 } else if (HasAttribute(line, kAttributeMsid)) {
2789 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2790 return false;
2791 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002792 }
2793 } else {
2794 // Only parse lines that we are interested of.
2795 LOG(LS_INFO) << "Ignored line: " << line;
2796 continue;
2797 }
2798 }
2799
2800 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002801 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2802 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2803 // the m= section.
2804 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002805
2806 // Add the ssrc group to the track.
2807 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2808 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2809 if (ssrc_group->ssrcs.empty()) {
2810 continue;
2811 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002812 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002813 for (StreamParamsVec::iterator track = tracks.begin();
2814 track != tracks.end(); ++track) {
2815 if (track->has_ssrc(ssrc)) {
2816 track->ssrc_groups.push_back(*ssrc_group);
2817 }
2818 }
2819 }
2820
2821 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002822 for (StreamParams& track : tracks) {
2823 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002824 }
2825
2826 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2827 AudioContentDescription* audio_desc =
2828 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002829 UpdateFromWildcardCodecs(audio_desc);
2830
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002831 // Verify audio codec ensures that no audio codec has been populated with
2832 // only fmtp.
2833 if (!VerifyAudioCodecs(audio_desc)) {
2834 return ParseFailed("Failed to parse audio codecs correctly.", error);
2835 }
2836 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2837 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2838 }
2839
2840 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002841 VideoContentDescription* video_desc =
2842 static_cast<VideoContentDescription*>(media_desc);
2843 UpdateFromWildcardCodecs(video_desc);
2844 // Verify video codec ensures that no video codec has been populated with
2845 // only rtcp-fb.
2846 if (!VerifyVideoCodecs(video_desc)) {
2847 return ParseFailed("Failed to parse video codecs correctly.", error);
2848 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002849 }
2850
2851 // RFC 5245
2852 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2853 for (Candidates::iterator it = candidates_orig.begin();
2854 it != candidates_orig.end(); ++it) {
nisseede5da42017-01-12 05:15:36 -08002855 RTC_DCHECK((*it).username().empty() ||
2856 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002857 (*it).set_username(transport->ice_ufrag);
nisseede5da42017-01-12 05:15:36 -08002858 RTC_DCHECK((*it).password().empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002859 (*it).set_password(transport->ice_pwd);
2860 candidates->push_back(
2861 new JsepIceCandidate(mline_id, mline_index, *it));
2862 }
2863 return true;
2864}
2865
2866bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2867 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002868 RTC_DCHECK(ssrc_infos != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002869 // RFC 5576
2870 // a=ssrc:<ssrc-id> <attribute>
2871 // a=ssrc:<ssrc-id> <attribute>:<value>
2872 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002873 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2874 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002875 const size_t expected_fields = 2;
2876 return ParseFailedExpectFieldNum(line, expected_fields, error);
2877 }
2878
2879 // ssrc:<ssrc-id>
2880 std::string ssrc_id_s;
2881 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2882 return false;
2883 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002884 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002885 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2886 return false;
2887 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002888
2889 std::string attribute;
2890 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002891 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002892 std::ostringstream description;
2893 description << "Failed to get the ssrc attribute value from " << field2
2894 << ". Expected format <attribute>:<value>.";
2895 return ParseFailed(line, description.str(), error);
2896 }
2897
2898 // Check if there's already an item for this |ssrc_id|. Create a new one if
2899 // there isn't.
2900 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2901 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2902 if (ssrc_info->ssrc_id == ssrc_id) {
2903 break;
2904 }
2905 }
2906 if (ssrc_info == ssrc_infos->end()) {
2907 SsrcInfo info;
2908 info.ssrc_id = ssrc_id;
2909 ssrc_infos->push_back(info);
2910 ssrc_info = ssrc_infos->end() - 1;
2911 }
2912
2913 // Store the info to the |ssrc_info|.
2914 if (attribute == kSsrcAttributeCname) {
2915 // RFC 5576
2916 // cname:<value>
2917 ssrc_info->cname = value;
2918 } else if (attribute == kSsrcAttributeMsid) {
2919 // draft-alvestrand-mmusic-msid-00
2920 // "msid:" identifier [ " " appdata ]
2921 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002922 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002923 if (fields.size() < 1 || fields.size() > 2) {
2924 return ParseFailed(line,
2925 "Expected format \"msid:<identifier>[ <appdata>]\".",
2926 error);
2927 }
deadbeef9d3584c2016-02-16 17:54:10 -08002928 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002929 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002930 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002931 }
2932 } else if (attribute == kSsrcAttributeMslabel) {
2933 // draft-alvestrand-rtcweb-mid-01
2934 // mslabel:<value>
2935 ssrc_info->mslabel = value;
2936 } else if (attribute == kSSrcAttributeLabel) {
2937 // The label isn't defined.
2938 // label:<value>
2939 ssrc_info->label = value;
2940 }
2941 return true;
2942}
2943
2944bool ParseSsrcGroupAttribute(const std::string& line,
2945 SsrcGroupVec* ssrc_groups,
2946 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002947 RTC_DCHECK(ssrc_groups != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002948 // RFC 5576
2949 // a=ssrc-group:<semantics> <ssrc-id> ...
2950 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002951 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002952 kSdpDelimiterSpace, &fields);
2953 const size_t expected_min_fields = 2;
2954 if (fields.size() < expected_min_fields) {
2955 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2956 }
2957 std::string semantics;
2958 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2959 return false;
2960 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002961 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002962 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002963 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002964 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2965 return false;
2966 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002967 ssrcs.push_back(ssrc);
2968 }
2969 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2970 return true;
2971}
2972
2973bool ParseCryptoAttribute(const std::string& line,
2974 MediaContentDescription* media_desc,
2975 SdpParseError* error) {
2976 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002977 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002978 kSdpDelimiterSpace, &fields);
2979 // RFC 4568
2980 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2981 const size_t expected_min_fields = 3;
2982 if (fields.size() < expected_min_fields) {
2983 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2984 }
2985 std::string tag_value;
2986 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2987 return false;
2988 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002989 int tag = 0;
2990 if (!GetValueFromString(line, tag_value, &tag, error)) {
2991 return false;
2992 }
jbauch083b73f2015-07-16 02:46:32 -07002993 const std::string& crypto_suite = fields[1];
2994 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002995 std::string session_params;
2996 if (fields.size() > 3) {
2997 session_params = fields[3];
2998 }
2999 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
3000 session_params));
3001 return true;
3002}
3003
3004// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07003005// to |name|, |clockrate|, |bitrate|, and |channels|.
3006void UpdateCodec(int payload_type,
3007 const std::string& name,
3008 int clockrate,
3009 int bitrate,
3010 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003011 AudioContentDescription* audio_desc) {
3012 // Codec may already be populated with (only) optional parameters
3013 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003014 cricket::AudioCodec codec =
3015 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003016 codec.name = name;
3017 codec.clockrate = clockrate;
3018 codec.bitrate = bitrate;
3019 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003020 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3021 codec);
3022}
3023
3024// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07003025// |name|, |width|, |height|, and |framerate|.
3026void UpdateCodec(int payload_type,
3027 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003028 VideoContentDescription* video_desc) {
3029 // Codec may already be populated with (only) optional parameters
3030 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003031 cricket::VideoCodec codec =
3032 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003033 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003034 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3035 codec);
3036}
3037
3038bool ParseRtpmapAttribute(const std::string& line,
3039 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07003040 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003041 MediaContentDescription* media_desc,
3042 SdpParseError* error) {
3043 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003044 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003045 kSdpDelimiterSpace, &fields);
3046 // RFC 4566
3047 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3048 const size_t expected_min_fields = 2;
3049 if (fields.size() < expected_min_fields) {
3050 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3051 }
3052 std::string payload_type_value;
3053 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3054 return false;
3055 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003056 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003057 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3058 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003059 return false;
3060 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003061
deadbeef67cf2c12016-04-13 10:07:16 -07003062 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3063 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003064 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3065 << "<fmt> of the m-line: " << line;
3066 return true;
3067 }
jbauch083b73f2015-07-16 02:46:32 -07003068 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003069 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003070 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003071 // <encoding name>/<clock rate>[/<encodingparameters>]
3072 // 2 mandatory fields
3073 if (codec_params.size() < 2 || codec_params.size() > 3) {
3074 return ParseFailed(line,
3075 "Expected format \"<encoding name>/<clock rate>"
3076 "[/<encodingparameters>]\".",
3077 error);
3078 }
jbauch083b73f2015-07-16 02:46:32 -07003079 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003080 int clock_rate = 0;
3081 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3082 return false;
3083 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003084 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3085 VideoContentDescription* video_desc =
3086 static_cast<VideoContentDescription*>(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003087 UpdateCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07003088 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003089 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3090 // RFC 4566
3091 // For audio streams, <encoding parameters> indicates the number
3092 // of audio channels. This parameter is OPTIONAL and may be
3093 // omitted if the number of channels is one, provided that no
3094 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003095 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003096 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003097 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3098 return false;
3099 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003100 }
3101 int bitrate = 0;
3102 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3103 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3104 // The bandwidth adaptation doesn't always work well, so this code
3105 // sets a fixed target bitrate instead.
3106 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3107 if (clock_rate <= 16000) {
3108 bitrate = kIsacWbDefaultRate;
3109 } else {
3110 bitrate = kIsacSwbDefaultRate;
3111 }
3112 }
3113 AudioContentDescription* audio_desc =
3114 static_cast<AudioContentDescription*>(media_desc);
3115 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003116 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003117 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3118 DataContentDescription* data_desc =
3119 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003120 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003121 }
3122 return true;
3123}
3124
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003125bool ParseFmtpParam(const std::string& line, std::string* parameter,
3126 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003127 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003128 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3129 return false;
3130 }
3131 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003132 return true;
3133}
3134
3135bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3136 MediaContentDescription* media_desc,
3137 SdpParseError* error) {
3138 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3139 media_type != cricket::MEDIA_TYPE_VIDEO) {
3140 return true;
3141 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003142
3143 std::string line_payload;
3144 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003145
3146 // RFC 5576
3147 // a=fmtp:<format> <format specific parameters>
3148 // At least two fields, whereas the second one is any of the optional
3149 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003150 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3151 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003152 ParseFailedExpectMinFieldNum(line, 2, error);
3153 return false;
3154 }
3155
Donald Curtis0e07f922015-05-15 09:21:23 -07003156 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003157 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003158 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003159 return false;
3160 }
3161
Donald Curtis0e07f922015-05-15 09:21:23 -07003162 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003163 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3164 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003165 return false;
3166 }
3167
3168 // Parse out format specific parameters.
3169 std::vector<std::string> fields;
3170 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3171
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003172 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003173 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003174 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003175 // Only fmtps with equals are currently supported. Other fmtp types
3176 // should be ignored. Unknown fmtps do not constitute an error.
3177 continue;
3178 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003179
3180 std::string name;
3181 std::string value;
3182 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003183 return false;
3184 }
3185 codec_params[name] = value;
3186 }
3187
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003188 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3189 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003190 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003191 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3192 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003193 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003194 }
3195 return true;
3196}
3197
3198bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3199 MediaContentDescription* media_desc,
3200 SdpParseError* error) {
3201 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3202 media_type != cricket::MEDIA_TYPE_VIDEO) {
3203 return true;
3204 }
3205 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003206 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003207 if (rtcp_fb_fields.size() < 2) {
3208 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3209 }
3210 std::string payload_type_string;
3211 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3212 error)) {
3213 return false;
3214 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003215 int payload_type = kWildcardPayloadType;
3216 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003217 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3218 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003219 return false;
3220 }
3221 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003222 std::string id = rtcp_fb_fields[1];
3223 std::string param = "";
3224 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3225 iter != rtcp_fb_fields.end(); ++iter) {
3226 param.append(*iter);
3227 }
3228 const cricket::FeedbackParam feedback_param(id, param);
3229
3230 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003231 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3232 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003233 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003234 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3235 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003236 }
3237 return true;
3238}
3239
3240} // namespace webrtc